massive changes and it works
This commit is contained in:
76
.gitignore
vendored
Normal file
76
.gitignore
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
# Binaries
|
||||
/bin/
|
||||
/manager
|
||||
/runner
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool
|
||||
*.out
|
||||
|
||||
# Dependency directories
|
||||
/vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
|
||||
# Database files
|
||||
*.db
|
||||
*.db-shm
|
||||
*.db-wal
|
||||
jiggablend.db
|
||||
jiggablend.db.wal
|
||||
jiggablend.db-shm
|
||||
|
||||
# Secrets and configuration
|
||||
runner-secrets.json
|
||||
runner-secrets-*.json
|
||||
*.secret
|
||||
.env
|
||||
.env.local
|
||||
|
||||
# Storage directories
|
||||
jiggablend-storage/
|
||||
jiggablend-workspaces/
|
||||
|
||||
# Node.js
|
||||
web/node_modules/
|
||||
web/dist/
|
||||
web/.vite/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.DS_Store
|
||||
|
||||
# Build artifacts
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Temporary files
|
||||
*.tmp
|
||||
*.temp
|
||||
/tmp/
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# OS files
|
||||
Thumbs.db
|
||||
.DS_Store
|
||||
|
||||
59
Makefile
59
Makefile
@@ -1,31 +1,66 @@
|
||||
.PHONY: build-manager build-runner run-manager run-runner clean test build-web
|
||||
.PHONY: build build-manager build-runner build-web run-manager run-runner run cleanup cleanup-manager cleanup-runner clean test
|
||||
|
||||
# Build all
|
||||
build: build-manager build-runner build-web
|
||||
build: clean-bin build-manager build-runner
|
||||
|
||||
# Build manager
|
||||
build-manager:
|
||||
build-manager: clean-bin build-web
|
||||
go build -o bin/manager ./cmd/manager
|
||||
|
||||
# Build runner
|
||||
build-runner:
|
||||
build-runner: clean-bin
|
||||
GOOS=linux GOARCH=amd64 go build -o bin/runner ./cmd/runner
|
||||
|
||||
# Build web UI
|
||||
build-web:
|
||||
build-web: clean-web
|
||||
cd web && npm install && npm run build
|
||||
|
||||
# Cleanup manager (database and storage)
|
||||
cleanup-manager:
|
||||
@echo "Cleaning up manager database and storage..."
|
||||
@rm -f jiggablend.db 2>/dev/null || true
|
||||
@rm -f jiggablend.db-shm 2>/dev/null || true
|
||||
@rm -f jiggablend.db-wal 2>/dev/null || true
|
||||
@rm -rf jiggablend-storage 2>/dev/null || true
|
||||
@echo "Manager cleanup complete"
|
||||
|
||||
# Cleanup runner (workspaces and secrets)
|
||||
cleanup-runner:
|
||||
@echo "Cleaning up runner workspaces and secrets..."
|
||||
@rm -rf jiggablend-workspaces jiggablend-workspace* *workspace* runner-secrets*.json 2>/dev/null || true
|
||||
@echo "Runner cleanup complete"
|
||||
|
||||
# Cleanup both manager and runner
|
||||
cleanup: cleanup-manager cleanup-runner
|
||||
|
||||
# Run all parallel
|
||||
run: cleanup-manager cleanup-runner build-manager build-runner
|
||||
@echo "Starting manager and runner in parallel..."
|
||||
@echo "Press Ctrl+C to stop both..."
|
||||
@trap 'kill $$MANAGER_PID $$RUNNER_PID 2>/dev/null; exit' INT TERM; \
|
||||
FIXED_REGISTRATION_TOKEN=test-token ENABLE_LOCAL_AUTH=true LOCAL_TEST_EMAIL=test@example.com LOCAL_TEST_PASSWORD=testpassword bin/manager & \
|
||||
MANAGER_PID=$$!; \
|
||||
REGISTRATION_TOKEN=test-token bin/runner & \
|
||||
RUNNER_PID=$$!; \
|
||||
wait $$MANAGER_PID $$RUNNER_PID
|
||||
|
||||
# Run manager
|
||||
run-manager:
|
||||
go run ./cmd/manager
|
||||
# Note: ENABLE_LOCAL_AUTH enables local user registration/login
|
||||
# LOCAL_TEST_EMAIL and LOCAL_TEST_PASSWORD create a test user on startup (if it doesn't exist)
|
||||
run-manager: cleanup-manager build-manager
|
||||
FIXED_REGISTRATION_TOKEN=test-token ENABLE_LOCAL_AUTH=true LOCAL_TEST_EMAIL=test@example.com LOCAL_TEST_PASSWORD=testpassword bin/manager
|
||||
|
||||
# Run runner
|
||||
run-runner:
|
||||
go run ./cmd/runner
|
||||
run-runner: cleanup-runner build-runner
|
||||
REGISTRATION_TOKEN=test-token bin/runner
|
||||
|
||||
# Clean build artifacts
|
||||
clean:
|
||||
rm -rf bin/ web/dist/
|
||||
# Clean bin build artifacts
|
||||
clean-bin:
|
||||
rm -rf bin/
|
||||
|
||||
# Clean web build artifacts
|
||||
clean-web:
|
||||
rm -rf web/dist/
|
||||
|
||||
# Run tests
|
||||
test:
|
||||
|
||||
31
README.md
31
README.md
@@ -1,10 +1,10 @@
|
||||
# Fuego - Blender Render Farm
|
||||
# JiggaBlend - Blender Render Farm
|
||||
|
||||
A distributed Blender render farm system built with Go. The system consists of a manager server that handles job submission, file storage, and runner coordination, and runner clients that execute Blender renders on Linux amd64 systems.
|
||||
|
||||
## Architecture
|
||||
|
||||
- **Manager**: Central server with REST API, web UI, SQLite database, and local file storage
|
||||
- **Manager**: Central server with REST API, web UI, DuckDB database, and local file storage
|
||||
- **Runner**: Linux amd64 client that connects to manager, receives jobs, executes Blender renders, and reports back
|
||||
|
||||
## Features
|
||||
@@ -20,7 +20,7 @@ A distributed Blender render farm system built with Go. The system consists of a
|
||||
|
||||
### Manager
|
||||
- Go 1.21 or later
|
||||
- SQLite3
|
||||
- DuckDB (via Go driver)
|
||||
|
||||
### Runner
|
||||
- Linux amd64
|
||||
@@ -32,7 +32,7 @@ A distributed Blender render farm system built with Go. The system consists of a
|
||||
1. Clone the repository:
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd fuego
|
||||
cd jiggablend
|
||||
```
|
||||
|
||||
2. Install dependencies:
|
||||
@@ -44,9 +44,10 @@ go mod download
|
||||
|
||||
### Manager
|
||||
|
||||
Set the following environment variables for OAuth (optional):
|
||||
Set the following environment variables for authentication (optional):
|
||||
|
||||
```bash
|
||||
# OAuth Providers (optional)
|
||||
export GOOGLE_CLIENT_ID="your-google-client-id"
|
||||
export GOOGLE_CLIENT_SECRET="your-google-client-secret"
|
||||
export GOOGLE_REDIRECT_URL="http://localhost:8080/api/auth/google/callback"
|
||||
@@ -54,6 +55,14 @@ export GOOGLE_REDIRECT_URL="http://localhost:8080/api/auth/google/callback"
|
||||
export DISCORD_CLIENT_ID="your-discord-client-id"
|
||||
export DISCORD_CLIENT_SECRET="your-discord-client-secret"
|
||||
export DISCORD_REDIRECT_URL="http://localhost:8080/api/auth/discord/callback"
|
||||
|
||||
# Local Authentication (optional)
|
||||
export ENABLE_LOCAL_AUTH="true"
|
||||
|
||||
# Test User (optional, for testing only)
|
||||
# Creates a local user on startup if it doesn't exist
|
||||
export LOCAL_TEST_EMAIL="test@example.com"
|
||||
export LOCAL_TEST_PASSWORD="testpassword"
|
||||
```
|
||||
|
||||
### Runner
|
||||
@@ -72,7 +81,7 @@ make run-manager
|
||||
go run ./cmd/manager
|
||||
|
||||
# With custom options
|
||||
go run ./cmd/manager -port 8080 -db fuego.db -storage ./storage
|
||||
go run ./cmd/manager -port 8080 -db jiggablend.db -storage ./storage
|
||||
```
|
||||
|
||||
The manager will start on `http://localhost:8080` by default.
|
||||
@@ -122,14 +131,14 @@ make build-runner
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
fuego/
|
||||
jiggablend/
|
||||
├── cmd/
|
||||
│ ├── manager/ # Manager server application
|
||||
│ └── runner/ # Runner client application
|
||||
├── internal/
|
||||
│ ├── api/ # REST API handlers
|
||||
│ ├── auth/ # OAuth authentication
|
||||
│ ├── database/ # SQLite database models and migrations
|
||||
│ ├── database/ # DuckDB database models and migrations
|
||||
│ ├── queue/ # Job queue management
|
||||
│ ├── storage/ # File storage operations
|
||||
│ └── runner/ # Runner management logic
|
||||
@@ -160,9 +169,9 @@ fuego/
|
||||
- `GET /api/jobs/{id}/files/{fileId}/download` - Download job file
|
||||
|
||||
### Runners
|
||||
- `GET /api/runners` - List all runners
|
||||
- `POST /api/runner/register` - Register a runner
|
||||
- `POST /api/runner/heartbeat` - Update runner heartbeat
|
||||
- `GET /api/admin/runners` - List all runners (admin only)
|
||||
- `POST /api/runner/register` - Register a runner (uses registration token)
|
||||
- `POST /api/runner/heartbeat` - Update runner heartbeat (runner authenticated)
|
||||
- `GET /api/runner/tasks` - Get pending tasks for runner
|
||||
- `POST /api/runner/tasks/{id}/complete` - Mark task as complete
|
||||
- `GET /api/runner/files/{jobId}/{fileName}` - Download file for runner
|
||||
|
||||
BIN
bin/manager
BIN
bin/manager
Binary file not shown.
BIN
bin/runner
BIN
bin/runner
Binary file not shown.
@@ -7,17 +7,17 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"fuego/internal/api"
|
||||
"fuego/internal/auth"
|
||||
"fuego/internal/database"
|
||||
"fuego/internal/storage"
|
||||
"jiggablend/internal/api"
|
||||
"jiggablend/internal/auth"
|
||||
"jiggablend/internal/database"
|
||||
"jiggablend/internal/storage"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
port = flag.String("port", getEnv("PORT", "8080"), "Server port")
|
||||
dbPath = flag.String("db", getEnv("DB_PATH", "fuego.db"), "Database path")
|
||||
storagePath = flag.String("storage", getEnv("STORAGE_PATH", "./storage"), "Storage path")
|
||||
port = flag.String("port", getEnv("PORT", "8080"), "Server port")
|
||||
dbPath = flag.String("db", getEnv("DB_PATH", "jiggablend.db"), "Database path")
|
||||
storagePath = flag.String("storage", getEnv("STORAGE_PATH", "./jiggablend-storage"), "Storage path")
|
||||
)
|
||||
flag.Parse()
|
||||
|
||||
@@ -63,4 +63,3 @@ func getEnv(key, defaultValue string) string {
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"fuego/internal/runner"
|
||||
"jiggablend/internal/runner"
|
||||
)
|
||||
|
||||
type SecretsFile struct {
|
||||
@@ -18,19 +24,16 @@ type SecretsFile struct {
|
||||
|
||||
func main() {
|
||||
var (
|
||||
managerURL = flag.String("manager", getEnv("MANAGER_URL", "http://localhost:8080"), "Manager URL")
|
||||
name = flag.String("name", getEnv("RUNNER_NAME", ""), "Runner name")
|
||||
hostname = flag.String("hostname", getEnv("RUNNER_HOSTNAME", ""), "Runner hostname")
|
||||
ipAddress = flag.String("ip", getEnv("RUNNER_IP", ""), "Runner IP address")
|
||||
token = flag.String("token", getEnv("REGISTRATION_TOKEN", ""), "Registration token")
|
||||
secretsFile = flag.String("secrets-file", getEnv("SECRETS_FILE", ""), "Path to secrets file for persistent storage")
|
||||
managerURL = flag.String("manager", getEnv("MANAGER_URL", "http://localhost:8080"), "Manager URL")
|
||||
name = flag.String("name", getEnv("RUNNER_NAME", ""), "Runner name")
|
||||
hostname = flag.String("hostname", getEnv("RUNNER_HOSTNAME", ""), "Runner hostname")
|
||||
ipAddress = flag.String("ip", getEnv("RUNNER_IP", ""), "Runner IP address")
|
||||
token = flag.String("token", getEnv("REGISTRATION_TOKEN", ""), "Registration token")
|
||||
secretsFile = flag.String("secrets-file", getEnv("SECRETS_FILE", ""), "Path to secrets file for persistent storage (default: ./runner-secrets.json, or ./runner-secrets-{id}.json if multiple runners)")
|
||||
runnerIDSuffix = flag.String("runner-id", getEnv("RUNNER_ID", ""), "Unique runner ID suffix (auto-generated if not provided)")
|
||||
)
|
||||
flag.Parse()
|
||||
|
||||
if *name == "" {
|
||||
hostname, _ := os.Hostname()
|
||||
*name = fmt.Sprintf("runner-%s", hostname)
|
||||
}
|
||||
if *hostname == "" {
|
||||
*hostname, _ = os.Hostname()
|
||||
}
|
||||
@@ -38,8 +41,54 @@ func main() {
|
||||
*ipAddress = "127.0.0.1"
|
||||
}
|
||||
|
||||
// Generate or use provided runner ID suffix
|
||||
runnerIDStr := *runnerIDSuffix
|
||||
if runnerIDStr == "" {
|
||||
runnerIDStr = generateShortID()
|
||||
}
|
||||
|
||||
// Generate runner name with ID if not provided
|
||||
if *name == "" {
|
||||
*name = fmt.Sprintf("runner-%s-%s", *hostname, runnerIDStr)
|
||||
} else {
|
||||
// Append ID to provided name to ensure uniqueness
|
||||
*name = fmt.Sprintf("%s-%s", *name, runnerIDStr)
|
||||
}
|
||||
|
||||
// Set default secrets file if not provided - always use current directory
|
||||
if *secretsFile == "" {
|
||||
if *runnerIDSuffix != "" || getEnv("RUNNER_ID", "") != "" {
|
||||
// Multiple runners - use local file with ID
|
||||
*secretsFile = fmt.Sprintf("./runner-secrets-%s.json", runnerIDStr)
|
||||
} else {
|
||||
// Single runner - use local file
|
||||
*secretsFile = "./runner-secrets.json"
|
||||
}
|
||||
}
|
||||
|
||||
client := runner.NewClient(*managerURL, *name, *hostname, *ipAddress)
|
||||
|
||||
// Probe capabilities once at startup (before any registration attempts)
|
||||
log.Printf("Probing runner capabilities...")
|
||||
client.ProbeCapabilities()
|
||||
capabilities := client.GetCapabilities()
|
||||
capList := []string{}
|
||||
for cap, value := range capabilities {
|
||||
// Only show boolean true capabilities and numeric GPU counts
|
||||
if enabled, ok := value.(bool); ok && enabled {
|
||||
capList = append(capList, cap)
|
||||
} else if count, ok := value.(int); ok && count > 0 {
|
||||
capList = append(capList, fmt.Sprintf("%s=%d", cap, count))
|
||||
} else if count, ok := value.(float64); ok && count > 0 {
|
||||
capList = append(capList, fmt.Sprintf("%s=%.0f", cap, count))
|
||||
}
|
||||
}
|
||||
if len(capList) > 0 {
|
||||
log.Printf("Detected capabilities: %s", strings.Join(capList, ", "))
|
||||
} else {
|
||||
log.Printf("Warning: No capabilities detected")
|
||||
}
|
||||
|
||||
// Try to load secrets from file
|
||||
var runnerID int64
|
||||
var runnerSecret, managerSecret string
|
||||
@@ -53,30 +102,55 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
// If no secrets loaded, register with token
|
||||
// If no secrets loaded, register with token (with retry logic)
|
||||
if runnerID == 0 {
|
||||
if *token == "" {
|
||||
log.Fatalf("Registration token required (use --token or set REGISTRATION_TOKEN env var)")
|
||||
}
|
||||
|
||||
var err error
|
||||
runnerID, runnerSecret, managerSecret, err = client.Register(*token)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to register runner: %v", err)
|
||||
}
|
||||
log.Printf("Registered runner with ID: %d", runnerID)
|
||||
// Retry registration with exponential backoff
|
||||
backoff := 1 * time.Second
|
||||
maxBackoff := 30 * time.Second
|
||||
maxRetries := 10
|
||||
retryCount := 0
|
||||
|
||||
// Save secrets to file if specified
|
||||
if *secretsFile != "" {
|
||||
secrets := SecretsFile{
|
||||
RunnerID: runnerID,
|
||||
RunnerSecret: runnerSecret,
|
||||
ManagerSecret: managerSecret,
|
||||
for {
|
||||
var err error
|
||||
runnerID, runnerSecret, managerSecret, err = client.Register(*token)
|
||||
if err == nil {
|
||||
log.Printf("Registered runner with ID: %d", runnerID)
|
||||
|
||||
// Always save secrets to file (secretsFile is now always set to a default if not provided)
|
||||
secrets := SecretsFile{
|
||||
RunnerID: runnerID,
|
||||
RunnerSecret: runnerSecret,
|
||||
ManagerSecret: managerSecret,
|
||||
}
|
||||
if err := saveSecrets(*secretsFile, secrets); err != nil {
|
||||
log.Printf("Warning: Failed to save secrets to %s: %v", *secretsFile, err)
|
||||
} else {
|
||||
log.Printf("Saved secrets to %s", *secretsFile)
|
||||
}
|
||||
break
|
||||
}
|
||||
if err := saveSecrets(*secretsFile, secrets); err != nil {
|
||||
log.Printf("Warning: Failed to save secrets: %v", err)
|
||||
} else {
|
||||
log.Printf("Saved secrets to %s", *secretsFile)
|
||||
|
||||
// Check if it's a token error (invalid/expired/used token) - shutdown immediately
|
||||
errMsg := err.Error()
|
||||
if strings.Contains(errMsg, "token error:") {
|
||||
log.Fatalf("Registration failed (token error): %v", err)
|
||||
}
|
||||
|
||||
// Only retry on connection errors or other retryable errors
|
||||
retryCount++
|
||||
if retryCount >= maxRetries {
|
||||
log.Fatalf("Failed to register runner after %d attempts: %v", maxRetries, err)
|
||||
}
|
||||
|
||||
log.Printf("Registration failed (attempt %d/%d): %v, retrying in %v", retryCount, maxRetries, err, backoff)
|
||||
time.Sleep(backoff)
|
||||
backoff *= 2
|
||||
if backoff > maxBackoff {
|
||||
backoff = maxBackoff
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,7 +164,18 @@ func main() {
|
||||
// ProcessTasks is now handled via WebSocket, but kept for HTTP fallback
|
||||
// WebSocket will handle task assignment automatically
|
||||
log.Printf("Runner started, connecting to manager via WebSocket...")
|
||||
|
||||
|
||||
// Set up signal handlers to kill processes on shutdown
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
go func() {
|
||||
sig := <-sigChan
|
||||
log.Printf("Received signal: %v, killing all processes and shutting down...", sig)
|
||||
client.KillAllProcesses()
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
// Block forever
|
||||
select {}
|
||||
}
|
||||
@@ -125,3 +210,12 @@ func getEnv(key, defaultValue string) string {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// generateShortID generates a short random ID (8 hex characters)
|
||||
func generateShortID() string {
|
||||
bytes := make([]byte, 4)
|
||||
if _, err := rand.Read(bytes); err != nil {
|
||||
// Fallback to timestamp-based ID if crypto/rand fails
|
||||
return fmt.Sprintf("%x", os.Getpid()^int(time.Now().Unix()))
|
||||
}
|
||||
return hex.EncodeToString(bytes)
|
||||
}
|
||||
|
||||
BIN
examples/benchmark.blend
Normal file
BIN
examples/benchmark.blend
Normal file
Binary file not shown.
31
go.mod
31
go.mod
@@ -1,4 +1,4 @@
|
||||
module fuego
|
||||
module jiggablend
|
||||
|
||||
go 1.25.4
|
||||
|
||||
@@ -7,8 +7,33 @@ require (
|
||||
github.com/go-chi/cors v1.2.2
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/mattn/go-sqlite3 v1.14.32
|
||||
github.com/marcboeker/go-duckdb/v2 v2.4.3
|
||||
golang.org/x/oauth2 v0.33.0
|
||||
)
|
||||
|
||||
require cloud.google.com/go/compute/metadata v0.3.0 // indirect
|
||||
require (
|
||||
cloud.google.com/go/compute/metadata v0.3.0 // indirect
|
||||
github.com/apache/arrow-go/v18 v18.4.1 // indirect
|
||||
github.com/duckdb/duckdb-go-bindings v0.1.21 // indirect
|
||||
github.com/duckdb/duckdb-go-bindings/darwin-amd64 v0.1.21 // indirect
|
||||
github.com/duckdb/duckdb-go-bindings/darwin-arm64 v0.1.21 // indirect
|
||||
github.com/duckdb/duckdb-go-bindings/linux-amd64 v0.1.21 // indirect
|
||||
github.com/duckdb/duckdb-go-bindings/linux-arm64 v0.1.21 // indirect
|
||||
github.com/duckdb/duckdb-go-bindings/windows-amd64 v0.1.21 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/google/flatbuffers v25.2.10+incompatible // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/marcboeker/go-duckdb/arrowmapping v0.0.21 // indirect
|
||||
github.com/marcboeker/go-duckdb/mapping v0.0.21 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||
github.com/zeebo/xxh3 v1.0.2 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
||||
golang.org/x/mod v0.27.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
|
||||
)
|
||||
|
||||
78
go.sum
78
go.sum
@@ -1,14 +1,88 @@
|
||||
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
||||
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
github.com/apache/arrow-go/v18 v18.4.1 h1:q/jVkBWCJOB9reDgaIZIdruLQUb1kbkvOnOFezVH1C4=
|
||||
github.com/apache/arrow-go/v18 v18.4.1/go.mod h1:tLyFubsAl17bvFdUAy24bsSvA/6ww95Iqi67fTpGu3E=
|
||||
github.com/apache/thrift v0.22.0 h1:r7mTJdj51TMDe6RtcmNdQxgn9XcyfGDOzegMDRg47uc=
|
||||
github.com/apache/thrift v0.22.0/go.mod h1:1e7J/O1Ae6ZQMTYdy9xa3w9k+XHWPfRvdPyJeynQ+/g=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/duckdb/duckdb-go-bindings v0.1.21 h1:bOb/MXNT4PN5JBZ7wpNg6hrj9+cuDjWDa4ee9UdbVyI=
|
||||
github.com/duckdb/duckdb-go-bindings v0.1.21/go.mod h1:pBnfviMzANT/9hi4bg+zW4ykRZZPCXlVuvBWEcZofkc=
|
||||
github.com/duckdb/duckdb-go-bindings/darwin-amd64 v0.1.21 h1:Sjjhf2F/zCjPF53c2VXOSKk0PzieMriSoyr5wfvr9d8=
|
||||
github.com/duckdb/duckdb-go-bindings/darwin-amd64 v0.1.21/go.mod h1:Ezo7IbAfB8NP7CqPIN8XEHKUg5xdRRQhcPPlCXImXYA=
|
||||
github.com/duckdb/duckdb-go-bindings/darwin-arm64 v0.1.21 h1:IUk0FFUB6dpWLhlN9hY1mmdPX7Hkn3QpyrAmn8pmS8g=
|
||||
github.com/duckdb/duckdb-go-bindings/darwin-arm64 v0.1.21/go.mod h1:eS7m/mLnPQgVF4za1+xTyorKRBuK0/BA44Oy6DgrGXI=
|
||||
github.com/duckdb/duckdb-go-bindings/linux-amd64 v0.1.21 h1:Qpc7ZE3n6Nwz30KTvaAwI6nGkXjXmMxBTdFpC8zDEYI=
|
||||
github.com/duckdb/duckdb-go-bindings/linux-amd64 v0.1.21/go.mod h1:1GOuk1PixiESxLaCGFhag+oFi7aP+9W8byymRAvunBk=
|
||||
github.com/duckdb/duckdb-go-bindings/linux-arm64 v0.1.21 h1:eX2DhobAZOgjXkh8lPnKAyrxj8gXd2nm+K71f6KV/mo=
|
||||
github.com/duckdb/duckdb-go-bindings/linux-arm64 v0.1.21/go.mod h1:o7crKMpT2eOIi5/FY6HPqaXcvieeLSqdXXaXbruGX7w=
|
||||
github.com/duckdb/duckdb-go-bindings/windows-amd64 v0.1.21 h1:hhziFnGV7mpA+v5J5G2JnYQ+UWCCP3NQ+OTvxFX10D8=
|
||||
github.com/duckdb/duckdb-go-bindings/windows-amd64 v0.1.21/go.mod h1:IlOhJdVKUJCAPj3QsDszUo8DVdvp1nBFp4TUJVdw99s=
|
||||
github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE=
|
||||
github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
||||
github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE=
|
||||
github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
||||
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q=
|
||||
github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
|
||||
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4=
|
||||
github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/marcboeker/go-duckdb/arrowmapping v0.0.21 h1:geHnVjlsAJGczSWEqYigy/7ARuD+eBtjd0kLN80SPJQ=
|
||||
github.com/marcboeker/go-duckdb/arrowmapping v0.0.21/go.mod h1:flFTc9MSqQCh2Xm62RYvG3Kyj29h7OtsTb6zUx1CdK8=
|
||||
github.com/marcboeker/go-duckdb/mapping v0.0.21 h1:6woNXZn8EfYdc9Vbv0qR6acnt0TM1s1eFqnrJZVrqEs=
|
||||
github.com/marcboeker/go-duckdb/mapping v0.0.21/go.mod h1:q3smhpLyv2yfgkQd7gGHMd+H/Z905y+WYIUjrl29vT4=
|
||||
github.com/marcboeker/go-duckdb/v2 v2.4.3 h1:bHUkphPsAp2Bh/VFEdiprGpUekxBNZiWWtK+Bv/ljRk=
|
||||
github.com/marcboeker/go-duckdb/v2 v2.4.3/go.mod h1:taim9Hktg2igHdNBmg5vgTfHAlV26z3gBI0QXQOcuyI=
|
||||
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs=
|
||||
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
|
||||
github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI=
|
||||
github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE=
|
||||
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
||||
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8=
|
||||
github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
|
||||
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"fuego/pkg/types"
|
||||
"jiggablend/pkg/types"
|
||||
)
|
||||
|
||||
// handleGenerateRegistrationToken generates a new registration token
|
||||
@@ -126,7 +126,7 @@ func (s *Server) handleDeleteRunner(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *Server) handleListRunnersAdmin(w http.ResponseWriter, r *http.Request) {
|
||||
rows, err := s.db.Query(
|
||||
`SELECT id, name, hostname, ip_address, status, last_heartbeat, capabilities,
|
||||
registration_token, verified, created_at
|
||||
registration_token, verified, priority, created_at
|
||||
FROM runners ORDER BY created_at DESC`,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -144,7 +144,7 @@ func (s *Server) handleListRunnersAdmin(w http.ResponseWriter, r *http.Request)
|
||||
err := rows.Scan(
|
||||
&runner.ID, &runner.Name, &runner.Hostname, &runner.IPAddress,
|
||||
&runner.Status, &runner.LastHeartbeat, &runner.Capabilities,
|
||||
®istrationToken, &verified, &runner.CreatedAt,
|
||||
®istrationToken, &verified, &runner.Priority, &runner.CreatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
s.respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to scan runner: %v", err))
|
||||
@@ -161,9 +161,206 @@ func (s *Server) handleListRunnersAdmin(w http.ResponseWriter, r *http.Request)
|
||||
"capabilities": runner.Capabilities,
|
||||
"registration_token": registrationToken.String,
|
||||
"verified": verified,
|
||||
"priority": runner.Priority,
|
||||
"created_at": runner.CreatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
s.respondJSON(w, http.StatusOK, runners)
|
||||
}
|
||||
|
||||
// handleListUsers lists all users
|
||||
func (s *Server) handleListUsers(w http.ResponseWriter, r *http.Request) {
|
||||
// Get first user ID to mark it in the response
|
||||
firstUserID, err := s.auth.GetFirstUserID()
|
||||
if err != nil {
|
||||
// If no users exist, firstUserID will be 0, which is fine
|
||||
firstUserID = 0
|
||||
}
|
||||
|
||||
rows, err := s.db.Query(
|
||||
`SELECT id, email, name, oauth_provider, is_admin, created_at
|
||||
FROM users ORDER BY created_at DESC`,
|
||||
)
|
||||
if err != nil {
|
||||
s.respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to query users: %v", err))
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
users := []map[string]interface{}{}
|
||||
for rows.Next() {
|
||||
var userID int64
|
||||
var email, name, oauthProvider string
|
||||
var isAdmin bool
|
||||
var createdAt time.Time
|
||||
|
||||
err := rows.Scan(&userID, &email, &name, &oauthProvider, &isAdmin, &createdAt)
|
||||
if err != nil {
|
||||
s.respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to scan user: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Get job count for this user
|
||||
var jobCount int
|
||||
err = s.db.QueryRow("SELECT COUNT(*) FROM jobs WHERE user_id = ?", userID).Scan(&jobCount)
|
||||
if err != nil {
|
||||
jobCount = 0 // Default to 0 if query fails
|
||||
}
|
||||
|
||||
users = append(users, map[string]interface{}{
|
||||
"id": userID,
|
||||
"email": email,
|
||||
"name": name,
|
||||
"oauth_provider": oauthProvider,
|
||||
"is_admin": isAdmin,
|
||||
"created_at": createdAt,
|
||||
"job_count": jobCount,
|
||||
"is_first_user": userID == firstUserID,
|
||||
})
|
||||
}
|
||||
|
||||
s.respondJSON(w, http.StatusOK, users)
|
||||
}
|
||||
|
||||
// handleGetUserJobs gets all jobs for a specific user
|
||||
func (s *Server) handleGetUserJobs(w http.ResponseWriter, r *http.Request) {
|
||||
userID, err := parseID(r, "id")
|
||||
if err != nil {
|
||||
s.respondError(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Verify user exists
|
||||
var exists bool
|
||||
err = s.db.QueryRow("SELECT EXISTS(SELECT 1 FROM users WHERE id = ?)", userID).Scan(&exists)
|
||||
if err != nil || !exists {
|
||||
s.respondError(w, http.StatusNotFound, "User not found")
|
||||
return
|
||||
}
|
||||
|
||||
rows, err := s.db.Query(
|
||||
`SELECT id, user_id, job_type, name, status, progress, frame_start, frame_end, output_format,
|
||||
allow_parallel_runners, timeout_seconds, blend_metadata, created_at, started_at, completed_at, error_message
|
||||
FROM jobs WHERE user_id = ? ORDER BY created_at DESC`,
|
||||
userID,
|
||||
)
|
||||
if err != nil {
|
||||
s.respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to query jobs: %v", err))
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
jobs := []types.Job{}
|
||||
for rows.Next() {
|
||||
var job types.Job
|
||||
var jobType string
|
||||
var startedAt, completedAt sql.NullTime
|
||||
var blendMetadataJSON sql.NullString
|
||||
var errorMessage sql.NullString
|
||||
var frameStart, frameEnd sql.NullInt64
|
||||
var outputFormat sql.NullString
|
||||
var allowParallelRunners sql.NullBool
|
||||
|
||||
err := rows.Scan(
|
||||
&job.ID, &job.UserID, &jobType, &job.Name, &job.Status, &job.Progress,
|
||||
&frameStart, &frameEnd, &outputFormat, &allowParallelRunners, &job.TimeoutSeconds,
|
||||
&blendMetadataJSON, &job.CreatedAt, &startedAt, &completedAt, &errorMessage,
|
||||
)
|
||||
if err != nil {
|
||||
s.respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to scan job: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
job.JobType = types.JobType(jobType)
|
||||
if frameStart.Valid {
|
||||
fs := int(frameStart.Int64)
|
||||
job.FrameStart = &fs
|
||||
}
|
||||
if frameEnd.Valid {
|
||||
fe := int(frameEnd.Int64)
|
||||
job.FrameEnd = &fe
|
||||
}
|
||||
if outputFormat.Valid {
|
||||
job.OutputFormat = &outputFormat.String
|
||||
}
|
||||
if allowParallelRunners.Valid {
|
||||
job.AllowParallelRunners = &allowParallelRunners.Bool
|
||||
}
|
||||
if startedAt.Valid {
|
||||
job.StartedAt = &startedAt.Time
|
||||
}
|
||||
if completedAt.Valid {
|
||||
job.CompletedAt = &completedAt.Time
|
||||
}
|
||||
if blendMetadataJSON.Valid && blendMetadataJSON.String != "" {
|
||||
var metadata types.BlendMetadata
|
||||
if err := json.Unmarshal([]byte(blendMetadataJSON.String), &metadata); err == nil {
|
||||
job.BlendMetadata = &metadata
|
||||
}
|
||||
}
|
||||
if errorMessage.Valid {
|
||||
job.ErrorMessage = errorMessage.String
|
||||
}
|
||||
|
||||
jobs = append(jobs, job)
|
||||
}
|
||||
|
||||
s.respondJSON(w, http.StatusOK, jobs)
|
||||
}
|
||||
|
||||
// handleGetRegistrationEnabled gets the registration enabled setting
|
||||
func (s *Server) handleGetRegistrationEnabled(w http.ResponseWriter, r *http.Request) {
|
||||
enabled, err := s.auth.IsRegistrationEnabled()
|
||||
if err != nil {
|
||||
s.respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to get registration setting: %v", err))
|
||||
return
|
||||
}
|
||||
s.respondJSON(w, http.StatusOK, map[string]bool{"enabled": enabled})
|
||||
}
|
||||
|
||||
// handleSetRegistrationEnabled sets the registration enabled setting
|
||||
func (s *Server) handleSetRegistrationEnabled(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
s.respondError(w, http.StatusBadRequest, "Invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.auth.SetRegistrationEnabled(req.Enabled); err != nil {
|
||||
s.respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to set registration setting: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
s.respondJSON(w, http.StatusOK, map[string]bool{"enabled": req.Enabled})
|
||||
}
|
||||
|
||||
// handleSetUserAdminStatus sets a user's admin status (admin only)
|
||||
func (s *Server) handleSetUserAdminStatus(w http.ResponseWriter, r *http.Request) {
|
||||
targetUserID, err := parseID(r, "id")
|
||||
if err != nil {
|
||||
s.respondError(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
s.respondError(w, http.StatusBadRequest, "Invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.auth.SetUserAdminStatus(targetUserID, req.IsAdmin); err != nil {
|
||||
s.respondError(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
s.respondJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"user_id": targetUserID,
|
||||
"is_admin": req.IsAdmin,
|
||||
"message": "Admin status updated successfully",
|
||||
})
|
||||
}
|
||||
|
||||
1246
internal/api/jobs.go
1246
internal/api/jobs.go
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"fuego/pkg/types"
|
||||
"jiggablend/pkg/types"
|
||||
)
|
||||
|
||||
// handleSubmitMetadata handles metadata submission from runner
|
||||
@@ -19,7 +19,7 @@ func (s *Server) handleSubmitMetadata(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Get runner ID from context (set by runnerAuthMiddleware)
|
||||
runnerID, ok := r.Context().Value("runner_id").(int64)
|
||||
runnerID, ok := r.Context().Value(runnerIDContextKey).(int64)
|
||||
if !ok {
|
||||
s.respondError(w, http.StatusUnauthorized, "runner_id not found in context")
|
||||
return
|
||||
@@ -44,16 +44,32 @@ func (s *Server) handleSubmitMetadata(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Find the metadata extraction task for this job
|
||||
// First try to find task assigned to this runner, then fall back to any metadata task for this job
|
||||
var taskID int64
|
||||
err = s.db.QueryRow(
|
||||
`SELECT id FROM tasks WHERE job_id = ? AND task_type = ? AND runner_id = ?`,
|
||||
jobID, types.TaskTypeMetadata, runnerID,
|
||||
).Scan(&taskID)
|
||||
if err == sql.ErrNoRows {
|
||||
s.respondError(w, http.StatusNotFound, "Metadata extraction task not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
// Fall back to any metadata task for this job (in case assignment changed)
|
||||
err = s.db.QueryRow(
|
||||
`SELECT id FROM tasks WHERE job_id = ? AND task_type = ? ORDER BY created_at DESC LIMIT 1`,
|
||||
jobID, types.TaskTypeMetadata,
|
||||
).Scan(&taskID)
|
||||
if err == sql.ErrNoRows {
|
||||
s.respondError(w, http.StatusNotFound, "Metadata extraction task not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
s.respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to find task: %v", err))
|
||||
return
|
||||
}
|
||||
// Update the task to be assigned to this runner if it wasn't already
|
||||
s.db.Exec(
|
||||
`UPDATE tasks SET runner_id = ? WHERE id = ? AND runner_id IS NULL`,
|
||||
runnerID, taskID,
|
||||
)
|
||||
} else if err != nil {
|
||||
s.respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to find task: %v", err))
|
||||
return
|
||||
}
|
||||
@@ -82,6 +98,9 @@ func (s *Server) handleSubmitMetadata(w http.ResponseWriter, r *http.Request) {
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("Failed to mark metadata task as completed: %v", err)
|
||||
} else {
|
||||
// Update job status and progress after metadata task completes
|
||||
s.updateJobStatusFromTasks(jobID)
|
||||
}
|
||||
|
||||
log.Printf("Metadata extracted for job %d: frame_start=%d, frame_end=%d", jobID, metadata.FrameStart, metadata.FrameEnd)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,10 +10,10 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
authpkg "fuego/internal/auth"
|
||||
"fuego/internal/database"
|
||||
"fuego/internal/storage"
|
||||
"fuego/pkg/types"
|
||||
authpkg "jiggablend/internal/auth"
|
||||
"jiggablend/internal/database"
|
||||
"jiggablend/internal/storage"
|
||||
"jiggablend/pkg/types"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
@@ -35,6 +35,12 @@ type Server struct {
|
||||
runnerConnsMu sync.RWMutex
|
||||
frontendConns map[string]*websocket.Conn // key: "jobId:taskId"
|
||||
frontendConnsMu sync.RWMutex
|
||||
// Mutexes for each frontend connection to serialize writes
|
||||
frontendConnsWriteMu map[string]*sync.Mutex // key: "jobId:taskId"
|
||||
frontendConnsWriteMuMu sync.RWMutex
|
||||
// Throttling for progress updates (per job)
|
||||
progressUpdateTimes map[int64]time.Time // key: jobID
|
||||
progressUpdateTimesMu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewServer creates a new API server
|
||||
@@ -57,8 +63,10 @@ func NewServer(db *database.DB, auth *authpkg.Auth, storage *storage.Storage) (*
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
},
|
||||
runnerConns: make(map[int64]*websocket.Conn),
|
||||
frontendConns: make(map[string]*websocket.Conn),
|
||||
runnerConns: make(map[int64]*websocket.Conn),
|
||||
frontendConns: make(map[string]*websocket.Conn),
|
||||
frontendConnsWriteMu: make(map[string]*sync.Mutex),
|
||||
progressUpdateTimes: make(map[int64]time.Time),
|
||||
}
|
||||
|
||||
s.setupMiddleware()
|
||||
@@ -72,13 +80,14 @@ func NewServer(db *database.DB, auth *authpkg.Auth, storage *storage.Storage) (*
|
||||
func (s *Server) setupMiddleware() {
|
||||
s.router.Use(middleware.Logger)
|
||||
s.router.Use(middleware.Recoverer)
|
||||
s.router.Use(middleware.Timeout(60 * time.Second))
|
||||
// Note: Timeout middleware is NOT applied globally to avoid conflicts with WebSocket connections
|
||||
// WebSocket connections are long-lived and should not have HTTP timeouts
|
||||
|
||||
s.router.Use(cors.Handler(cors.Options{
|
||||
AllowedOrigins: []string{"*"},
|
||||
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
||||
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type"},
|
||||
ExposedHeaders: []string{"Link"},
|
||||
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "Range"},
|
||||
ExposedHeaders: []string{"Link", "Content-Range", "Accept-Ranges", "Content-Length"},
|
||||
AllowCredentials: true,
|
||||
MaxAge: 300,
|
||||
}))
|
||||
@@ -88,12 +97,17 @@ func (s *Server) setupMiddleware() {
|
||||
func (s *Server) setupRoutes() {
|
||||
// Public routes
|
||||
s.router.Route("/api/auth", func(r chi.Router) {
|
||||
r.Get("/providers", s.handleGetAuthProviders)
|
||||
r.Get("/google/login", s.handleGoogleLogin)
|
||||
r.Get("/google/callback", s.handleGoogleCallback)
|
||||
r.Get("/discord/login", s.handleDiscordLogin)
|
||||
r.Get("/discord/callback", s.handleDiscordCallback)
|
||||
r.Get("/local/available", s.handleLocalLoginAvailable)
|
||||
r.Post("/local/register", s.handleLocalRegister)
|
||||
r.Post("/local/login", s.handleLocalLogin)
|
||||
r.Post("/logout", s.handleLogout)
|
||||
r.Get("/me", s.handleGetMe)
|
||||
r.Post("/change-password", s.handleChangePassword)
|
||||
})
|
||||
|
||||
// Protected routes
|
||||
@@ -105,6 +119,7 @@ func (s *Server) setupRoutes() {
|
||||
r.Get("/", s.handleListJobs)
|
||||
r.Get("/{id}", s.handleGetJob)
|
||||
r.Delete("/{id}", s.handleCancelJob)
|
||||
r.Post("/{id}/delete", s.handleDeleteJob)
|
||||
r.Post("/{id}/upload", s.handleUploadJobFile)
|
||||
r.Get("/{id}/files", s.handleListJobFiles)
|
||||
r.Get("/{id}/files/{fileId}/download", s.handleDownloadJobFile)
|
||||
@@ -112,18 +127,17 @@ func (s *Server) setupRoutes() {
|
||||
r.Get("/{id}/metadata", s.handleGetJobMetadata)
|
||||
r.Get("/{id}/tasks", s.handleListJobTasks)
|
||||
r.Get("/{id}/tasks/{taskId}/logs", s.handleGetTaskLogs)
|
||||
r.Get("/{id}/tasks/{taskId}/logs/ws", s.handleStreamTaskLogsWebSocket)
|
||||
// WebSocket route - no timeout middleware (long-lived connection)
|
||||
r.With(func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Remove timeout middleware for WebSocket
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}).Get("/{id}/tasks/{taskId}/logs/ws", s.handleStreamTaskLogsWebSocket)
|
||||
r.Get("/{id}/tasks/{taskId}/steps", s.handleGetTaskSteps)
|
||||
r.Post("/{id}/tasks/{taskId}/retry", s.handleRetryTask)
|
||||
})
|
||||
|
||||
s.router.Route("/api/runners", func(r chi.Router) {
|
||||
r.Use(func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(s.auth.Middleware(next.ServeHTTP))
|
||||
})
|
||||
r.Get("/", s.handleListRunners)
|
||||
})
|
||||
|
||||
// Admin routes
|
||||
s.router.Route("/api/admin", func(r chi.Router) {
|
||||
r.Use(func(next http.Handler) http.Handler {
|
||||
@@ -139,14 +153,23 @@ func (s *Server) setupRoutes() {
|
||||
r.Post("/{id}/verify", s.handleVerifyRunner)
|
||||
r.Delete("/{id}", s.handleDeleteRunner)
|
||||
})
|
||||
r.Route("/users", func(r chi.Router) {
|
||||
r.Get("/", s.handleListUsers)
|
||||
r.Get("/{id}/jobs", s.handleGetUserJobs)
|
||||
r.Post("/{id}/admin", s.handleSetUserAdminStatus)
|
||||
})
|
||||
r.Route("/settings", func(r chi.Router) {
|
||||
r.Get("/registration", s.handleGetRegistrationEnabled)
|
||||
r.Post("/registration", s.handleSetRegistrationEnabled)
|
||||
})
|
||||
})
|
||||
|
||||
// Runner API
|
||||
s.router.Route("/api/runner", func(r chi.Router) {
|
||||
// Registration doesn't require auth (uses token)
|
||||
r.Post("/register", s.handleRegisterRunner)
|
||||
r.With(middleware.Timeout(60*time.Second)).Post("/register", s.handleRegisterRunner)
|
||||
|
||||
// WebSocket endpoint (auth handled in handler)
|
||||
// WebSocket endpoint (auth handled in handler) - no timeout middleware
|
||||
r.Get("/ws", s.handleRunnerWebSocket)
|
||||
|
||||
// File operations still use HTTP (WebSocket not suitable for large files)
|
||||
@@ -156,7 +179,7 @@ func (s *Server) setupRoutes() {
|
||||
})
|
||||
r.Post("/tasks/{id}/progress", s.handleUpdateTaskProgress)
|
||||
r.Post("/tasks/{id}/steps", s.handleUpdateTaskStep)
|
||||
r.Get("/files/{jobId}/{fileName}", s.handleDownloadFileForRunner)
|
||||
r.Get("/files/{jobId}/*", s.handleDownloadFileForRunner)
|
||||
r.Post("/files/{jobId}/upload", s.handleUploadFileFromRunner)
|
||||
r.Get("/jobs/{jobId}/status", s.handleGetJobStatusForRunner)
|
||||
r.Get("/jobs/{jobId}/files", s.handleGetJobFilesForRunner)
|
||||
@@ -205,6 +228,11 @@ func (s *Server) handleGoogleCallback(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
session, err := s.auth.GoogleCallback(r.Context(), code)
|
||||
if err != nil {
|
||||
// If registration is disabled, redirect back to login with error
|
||||
if err.Error() == "registration is disabled" {
|
||||
http.Redirect(w, r, "/?error=registration_disabled", http.StatusFound)
|
||||
return
|
||||
}
|
||||
s.respondError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
@@ -240,6 +268,11 @@ func (s *Server) handleDiscordCallback(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
session, err := s.auth.DiscordCallback(r.Context(), code)
|
||||
if err != nil {
|
||||
// If registration is disabled, redirect back to login with error
|
||||
if err.Error() == "registration is disabled" {
|
||||
http.Redirect(w, r, "/?error=registration_disabled", http.StatusFound)
|
||||
return
|
||||
}
|
||||
s.respondError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
@@ -293,6 +326,166 @@ func (s *Server) handleGetMe(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) handleGetAuthProviders(w http.ResponseWriter, r *http.Request) {
|
||||
s.respondJSON(w, http.StatusOK, map[string]bool{
|
||||
"google": s.auth.IsGoogleOAuthConfigured(),
|
||||
"discord": s.auth.IsDiscordOAuthConfigured(),
|
||||
"local": s.auth.IsLocalLoginEnabled(),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) handleLocalLoginAvailable(w http.ResponseWriter, r *http.Request) {
|
||||
s.respondJSON(w, http.StatusOK, map[string]bool{
|
||||
"available": s.auth.IsLocalLoginEnabled(),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) handleLocalRegister(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
s.respondError(w, http.StatusBadRequest, "Invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
if req.Email == "" || req.Name == "" || req.Password == "" {
|
||||
s.respondError(w, http.StatusBadRequest, "Email, name, and password are required")
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Password) < 8 {
|
||||
s.respondError(w, http.StatusBadRequest, "Password must be at least 8 characters long")
|
||||
return
|
||||
}
|
||||
|
||||
session, err := s.auth.RegisterLocalUser(req.Email, req.Name, req.Password)
|
||||
if err != nil {
|
||||
s.respondError(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
sessionID := s.auth.CreateSession(session)
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "session_id",
|
||||
Value: sessionID,
|
||||
Path: "/",
|
||||
MaxAge: 86400,
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
})
|
||||
|
||||
s.respondJSON(w, http.StatusCreated, map[string]interface{}{
|
||||
"message": "Registration successful",
|
||||
"user": map[string]interface{}{
|
||||
"id": session.UserID,
|
||||
"email": session.Email,
|
||||
"name": session.Name,
|
||||
"is_admin": session.IsAdmin,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) handleLocalLogin(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
s.respondError(w, http.StatusBadRequest, "Invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
if req.Username == "" || req.Password == "" {
|
||||
s.respondError(w, http.StatusBadRequest, "Username and password are required")
|
||||
return
|
||||
}
|
||||
|
||||
session, err := s.auth.LocalLogin(req.Username, req.Password)
|
||||
if err != nil {
|
||||
s.respondError(w, http.StatusUnauthorized, "Invalid credentials")
|
||||
return
|
||||
}
|
||||
|
||||
sessionID := s.auth.CreateSession(session)
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "session_id",
|
||||
Value: sessionID,
|
||||
Path: "/",
|
||||
MaxAge: 86400,
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
})
|
||||
|
||||
s.respondJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"message": "Login successful",
|
||||
"user": map[string]interface{}{
|
||||
"id": session.UserID,
|
||||
"email": session.Email,
|
||||
"name": session.Name,
|
||||
"is_admin": session.IsAdmin,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) handleChangePassword(w http.ResponseWriter, r *http.Request) {
|
||||
userID, err := getUserID(r)
|
||||
if err != nil {
|
||||
s.respondError(w, http.StatusUnauthorized, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
OldPassword string `json:"old_password"`
|
||||
NewPassword string `json:"new_password"`
|
||||
TargetUserID *int64 `json:"target_user_id,omitempty"` // For admin to change other users' passwords
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
s.respondError(w, http.StatusBadRequest, "Invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
if req.NewPassword == "" {
|
||||
s.respondError(w, http.StatusBadRequest, "New password is required")
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.NewPassword) < 8 {
|
||||
s.respondError(w, http.StatusBadRequest, "Password must be at least 8 characters long")
|
||||
return
|
||||
}
|
||||
|
||||
isAdmin := authpkg.IsAdmin(r.Context())
|
||||
|
||||
// If target_user_id is provided and user is admin, allow changing other user's password
|
||||
if req.TargetUserID != nil && isAdmin {
|
||||
if err := s.auth.AdminChangePassword(*req.TargetUserID, req.NewPassword); err != nil {
|
||||
s.respondError(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
s.respondJSON(w, http.StatusOK, map[string]string{"message": "Password changed successfully"})
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise, user is changing their own password (requires old password)
|
||||
if req.OldPassword == "" {
|
||||
s.respondError(w, http.StatusBadRequest, "Old password is required")
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.auth.ChangePassword(userID, req.OldPassword, req.NewPassword); err != nil {
|
||||
s.respondError(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
s.respondJSON(w, http.StatusOK, map[string]string{"message": "Password changed successfully"})
|
||||
}
|
||||
|
||||
// Helper to get user ID from context
|
||||
func getUserID(r *http.Request) (int64, error) {
|
||||
userID, ok := authpkg.GetUserID(r.Context())
|
||||
@@ -315,6 +508,7 @@ func parseID(r *http.Request, param string) (int64, error) {
|
||||
// StartBackgroundTasks starts background goroutines for error recovery
|
||||
func (s *Server) StartBackgroundTasks() {
|
||||
go s.recoverStuckTasks()
|
||||
go s.cleanupOldMetadataJobs()
|
||||
}
|
||||
|
||||
// recoverStuckTasks periodically checks for dead runners and stuck tasks
|
||||
@@ -322,8 +516,8 @@ func (s *Server) recoverStuckTasks() {
|
||||
ticker := time.NewTicker(10 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
// Also distribute tasks every 5 seconds
|
||||
distributeTicker := time.NewTicker(5 * time.Second)
|
||||
// Also distribute tasks every 10 seconds (reduced frequency since we have event-driven distribution)
|
||||
distributeTicker := time.NewTicker(10 * time.Second)
|
||||
defer distributeTicker.Stop()
|
||||
|
||||
go func() {
|
||||
@@ -341,9 +535,10 @@ func (s *Server) recoverStuckTasks() {
|
||||
}()
|
||||
|
||||
// Find dead runners (no heartbeat for 90 seconds)
|
||||
// But only mark as dead if they're not actually connected via WebSocket
|
||||
rows, err := s.db.Query(
|
||||
`SELECT id FROM runners
|
||||
WHERE last_heartbeat < datetime('now', '-90 seconds')
|
||||
WHERE last_heartbeat < CURRENT_TIMESTAMP - INTERVAL '90 seconds'
|
||||
AND status = ?`,
|
||||
types.RunnerStatusOnline,
|
||||
)
|
||||
@@ -354,12 +549,20 @@ func (s *Server) recoverStuckTasks() {
|
||||
defer rows.Close()
|
||||
|
||||
var deadRunnerIDs []int64
|
||||
s.runnerConnsMu.RLock()
|
||||
for rows.Next() {
|
||||
var runnerID int64
|
||||
if err := rows.Scan(&runnerID); err == nil {
|
||||
deadRunnerIDs = append(deadRunnerIDs, runnerID)
|
||||
// Only mark as dead if not actually connected via WebSocket
|
||||
// The WebSocket connection is the source of truth
|
||||
if _, stillConnected := s.runnerConns[runnerID]; !stillConnected {
|
||||
deadRunnerIDs = append(deadRunnerIDs, runnerID)
|
||||
}
|
||||
// If still connected, heartbeat should be updated by pong handler or heartbeat message
|
||||
// No need to manually update here - if it's stale, the pong handler isn't working
|
||||
}
|
||||
}
|
||||
s.runnerConnsMu.RUnlock()
|
||||
rows.Close()
|
||||
|
||||
if len(deadRunnerIDs) == 0 {
|
||||
@@ -370,67 +573,7 @@ func (s *Server) recoverStuckTasks() {
|
||||
|
||||
// Reset tasks assigned to dead runners
|
||||
for _, runnerID := range deadRunnerIDs {
|
||||
// Get tasks assigned to this runner
|
||||
taskRows, err := s.db.Query(
|
||||
`SELECT id, retry_count, max_retries FROM tasks
|
||||
WHERE runner_id = ? AND status = ?`,
|
||||
runnerID, types.TaskStatusRunning,
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("Failed to query tasks for runner %d: %v", runnerID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
var tasksToReset []struct {
|
||||
ID int64
|
||||
RetryCount int
|
||||
MaxRetries int
|
||||
}
|
||||
|
||||
for taskRows.Next() {
|
||||
var t struct {
|
||||
ID int64
|
||||
RetryCount int
|
||||
MaxRetries int
|
||||
}
|
||||
if err := taskRows.Scan(&t.ID, &t.RetryCount, &t.MaxRetries); err == nil {
|
||||
tasksToReset = append(tasksToReset, t)
|
||||
}
|
||||
}
|
||||
taskRows.Close()
|
||||
|
||||
// Reset or fail tasks
|
||||
for _, task := range tasksToReset {
|
||||
if task.RetryCount >= task.MaxRetries {
|
||||
// Mark as failed
|
||||
_, err = s.db.Exec(
|
||||
`UPDATE tasks SET status = ?, error_message = ?, runner_id = NULL
|
||||
WHERE id = ?`,
|
||||
types.TaskStatusFailed, "Runner died, max retries exceeded", task.ID,
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("Failed to mark task %d as failed: %v", task.ID, err)
|
||||
}
|
||||
} else {
|
||||
// Reset to pending
|
||||
_, err = s.db.Exec(
|
||||
`UPDATE tasks SET status = ?, runner_id = NULL, current_step = NULL,
|
||||
retry_count = retry_count + 1 WHERE id = ?`,
|
||||
types.TaskStatusPending, task.ID,
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("Failed to reset task %d: %v", task.ID, err)
|
||||
} else {
|
||||
// Add log entry
|
||||
_, _ = s.db.Exec(
|
||||
`INSERT INTO task_logs (task_id, log_level, message, step_name, created_at)
|
||||
VALUES (?, ?, ?, ?, ?)`,
|
||||
task.ID, types.LogLevelWarn, fmt.Sprintf("Runner died, task reset (retry %d/%d)", task.RetryCount+1, task.MaxRetries),
|
||||
"", time.Now(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
s.redistributeRunnerTasks(runnerID)
|
||||
|
||||
// Mark runner as offline
|
||||
_, _ = s.db.Exec(
|
||||
@@ -457,7 +600,7 @@ func (s *Server) recoverTaskTimeouts() {
|
||||
WHERE t.status = ?
|
||||
AND t.started_at IS NOT NULL
|
||||
AND (t.timeout_seconds IS NULL OR
|
||||
datetime(t.started_at, '+' || t.timeout_seconds || ' seconds') < datetime('now'))`,
|
||||
t.started_at + INTERVAL (t.timeout_seconds || ' seconds') < CURRENT_TIMESTAMP)`,
|
||||
types.TaskStatusRunning,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -507,13 +650,8 @@ func (s *Server) recoverTaskTimeouts() {
|
||||
types.TaskStatusPending, taskID,
|
||||
)
|
||||
if err == nil {
|
||||
// Add log entry
|
||||
_, _ = s.db.Exec(
|
||||
`INSERT INTO task_logs (task_id, log_level, message, step_name, created_at)
|
||||
VALUES (?, ?, ?, ?, ?)`,
|
||||
taskID, types.LogLevelWarn, fmt.Sprintf("Task timeout exceeded, resetting (retry %d/%d)", retryCount+1, maxRetries),
|
||||
"", time.Now(),
|
||||
)
|
||||
// Add log entry using the helper function
|
||||
s.logTaskEvent(taskID, nil, types.LogLevelWarn, fmt.Sprintf("Task timeout exceeded, resetting (retry %d/%d)", retryCount+1, maxRetries), "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,21 +5,24 @@ import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
)
|
||||
|
||||
// Auth handles authentication
|
||||
type Auth struct {
|
||||
db *sql.DB
|
||||
googleConfig *oauth2.Config
|
||||
discordConfig *oauth2.Config
|
||||
sessionStore map[string]*Session
|
||||
db *sql.DB
|
||||
googleConfig *oauth2.Config
|
||||
discordConfig *oauth2.Config
|
||||
sessionStore map[string]*Session
|
||||
}
|
||||
|
||||
// Session represents a user session
|
||||
@@ -67,9 +70,95 @@ func NewAuth(db *sql.DB) (*Auth, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize admin settings on startup to ensure they persist between boots
|
||||
if err := auth.initializeSettings(); err != nil {
|
||||
log.Printf("Warning: Failed to initialize admin settings: %v", err)
|
||||
// Don't fail startup, but log the warning
|
||||
}
|
||||
|
||||
// Initialize test local user from environment variables (for testing only)
|
||||
if err := auth.initializeTestUser(); err != nil {
|
||||
log.Printf("Warning: Failed to initialize test user: %v", err)
|
||||
// Don't fail startup, but log the warning
|
||||
}
|
||||
|
||||
return auth, nil
|
||||
}
|
||||
|
||||
// initializeSettings ensures all admin settings are initialized with defaults if they don't exist
|
||||
func (a *Auth) initializeSettings() error {
|
||||
// Initialize registration_enabled setting (default: true) if it doesn't exist
|
||||
var settingCount int
|
||||
err := a.db.QueryRow("SELECT COUNT(*) FROM settings WHERE key = ?", "registration_enabled").Scan(&settingCount)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check registration_enabled setting: %w", err)
|
||||
}
|
||||
if settingCount == 0 {
|
||||
_, err = a.db.Exec(
|
||||
`INSERT INTO settings (key, value, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP)`,
|
||||
"registration_enabled", "true",
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize registration_enabled setting: %w", err)
|
||||
}
|
||||
log.Printf("Initialized admin setting: registration_enabled = true")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// initializeTestUser creates a test local user from environment variables (for testing only)
|
||||
func (a *Auth) initializeTestUser() error {
|
||||
testEmail := os.Getenv("LOCAL_TEST_EMAIL")
|
||||
testPassword := os.Getenv("LOCAL_TEST_PASSWORD")
|
||||
|
||||
if testEmail == "" || testPassword == "" {
|
||||
// No test user configured, skip
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if user already exists
|
||||
var exists bool
|
||||
err := a.db.QueryRow("SELECT EXISTS(SELECT 1 FROM users WHERE email = ? AND oauth_provider = 'local')", testEmail).Scan(&exists)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if test user exists: %w", err)
|
||||
}
|
||||
|
||||
if exists {
|
||||
// User already exists, skip creation
|
||||
log.Printf("Test user %s already exists, skipping creation", testEmail)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Hash password
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(testPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to hash test user password: %w", err)
|
||||
}
|
||||
|
||||
// Check if this is the first user (make them admin)
|
||||
var userCount int
|
||||
a.db.QueryRow("SELECT COUNT(*) FROM users").Scan(&userCount)
|
||||
isAdmin := userCount == 0
|
||||
|
||||
// Create test user (use email as name if no name is provided)
|
||||
testName := testEmail
|
||||
if atIndex := strings.Index(testEmail, "@"); atIndex > 0 {
|
||||
testName = testEmail[:atIndex]
|
||||
}
|
||||
|
||||
// Create test user
|
||||
_, err = a.db.Exec(
|
||||
"INSERT INTO users (email, name, oauth_provider, oauth_id, password_hash, is_admin) VALUES (?, ?, 'local', ?, ?, ?)",
|
||||
testEmail, testName, testEmail, string(hashedPassword), isAdmin,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create test user: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("Created test user: %s (admin: %v)", testEmail, isAdmin)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GoogleLoginURL returns the Google OAuth login URL
|
||||
func (a *Auth) GoogleLoginURL() (string, error) {
|
||||
if a.googleConfig == nil {
|
||||
@@ -150,36 +239,119 @@ func (a *Auth) DiscordCallback(ctx context.Context, code string) (*Session, erro
|
||||
return a.getOrCreateUser("discord", userInfo.ID, userInfo.Email, userInfo.Username)
|
||||
}
|
||||
|
||||
// IsRegistrationEnabled checks if new user registration is enabled
|
||||
func (a *Auth) IsRegistrationEnabled() (bool, error) {
|
||||
var value string
|
||||
err := a.db.QueryRow("SELECT value FROM settings WHERE key = ?", "registration_enabled").Scan(&value)
|
||||
if err == sql.ErrNoRows {
|
||||
// Default to enabled if setting doesn't exist
|
||||
return true, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to check registration setting: %w", err)
|
||||
}
|
||||
return value == "true", nil
|
||||
}
|
||||
|
||||
// SetRegistrationEnabled sets whether new user registration is enabled
|
||||
func (a *Auth) SetRegistrationEnabled(enabled bool) error {
|
||||
value := "false"
|
||||
if enabled {
|
||||
value = "true"
|
||||
}
|
||||
|
||||
// Check if setting exists
|
||||
var exists bool
|
||||
err := a.db.QueryRow("SELECT EXISTS(SELECT 1 FROM settings WHERE key = ?)", "registration_enabled").Scan(&exists)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if setting exists: %w", err)
|
||||
}
|
||||
|
||||
if exists {
|
||||
// Update existing setting
|
||||
_, err = a.db.Exec(
|
||||
"UPDATE settings SET value = ?, updated_at = CURRENT_TIMESTAMP WHERE key = ?",
|
||||
value, "registration_enabled",
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update setting: %w", err)
|
||||
}
|
||||
} else {
|
||||
// Insert new setting
|
||||
_, err = a.db.Exec(
|
||||
"INSERT INTO settings (key, value, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP)",
|
||||
"registration_enabled", value,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to insert setting: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getOrCreateUser gets or creates a user in the database
|
||||
// Automatically links accounts by email across different OAuth providers and local login
|
||||
func (a *Auth) getOrCreateUser(provider, oauthID, email, name string) (*Session, error) {
|
||||
var userID int64
|
||||
var dbEmail, dbName string
|
||||
var isAdmin bool
|
||||
var dbProvider, dbOAuthID string
|
||||
|
||||
// First, try to find by provider + oauth_id
|
||||
err := a.db.QueryRow(
|
||||
"SELECT id, email, name, is_admin FROM users WHERE oauth_provider = ? AND oauth_id = ?",
|
||||
"SELECT id, email, name, is_admin, oauth_provider, oauth_id FROM users WHERE oauth_provider = ? AND oauth_id = ?",
|
||||
provider, oauthID,
|
||||
).Scan(&userID, &dbEmail, &dbName, &isAdmin)
|
||||
).Scan(&userID, &dbEmail, &dbName, &isAdmin, &dbProvider, &dbOAuthID)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
// Check if this is the first user
|
||||
var userCount int
|
||||
a.db.QueryRow("SELECT COUNT(*) FROM users").Scan(&userCount)
|
||||
isAdmin = userCount == 0
|
||||
// Not found by provider+oauth_id, check by email for account linking
|
||||
err = a.db.QueryRow(
|
||||
"SELECT id, email, name, is_admin, oauth_provider, oauth_id FROM users WHERE email = ?",
|
||||
email,
|
||||
).Scan(&userID, &dbEmail, &dbName, &isAdmin, &dbProvider, &dbOAuthID)
|
||||
|
||||
// Create new user
|
||||
result, err := a.db.Exec(
|
||||
"INSERT INTO users (email, name, oauth_provider, oauth_id, is_admin) VALUES (?, ?, ?, ?, ?)",
|
||||
email, name, provider, oauthID, isAdmin,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create user: %w", err)
|
||||
if err == sql.ErrNoRows {
|
||||
// User doesn't exist, check if registration is enabled
|
||||
registrationEnabled, err := a.IsRegistrationEnabled()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check registration setting: %w", err)
|
||||
}
|
||||
if !registrationEnabled {
|
||||
return nil, fmt.Errorf("registration is disabled")
|
||||
}
|
||||
|
||||
// Check if this is the first user
|
||||
var userCount int
|
||||
a.db.QueryRow("SELECT COUNT(*) FROM users").Scan(&userCount)
|
||||
isAdmin = userCount == 0
|
||||
|
||||
// Create new user
|
||||
err = a.db.QueryRow(
|
||||
"INSERT INTO users (email, name, oauth_provider, oauth_id, is_admin) VALUES (?, ?, ?, ?, ?) RETURNING id",
|
||||
email, name, provider, oauthID, isAdmin,
|
||||
).Scan(&userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create user: %w", err)
|
||||
}
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("failed to query user by email: %w", err)
|
||||
} else {
|
||||
// User exists with same email but different provider - link accounts by updating provider info
|
||||
// This allows the user to log in with any provider that has the same email
|
||||
_, err = a.db.Exec(
|
||||
"UPDATE users SET oauth_provider = ?, oauth_id = ?, name = ? WHERE id = ?",
|
||||
provider, oauthID, name, userID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to link account: %w", err)
|
||||
}
|
||||
log.Printf("Linked account: user %d (email: %s) now accessible via %s provider", userID, email, provider)
|
||||
}
|
||||
userID, _ = result.LastInsertId()
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("failed to query user: %w", err)
|
||||
} else {
|
||||
// Update user info if changed
|
||||
// User found by provider+oauth_id, update info if changed
|
||||
if dbEmail != email || dbName != name {
|
||||
_, err = a.db.Exec(
|
||||
"UPDATE users SET email = ?, name = ? WHERE id = ?",
|
||||
@@ -238,13 +410,17 @@ func (a *Auth) Middleware(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
cookie, err := r.Cookie("session_id")
|
||||
if err != nil {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
json.NewEncoder(w).Encode(map[string]string{"error": "Unauthorized"})
|
||||
return
|
||||
}
|
||||
|
||||
session, ok := a.GetSession(cookie.Value)
|
||||
if !ok {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
json.NewEncoder(w).Encode(map[string]string{"error": "Unauthorized"})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -275,19 +451,25 @@ func (a *Auth) AdminMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
||||
// First check authentication
|
||||
cookie, err := r.Cookie("session_id")
|
||||
if err != nil {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
json.NewEncoder(w).Encode(map[string]string{"error": "Unauthorized"})
|
||||
return
|
||||
}
|
||||
|
||||
session, ok := a.GetSession(cookie.Value)
|
||||
if !ok {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
json.NewEncoder(w).Encode(map[string]string{"error": "Unauthorized"})
|
||||
return
|
||||
}
|
||||
|
||||
// Then check admin status
|
||||
if !session.IsAdmin {
|
||||
http.Error(w, "Forbidden: Admin access required", http.StatusForbidden)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
json.NewEncoder(w).Encode(map[string]string{"error": "Forbidden: Admin access required"})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -300,3 +482,221 @@ func (a *Auth) AdminMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// IsLocalLoginEnabled returns whether local login is enabled
|
||||
// Local login is enabled when ENABLE_LOCAL_AUTH environment variable is set to "true"
|
||||
func (a *Auth) IsLocalLoginEnabled() bool {
|
||||
return os.Getenv("ENABLE_LOCAL_AUTH") == "true"
|
||||
}
|
||||
|
||||
// IsGoogleOAuthConfigured returns whether Google OAuth is configured
|
||||
func (a *Auth) IsGoogleOAuthConfigured() bool {
|
||||
return a.googleConfig != nil
|
||||
}
|
||||
|
||||
// IsDiscordOAuthConfigured returns whether Discord OAuth is configured
|
||||
func (a *Auth) IsDiscordOAuthConfigured() bool {
|
||||
return a.discordConfig != nil
|
||||
}
|
||||
|
||||
// LocalLogin handles local username/password authentication
|
||||
func (a *Auth) LocalLogin(username, password string) (*Session, error) {
|
||||
// Find user by email (local users use email as username)
|
||||
email := username
|
||||
var userID int64
|
||||
var dbEmail, dbName, passwordHash string
|
||||
var isAdmin bool
|
||||
|
||||
err := a.db.QueryRow(
|
||||
"SELECT id, email, name, password_hash, is_admin FROM users WHERE email = ? AND oauth_provider = 'local'",
|
||||
email,
|
||||
).Scan(&userID, &dbEmail, &dbName, &passwordHash, &isAdmin)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, fmt.Errorf("invalid credentials")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query user: %w", err)
|
||||
}
|
||||
|
||||
// Verify password
|
||||
if passwordHash == "" {
|
||||
return nil, fmt.Errorf("invalid credentials")
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(password))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid credentials")
|
||||
}
|
||||
|
||||
// Create session
|
||||
session := &Session{
|
||||
UserID: userID,
|
||||
Email: dbEmail,
|
||||
Name: dbName,
|
||||
IsAdmin: isAdmin,
|
||||
ExpiresAt: time.Now().Add(24 * time.Hour),
|
||||
}
|
||||
|
||||
return session, nil
|
||||
}
|
||||
|
||||
// RegisterLocalUser creates a new local user account
|
||||
func (a *Auth) RegisterLocalUser(email, name, password string) (*Session, error) {
|
||||
// Check if registration is enabled
|
||||
registrationEnabled, err := a.IsRegistrationEnabled()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check registration setting: %w", err)
|
||||
}
|
||||
if !registrationEnabled {
|
||||
return nil, fmt.Errorf("registration is disabled")
|
||||
}
|
||||
|
||||
// Check if user already exists
|
||||
var exists bool
|
||||
err = a.db.QueryRow("SELECT EXISTS(SELECT 1 FROM users WHERE email = ?)", email).Scan(&exists)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check if user exists: %w", err)
|
||||
}
|
||||
if exists {
|
||||
return nil, fmt.Errorf("user with this email already exists")
|
||||
}
|
||||
|
||||
// Hash password
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to hash password: %w", err)
|
||||
}
|
||||
|
||||
// Check if this is the first user (make them admin)
|
||||
var userCount int
|
||||
a.db.QueryRow("SELECT COUNT(*) FROM users").Scan(&userCount)
|
||||
isAdmin := userCount == 0
|
||||
|
||||
// Create user
|
||||
var userID int64
|
||||
err = a.db.QueryRow(
|
||||
"INSERT INTO users (email, name, oauth_provider, oauth_id, password_hash, is_admin) VALUES (?, ?, 'local', ?, ?, ?) RETURNING id",
|
||||
email, name, email, string(hashedPassword), isAdmin,
|
||||
).Scan(&userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create user: %w", err)
|
||||
}
|
||||
|
||||
// Create session
|
||||
session := &Session{
|
||||
UserID: userID,
|
||||
Email: email,
|
||||
Name: name,
|
||||
IsAdmin: isAdmin,
|
||||
ExpiresAt: time.Now().Add(24 * time.Hour),
|
||||
}
|
||||
|
||||
return session, nil
|
||||
}
|
||||
|
||||
// ChangePassword allows a user to change their own password
|
||||
func (a *Auth) ChangePassword(userID int64, oldPassword, newPassword string) error {
|
||||
// Get current password hash
|
||||
var passwordHash string
|
||||
err := a.db.QueryRow("SELECT password_hash FROM users WHERE id = ? AND oauth_provider = 'local'", userID).Scan(&passwordHash)
|
||||
if err == sql.ErrNoRows {
|
||||
return fmt.Errorf("user not found or not a local user")
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to query user: %w", err)
|
||||
}
|
||||
|
||||
// Verify old password
|
||||
if passwordHash == "" {
|
||||
return fmt.Errorf("user has no password set")
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(oldPassword))
|
||||
if err != nil {
|
||||
return fmt.Errorf("incorrect old password")
|
||||
}
|
||||
|
||||
// Hash new password
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to hash password: %w", err)
|
||||
}
|
||||
|
||||
// Update password
|
||||
_, err = a.db.Exec("UPDATE users SET password_hash = ? WHERE id = ?", string(hashedPassword), userID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update password: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AdminChangePassword allows an admin to change any user's password without knowing the old password
|
||||
func (a *Auth) AdminChangePassword(targetUserID int64, newPassword string) error {
|
||||
// Verify user exists and is a local user
|
||||
var exists bool
|
||||
err := a.db.QueryRow("SELECT EXISTS(SELECT 1 FROM users WHERE id = ? AND oauth_provider = 'local')", targetUserID).Scan(&exists)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if user exists: %w", err)
|
||||
}
|
||||
if !exists {
|
||||
return fmt.Errorf("user not found or not a local user")
|
||||
}
|
||||
|
||||
// Hash new password
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to hash password: %w", err)
|
||||
}
|
||||
|
||||
// Update password
|
||||
_, err = a.db.Exec("UPDATE users SET password_hash = ? WHERE id = ?", string(hashedPassword), targetUserID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update password: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFirstUserID returns the ID of the first user (user with the lowest ID)
|
||||
func (a *Auth) GetFirstUserID() (int64, error) {
|
||||
var firstUserID int64
|
||||
err := a.db.QueryRow("SELECT id FROM users ORDER BY id ASC LIMIT 1").Scan(&firstUserID)
|
||||
if err == sql.ErrNoRows {
|
||||
return 0, fmt.Errorf("no users found")
|
||||
}
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to get first user ID: %w", err)
|
||||
}
|
||||
return firstUserID, nil
|
||||
}
|
||||
|
||||
// SetUserAdminStatus allows an admin to change a user's admin status
|
||||
func (a *Auth) SetUserAdminStatus(targetUserID int64, isAdmin bool) error {
|
||||
// Verify user exists
|
||||
var exists bool
|
||||
err := a.db.QueryRow("SELECT EXISTS(SELECT 1 FROM users WHERE id = ?)", targetUserID).Scan(&exists)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if user exists: %w", err)
|
||||
}
|
||||
if !exists {
|
||||
return fmt.Errorf("user not found")
|
||||
}
|
||||
|
||||
// Prevent removing admin status from the first user
|
||||
firstUserID, err := a.GetFirstUserID()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check first user: %w", err)
|
||||
}
|
||||
if targetUserID == firstUserID && !isAdmin {
|
||||
return fmt.Errorf("cannot remove admin status from the first user")
|
||||
}
|
||||
|
||||
// Update admin status
|
||||
_, err = a.db.Exec("UPDATE users SET is_admin = ? WHERE id = ?", isAdmin, targetUserID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update admin status: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,25 +8,36 @@ import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Secrets handles secret and token management
|
||||
type Secrets struct {
|
||||
db *sql.DB
|
||||
db *sql.DB
|
||||
fixedRegistrationToken string // Fixed token from environment variable (reusable, never expires)
|
||||
}
|
||||
|
||||
// NewSecrets creates a new secrets manager
|
||||
func NewSecrets(db *sql.DB) (*Secrets, error) {
|
||||
s := &Secrets{db: db}
|
||||
|
||||
|
||||
// Check for fixed registration token from environment
|
||||
fixedToken := os.Getenv("FIXED_REGISTRATION_TOKEN")
|
||||
if fixedToken != "" {
|
||||
s.fixedRegistrationToken = fixedToken
|
||||
log.Printf("Fixed registration token enabled (from FIXED_REGISTRATION_TOKEN env var)")
|
||||
log.Printf("WARNING: Fixed registration token is reusable and never expires - use only for testing/development!")
|
||||
}
|
||||
|
||||
// Ensure manager secret exists
|
||||
if err := s.ensureManagerSecret(); err != nil {
|
||||
return nil, fmt.Errorf("failed to ensure manager secret: %w", err)
|
||||
}
|
||||
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@@ -37,20 +48,20 @@ func (s *Secrets) ensureManagerSecret() error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check manager secrets: %w", err)
|
||||
}
|
||||
|
||||
|
||||
if count == 0 {
|
||||
// Generate new manager secret
|
||||
secret, err := generateSecret(32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate manager secret: %w", err)
|
||||
}
|
||||
|
||||
|
||||
_, err = s.db.Exec("INSERT INTO manager_secrets (secret) VALUES (?)", secret)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to store manager secret: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -70,9 +81,9 @@ func (s *Secrets) GenerateRegistrationToken(createdBy int64, expiresIn time.Dura
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate token: %w", err)
|
||||
}
|
||||
|
||||
|
||||
expiresAt := time.Now().Add(expiresIn)
|
||||
|
||||
|
||||
_, err = s.db.Exec(
|
||||
"INSERT INTO registration_tokens (token, expires_at, created_by) VALUES (?, ?, ?)",
|
||||
token, expiresAt, createdBy,
|
||||
@@ -80,43 +91,67 @@ func (s *Secrets) GenerateRegistrationToken(createdBy int64, expiresIn time.Dura
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to store registration token: %w", err)
|
||||
}
|
||||
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// TokenValidationResult represents the result of token validation
|
||||
type TokenValidationResult struct {
|
||||
Valid bool
|
||||
Reason string // "valid", "not_found", "already_used", "expired"
|
||||
Error error
|
||||
}
|
||||
|
||||
// ValidateRegistrationToken validates a registration token
|
||||
func (s *Secrets) ValidateRegistrationToken(token string) (bool, error) {
|
||||
result, err := s.ValidateRegistrationTokenDetailed(token)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// For backward compatibility, return just the valid boolean
|
||||
return result.Valid, nil
|
||||
}
|
||||
|
||||
// ValidateRegistrationTokenDetailed validates a registration token and returns detailed result
|
||||
func (s *Secrets) ValidateRegistrationTokenDetailed(token string) (*TokenValidationResult, error) {
|
||||
// Check fixed token first (if set) - it's reusable and never expires
|
||||
if s.fixedRegistrationToken != "" && token == s.fixedRegistrationToken {
|
||||
log.Printf("Fixed registration token used (from FIXED_REGISTRATION_TOKEN env var)")
|
||||
return &TokenValidationResult{Valid: true, Reason: "valid"}, nil
|
||||
}
|
||||
|
||||
// Check database tokens
|
||||
var used bool
|
||||
var expiresAt time.Time
|
||||
var id int64
|
||||
|
||||
|
||||
err := s.db.QueryRow(
|
||||
"SELECT id, expires_at, used FROM registration_tokens WHERE token = ?",
|
||||
token,
|
||||
).Scan(&id, &expiresAt, &used)
|
||||
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
return false, nil
|
||||
return &TokenValidationResult{Valid: false, Reason: "not_found"}, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to query token: %w", err)
|
||||
return nil, fmt.Errorf("failed to query token: %w", err)
|
||||
}
|
||||
|
||||
|
||||
if used {
|
||||
return false, nil
|
||||
return &TokenValidationResult{Valid: false, Reason: "already_used"}, nil
|
||||
}
|
||||
|
||||
|
||||
if time.Now().After(expiresAt) {
|
||||
return false, nil
|
||||
return &TokenValidationResult{Valid: false, Reason: "expired"}, nil
|
||||
}
|
||||
|
||||
|
||||
// Mark token as used
|
||||
_, err = s.db.Exec("UPDATE registration_tokens SET used = 1 WHERE id = ?", id)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to mark token as used: %w", err)
|
||||
return nil, fmt.Errorf("failed to mark token as used: %w", err)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
|
||||
return &TokenValidationResult{Valid: true, Reason: "valid"}, nil
|
||||
}
|
||||
|
||||
// ListRegistrationTokens lists all registration tokens
|
||||
@@ -130,19 +165,19 @@ func (s *Secrets) ListRegistrationTokens() ([]map[string]interface{}, error) {
|
||||
return nil, fmt.Errorf("failed to query tokens: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
|
||||
var tokens []map[string]interface{}
|
||||
for rows.Next() {
|
||||
var id, createdBy sql.NullInt64
|
||||
var token string
|
||||
var expiresAt, createdAt time.Time
|
||||
var used bool
|
||||
|
||||
|
||||
err := rows.Scan(&id, &token, &expiresAt, &used, &createdAt, &createdBy)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
tokens = append(tokens, map[string]interface{}{
|
||||
"id": id.Int64,
|
||||
"token": token,
|
||||
@@ -152,7 +187,7 @@ func (s *Secrets) ListRegistrationTokens() ([]map[string]interface{}, error) {
|
||||
"created_by": createdBy.Int64,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
return tokens, nil
|
||||
}
|
||||
|
||||
@@ -181,28 +216,29 @@ func VerifyRequest(r *http.Request, secret string, maxAge time.Duration) (bool,
|
||||
if signature == "" {
|
||||
return false, fmt.Errorf("missing signature")
|
||||
}
|
||||
|
||||
|
||||
timestampStr := r.Header.Get("X-Runner-Timestamp")
|
||||
if timestampStr == "" {
|
||||
return false, fmt.Errorf("missing timestamp")
|
||||
}
|
||||
|
||||
var timestamp time.Time
|
||||
_, err := fmt.Sscanf(timestampStr, "%d", ×tamp)
|
||||
|
||||
var timestampUnix int64
|
||||
_, err := fmt.Sscanf(timestampStr, "%d", ×tampUnix)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid timestamp: %w", err)
|
||||
}
|
||||
|
||||
timestamp := time.Unix(timestampUnix, 0)
|
||||
|
||||
// Check timestamp is not too old
|
||||
if time.Since(timestamp) > maxAge {
|
||||
return false, fmt.Errorf("request too old")
|
||||
}
|
||||
|
||||
|
||||
// Check timestamp is not in the future (allow 1 minute clock skew)
|
||||
if timestamp.After(time.Now().Add(1 * time.Minute)) {
|
||||
return false, fmt.Errorf("timestamp in future")
|
||||
}
|
||||
|
||||
|
||||
// Read body
|
||||
bodyBytes, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
@@ -210,10 +246,13 @@ func VerifyRequest(r *http.Request, secret string, maxAge time.Duration) (bool,
|
||||
}
|
||||
// Restore body for handler
|
||||
r.Body = io.NopCloser(strings.NewReader(string(bodyBytes)))
|
||||
|
||||
// Verify signature
|
||||
expectedSig := SignRequest(r.Method, r.URL.Path, string(bodyBytes), secret, timestamp)
|
||||
|
||||
|
||||
// Verify signature - use path without query parameters (query params are not part of signature)
|
||||
// The runner signs with the path including query params, but we verify with just the path
|
||||
// This is intentional - query params are for identification, not part of the signature
|
||||
path := r.URL.Path
|
||||
expectedSig := SignRequest(r.Method, path, string(bodyBytes), secret, timestamp)
|
||||
|
||||
return hmac.Equal([]byte(signature), []byte(expectedSig)), nil
|
||||
}
|
||||
|
||||
@@ -241,4 +280,3 @@ func generateSecret(length int) (string, error) {
|
||||
}
|
||||
return hex.EncodeToString(bytes), nil
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,9 @@ package database
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
_ "github.com/marcboeker/go-duckdb/v2"
|
||||
)
|
||||
|
||||
// DB wraps the database connection
|
||||
@@ -14,7 +15,7 @@ type DB struct {
|
||||
|
||||
// NewDB creates a new database connection
|
||||
func NewDB(dbPath string) (*DB, error) {
|
||||
db, err := sql.Open("sqlite3", dbPath+"?_foreign_keys=1")
|
||||
db, err := sql.Open("duckdb", dbPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open database: %w", err)
|
||||
}
|
||||
@@ -33,114 +34,133 @@ func NewDB(dbPath string) (*DB, error) {
|
||||
|
||||
// migrate runs database migrations
|
||||
func (db *DB) migrate() error {
|
||||
// Create sequences for auto-incrementing primary keys
|
||||
sequences := []string{
|
||||
`CREATE SEQUENCE IF NOT EXISTS seq_users_id START 1`,
|
||||
`CREATE SEQUENCE IF NOT EXISTS seq_jobs_id START 1`,
|
||||
`CREATE SEQUENCE IF NOT EXISTS seq_runners_id START 1`,
|
||||
`CREATE SEQUENCE IF NOT EXISTS seq_tasks_id START 1`,
|
||||
`CREATE SEQUENCE IF NOT EXISTS seq_job_files_id START 1`,
|
||||
`CREATE SEQUENCE IF NOT EXISTS seq_manager_secrets_id START 1`,
|
||||
`CREATE SEQUENCE IF NOT EXISTS seq_registration_tokens_id START 1`,
|
||||
`CREATE SEQUENCE IF NOT EXISTS seq_task_logs_id START 1`,
|
||||
`CREATE SEQUENCE IF NOT EXISTS seq_task_steps_id START 1`,
|
||||
}
|
||||
|
||||
for _, seq := range sequences {
|
||||
if _, err := db.Exec(seq); err != nil {
|
||||
return fmt.Errorf("failed to create sequence: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
schema := `
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
id BIGINT PRIMARY KEY DEFAULT nextval('seq_users_id'),
|
||||
email TEXT UNIQUE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
oauth_provider TEXT NOT NULL,
|
||||
oauth_id TEXT NOT NULL,
|
||||
is_admin BOOLEAN NOT NULL DEFAULT 0,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
password_hash TEXT,
|
||||
is_admin BOOLEAN NOT NULL DEFAULT false,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(oauth_provider, oauth_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS jobs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
id BIGINT PRIMARY KEY DEFAULT nextval('seq_jobs_id'),
|
||||
user_id BIGINT NOT NULL,
|
||||
job_type TEXT NOT NULL DEFAULT 'render',
|
||||
name TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
progress REAL NOT NULL DEFAULT 0.0,
|
||||
frame_start INTEGER NOT NULL,
|
||||
frame_end INTEGER NOT NULL,
|
||||
output_format TEXT NOT NULL DEFAULT 'PNG',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
started_at DATETIME,
|
||||
completed_at DATETIME,
|
||||
frame_start INTEGER,
|
||||
frame_end INTEGER,
|
||||
output_format TEXT,
|
||||
allow_parallel_runners BOOLEAN,
|
||||
timeout_seconds INTEGER DEFAULT 86400,
|
||||
blend_metadata TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
started_at TIMESTAMP,
|
||||
completed_at TIMESTAMP,
|
||||
error_message TEXT,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS runners (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
id BIGINT PRIMARY KEY DEFAULT nextval('seq_runners_id'),
|
||||
name TEXT NOT NULL,
|
||||
hostname TEXT NOT NULL,
|
||||
ip_address TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'offline',
|
||||
last_heartbeat DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
last_heartbeat TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
capabilities TEXT,
|
||||
registration_token TEXT,
|
||||
runner_secret TEXT,
|
||||
manager_secret TEXT,
|
||||
verified BOOLEAN NOT NULL DEFAULT 0,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
verified BOOLEAN NOT NULL DEFAULT false,
|
||||
priority INTEGER NOT NULL DEFAULT 100,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tasks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
job_id INTEGER NOT NULL,
|
||||
runner_id INTEGER,
|
||||
id BIGINT PRIMARY KEY DEFAULT nextval('seq_tasks_id'),
|
||||
job_id BIGINT NOT NULL,
|
||||
runner_id BIGINT,
|
||||
frame_start INTEGER NOT NULL,
|
||||
frame_end INTEGER NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
output_path TEXT,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
started_at DATETIME,
|
||||
completed_at DATETIME,
|
||||
error_message TEXT,
|
||||
FOREIGN KEY (job_id) REFERENCES jobs(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (runner_id) REFERENCES runners(id) ON DELETE SET NULL
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
started_at TIMESTAMP,
|
||||
completed_at TIMESTAMP,
|
||||
error_message TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS job_files (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
job_id INTEGER NOT NULL,
|
||||
id BIGINT PRIMARY KEY DEFAULT nextval('seq_job_files_id'),
|
||||
job_id BIGINT NOT NULL,
|
||||
file_type TEXT NOT NULL,
|
||||
file_path TEXT NOT NULL,
|
||||
file_name TEXT NOT NULL,
|
||||
file_size INTEGER NOT NULL,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (job_id) REFERENCES jobs(id) ON DELETE CASCADE
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS manager_secrets (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
id BIGINT PRIMARY KEY DEFAULT nextval('seq_manager_secrets_id'),
|
||||
secret TEXT UNIQUE NOT NULL,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS registration_tokens (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
id BIGINT PRIMARY KEY DEFAULT nextval('seq_registration_tokens_id'),
|
||||
token TEXT UNIQUE NOT NULL,
|
||||
expires_at DATETIME NOT NULL,
|
||||
used BOOLEAN NOT NULL DEFAULT 0,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER,
|
||||
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
used BOOLEAN NOT NULL DEFAULT false,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by BIGINT,
|
||||
FOREIGN KEY (created_by) REFERENCES users(id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS task_logs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
task_id INTEGER NOT NULL,
|
||||
runner_id INTEGER,
|
||||
id BIGINT PRIMARY KEY DEFAULT nextval('seq_task_logs_id'),
|
||||
task_id BIGINT NOT NULL,
|
||||
runner_id BIGINT,
|
||||
log_level TEXT NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
step_name TEXT,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (runner_id) REFERENCES runners(id) ON DELETE SET NULL
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS task_steps (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
task_id INTEGER NOT NULL,
|
||||
id BIGINT PRIMARY KEY DEFAULT nextval('seq_task_steps_id'),
|
||||
task_id BIGINT NOT NULL,
|
||||
step_name TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
started_at DATETIME,
|
||||
completed_at DATETIME,
|
||||
started_at TIMESTAMP,
|
||||
completed_at TIMESTAMP,
|
||||
duration_ms INTEGER,
|
||||
error_message TEXT,
|
||||
FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE
|
||||
error_message TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_jobs_user_id ON jobs(user_id);
|
||||
@@ -156,6 +176,12 @@ func (db *DB) migrate() error {
|
||||
CREATE INDEX IF NOT EXISTS idx_task_logs_runner_id ON task_logs(runner_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_task_steps_task_id ON task_steps(task_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_runners_last_heartbeat ON runners(last_heartbeat);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
`
|
||||
|
||||
if _, err := db.Exec(schema); err != nil {
|
||||
@@ -165,30 +191,57 @@ func (db *DB) migrate() error {
|
||||
// Migrate existing tables to add new columns
|
||||
migrations := []string{
|
||||
// Add is_admin to users if it doesn't exist
|
||||
`ALTER TABLE users ADD COLUMN is_admin BOOLEAN NOT NULL DEFAULT 0`,
|
||||
`ALTER TABLE users ADD COLUMN IF NOT EXISTS is_admin BOOLEAN NOT NULL DEFAULT false`,
|
||||
// Add new columns to runners if they don't exist
|
||||
`ALTER TABLE runners ADD COLUMN registration_token TEXT`,
|
||||
`ALTER TABLE runners ADD COLUMN runner_secret TEXT`,
|
||||
`ALTER TABLE runners ADD COLUMN manager_secret TEXT`,
|
||||
`ALTER TABLE runners ADD COLUMN verified BOOLEAN NOT NULL DEFAULT 0`,
|
||||
`ALTER TABLE runners ADD COLUMN IF NOT EXISTS registration_token TEXT`,
|
||||
`ALTER TABLE runners ADD COLUMN IF NOT EXISTS runner_secret TEXT`,
|
||||
`ALTER TABLE runners ADD COLUMN IF NOT EXISTS manager_secret TEXT`,
|
||||
`ALTER TABLE runners ADD COLUMN IF NOT EXISTS verified BOOLEAN NOT NULL DEFAULT false`,
|
||||
`ALTER TABLE runners ADD COLUMN IF NOT EXISTS priority INTEGER NOT NULL DEFAULT 100`,
|
||||
// Add allow_parallel_runners to jobs if it doesn't exist
|
||||
`ALTER TABLE jobs ADD COLUMN allow_parallel_runners BOOLEAN NOT NULL DEFAULT 1`,
|
||||
`ALTER TABLE jobs ADD COLUMN IF NOT EXISTS allow_parallel_runners BOOLEAN NOT NULL DEFAULT true`,
|
||||
// Add timeout_seconds to jobs if it doesn't exist
|
||||
`ALTER TABLE jobs ADD COLUMN timeout_seconds INTEGER DEFAULT 86400`,
|
||||
`ALTER TABLE jobs ADD COLUMN IF NOT EXISTS timeout_seconds INTEGER DEFAULT 86400`,
|
||||
// Add blend_metadata to jobs if it doesn't exist
|
||||
`ALTER TABLE jobs ADD COLUMN blend_metadata TEXT`,
|
||||
`ALTER TABLE jobs ADD COLUMN IF NOT EXISTS blend_metadata TEXT`,
|
||||
// Add job_type to jobs if it doesn't exist
|
||||
`ALTER TABLE jobs ADD COLUMN IF NOT EXISTS job_type TEXT DEFAULT 'render'`,
|
||||
// Add task_type to tasks if it doesn't exist
|
||||
`ALTER TABLE tasks ADD COLUMN task_type TEXT DEFAULT 'render'`,
|
||||
`ALTER TABLE tasks ADD COLUMN IF NOT EXISTS task_type TEXT DEFAULT 'render'`,
|
||||
// Add new columns to tasks if they don't exist
|
||||
`ALTER TABLE tasks ADD COLUMN current_step TEXT`,
|
||||
`ALTER TABLE tasks ADD COLUMN retry_count INTEGER DEFAULT 0`,
|
||||
`ALTER TABLE tasks ADD COLUMN max_retries INTEGER DEFAULT 3`,
|
||||
`ALTER TABLE tasks ADD COLUMN timeout_seconds INTEGER`,
|
||||
`ALTER TABLE tasks ADD COLUMN IF NOT EXISTS current_step TEXT`,
|
||||
`ALTER TABLE tasks ADD COLUMN IF NOT EXISTS retry_count INTEGER DEFAULT 0`,
|
||||
`ALTER TABLE tasks ADD COLUMN IF NOT EXISTS max_retries INTEGER DEFAULT 3`,
|
||||
`ALTER TABLE tasks ADD COLUMN IF NOT EXISTS timeout_seconds INTEGER`,
|
||||
}
|
||||
|
||||
for _, migration := range migrations {
|
||||
// SQLite doesn't support IF NOT EXISTS for ALTER TABLE, so we ignore errors
|
||||
db.Exec(migration)
|
||||
// DuckDB supports IF NOT EXISTS for ALTER TABLE, so we can safely execute
|
||||
if _, err := db.Exec(migration); err != nil {
|
||||
// Log but don't fail - column might already exist or table might not exist yet
|
||||
// This is fine for migrations that run after schema creation
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize registration_enabled setting (default: true) if it doesn't exist
|
||||
var settingCount int
|
||||
err := db.QueryRow("SELECT COUNT(*) FROM settings WHERE key = ?", "registration_enabled").Scan(&settingCount)
|
||||
if err == nil && settingCount == 0 {
|
||||
_, err = db.Exec("INSERT INTO settings (key, value) VALUES (?, ?)", "registration_enabled", "true")
|
||||
if err != nil {
|
||||
// Log but don't fail - setting might have been created by another process
|
||||
log.Printf("Note: Could not initialize registration_enabled setting: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
for _, migration := range migrations {
|
||||
// DuckDB supports IF NOT EXISTS for ALTER TABLE, so we can safely execute
|
||||
if _, err := db.Exec(migration); err != nil {
|
||||
// Log but don't fail - column might already exist or table might not exist yet
|
||||
// This is fine for migrations that run after schema creation
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -198,4 +251,3 @@ func (db *DB) migrate() error {
|
||||
func (db *DB) Close() error {
|
||||
return db.DB.Close()
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,12 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Storage handles file storage operations
|
||||
@@ -135,3 +137,60 @@ func (s *Storage) GetFileSize(filePath string) (int64, error) {
|
||||
return info.Size(), nil
|
||||
}
|
||||
|
||||
// ExtractZip extracts a ZIP file to the destination directory
|
||||
// Returns a list of all extracted file paths
|
||||
func (s *Storage) ExtractZip(zipPath, destDir string) ([]string, error) {
|
||||
r, err := zip.OpenReader(zipPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open ZIP file: %w", err)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
var extractedFiles []string
|
||||
|
||||
for _, f := range r.File {
|
||||
// Sanitize file path to prevent directory traversal
|
||||
destPath := filepath.Join(destDir, f.Name)
|
||||
if !strings.HasPrefix(destPath, filepath.Clean(destDir)+string(os.PathSeparator)) {
|
||||
return nil, fmt.Errorf("invalid file path in ZIP: %s", f.Name)
|
||||
}
|
||||
|
||||
// Create directory structure
|
||||
if f.FileInfo().IsDir() {
|
||||
if err := os.MkdirAll(destPath, 0755); err != nil {
|
||||
return nil, fmt.Errorf("failed to create directory: %w", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Create parent directories
|
||||
if err := os.MkdirAll(filepath.Dir(destPath), 0755); err != nil {
|
||||
return nil, fmt.Errorf("failed to create parent directory: %w", err)
|
||||
}
|
||||
|
||||
// Extract file
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open file in ZIP: %w", err)
|
||||
}
|
||||
|
||||
outFile, err := os.Create(destPath)
|
||||
if err != nil {
|
||||
rc.Close()
|
||||
return nil, fmt.Errorf("failed to create file: %w", err)
|
||||
}
|
||||
|
||||
_, err = io.Copy(outFile, rc)
|
||||
outFile.Close()
|
||||
rc.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract file: %w", err)
|
||||
}
|
||||
|
||||
extractedFiles = append(extractedFiles, destPath)
|
||||
}
|
||||
|
||||
return extractedFiles, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,12 @@ import "time"
|
||||
|
||||
// User represents a user in the system
|
||||
type User struct {
|
||||
ID int64 `json:"id"`
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
OAuthProvider string `json:"oauth_provider"` // "google" or "discord"
|
||||
OAuthID string `json:"oauth_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
ID int64 `json:"id"`
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
OAuthProvider string `json:"oauth_provider"` // "google" or "discord"
|
||||
OAuthID string `json:"oauth_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
// JobStatus represents the status of a job
|
||||
@@ -23,23 +23,32 @@ const (
|
||||
JobStatusCancelled JobStatus = "cancelled"
|
||||
)
|
||||
|
||||
// Job represents a render job
|
||||
// JobType represents the type of a job
|
||||
type JobType string
|
||||
|
||||
const (
|
||||
JobTypeMetadata JobType = "metadata" // Metadata extraction job - only needs blend file
|
||||
JobTypeRender JobType = "render" // Render job - needs frame range, format, etc.
|
||||
)
|
||||
|
||||
// Job represents a job (metadata extraction or render)
|
||||
type Job struct {
|
||||
ID int64 `json:"id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
Name string `json:"name"`
|
||||
Status JobStatus `json:"status"`
|
||||
Progress float64 `json:"progress"` // 0.0 to 100.0
|
||||
FrameStart int `json:"frame_start"`
|
||||
FrameEnd int `json:"frame_end"`
|
||||
OutputFormat string `json:"output_format"` // PNG, JPEG, EXR, etc.
|
||||
AllowParallelRunners bool `json:"allow_parallel_runners"` // Allow multiple runners to work on this job
|
||||
TimeoutSeconds int `json:"timeout_seconds"` // Job-level timeout (24 hours default)
|
||||
BlendMetadata *BlendMetadata `json:"blend_metadata,omitempty"` // Extracted metadata from blend file
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
StartedAt *time.Time `json:"started_at,omitempty"`
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
ID int64 `json:"id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
JobType JobType `json:"job_type"` // "metadata" or "render"
|
||||
Name string `json:"name"`
|
||||
Status JobStatus `json:"status"`
|
||||
Progress float64 `json:"progress"` // 0.0 to 100.0
|
||||
FrameStart *int `json:"frame_start,omitempty"` // Only for render jobs
|
||||
FrameEnd *int `json:"frame_end,omitempty"` // Only for render jobs
|
||||
OutputFormat *string `json:"output_format,omitempty"` // Only for render jobs - PNG, JPEG, EXR, etc.
|
||||
AllowParallelRunners *bool `json:"allow_parallel_runners,omitempty"` // Only for render jobs
|
||||
TimeoutSeconds int `json:"timeout_seconds"` // Job-level timeout (24 hours default)
|
||||
BlendMetadata *BlendMetadata `json:"blend_metadata,omitempty"` // Extracted metadata from blend file
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
StartedAt *time.Time `json:"started_at,omitempty"`
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
}
|
||||
|
||||
// RunnerStatus represents the status of a runner
|
||||
@@ -53,14 +62,15 @@ const (
|
||||
|
||||
// Runner represents a render runner
|
||||
type Runner struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Hostname string `json:"hostname"`
|
||||
IPAddress string `json:"ip_address"`
|
||||
Status RunnerStatus `json:"status"`
|
||||
LastHeartbeat time.Time `json:"last_heartbeat"`
|
||||
Capabilities string `json:"capabilities"` // JSON string of capabilities
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Hostname string `json:"hostname"`
|
||||
IPAddress string `json:"ip_address"`
|
||||
Status RunnerStatus `json:"status"`
|
||||
LastHeartbeat time.Time `json:"last_heartbeat"`
|
||||
Capabilities string `json:"capabilities"` // JSON string of capabilities
|
||||
Priority int `json:"priority"` // Higher number = higher priority (default: 100)
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
// TaskStatus represents the status of a task
|
||||
@@ -77,29 +87,29 @@ const (
|
||||
type TaskType string
|
||||
|
||||
const (
|
||||
TaskTypeRender TaskType = "render"
|
||||
TaskTypeMetadata TaskType = "metadata"
|
||||
TaskTypeRender TaskType = "render"
|
||||
TaskTypeMetadata TaskType = "metadata"
|
||||
TaskTypeVideoGeneration TaskType = "video_generation"
|
||||
)
|
||||
|
||||
// Task represents a render task assigned to a runner
|
||||
type Task struct {
|
||||
ID int64 `json:"id"`
|
||||
JobID int64 `json:"job_id"`
|
||||
RunnerID *int64 `json:"runner_id,omitempty"`
|
||||
FrameStart int `json:"frame_start"`
|
||||
FrameEnd int `json:"frame_end"`
|
||||
TaskType TaskType `json:"task_type"`
|
||||
Status TaskStatus `json:"status"`
|
||||
CurrentStep string `json:"current_step,omitempty"`
|
||||
RetryCount int `json:"retry_count"`
|
||||
MaxRetries int `json:"max_retries"`
|
||||
TimeoutSeconds *int `json:"timeout_seconds,omitempty"` // Task timeout (5 min for frames, 24h for FFmpeg)
|
||||
OutputPath string `json:"output_path,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
StartedAt *time.Time `json:"started_at,omitempty"`
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
ID int64 `json:"id"`
|
||||
JobID int64 `json:"job_id"`
|
||||
RunnerID *int64 `json:"runner_id,omitempty"`
|
||||
FrameStart int `json:"frame_start"`
|
||||
FrameEnd int `json:"frame_end"`
|
||||
TaskType TaskType `json:"task_type"`
|
||||
Status TaskStatus `json:"status"`
|
||||
CurrentStep string `json:"current_step,omitempty"`
|
||||
RetryCount int `json:"retry_count"`
|
||||
MaxRetries int `json:"max_retries"`
|
||||
TimeoutSeconds *int `json:"timeout_seconds,omitempty"` // Task timeout (5 min for frames, 24h for FFmpeg)
|
||||
OutputPath string `json:"output_path,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
StartedAt *time.Time `json:"started_at,omitempty"`
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
}
|
||||
|
||||
// JobFileType represents the type of file
|
||||
@@ -112,22 +122,24 @@ const (
|
||||
|
||||
// JobFile represents a file associated with a job
|
||||
type JobFile struct {
|
||||
ID int64 `json:"id"`
|
||||
JobID int64 `json:"job_id"`
|
||||
ID int64 `json:"id"`
|
||||
JobID int64 `json:"job_id"`
|
||||
FileType JobFileType `json:"file_type"`
|
||||
FilePath string `json:"file_path"`
|
||||
FileName string `json:"file_name"`
|
||||
FileSize int64 `json:"file_size"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
FilePath string `json:"file_path"`
|
||||
FileName string `json:"file_name"`
|
||||
FileSize int64 `json:"file_size"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
// CreateJobRequest represents a request to create a new job
|
||||
type CreateJobRequest struct {
|
||||
Name string `json:"name"`
|
||||
FrameStart int `json:"frame_start"`
|
||||
FrameEnd int `json:"frame_end"`
|
||||
OutputFormat string `json:"output_format"`
|
||||
AllowParallelRunners *bool `json:"allow_parallel_runners,omitempty"` // Optional, defaults to true
|
||||
JobType JobType `json:"job_type"` // "metadata" or "render"
|
||||
Name string `json:"name"`
|
||||
FrameStart *int `json:"frame_start,omitempty"` // Required for render jobs
|
||||
FrameEnd *int `json:"frame_end,omitempty"` // Required for render jobs
|
||||
OutputFormat *string `json:"output_format,omitempty"` // Required for render jobs
|
||||
AllowParallelRunners *bool `json:"allow_parallel_runners,omitempty"` // Optional for render jobs, defaults to true
|
||||
MetadataJobID *int64 `json:"metadata_job_id,omitempty"` // Optional: ID of metadata job to copy input files from
|
||||
}
|
||||
|
||||
// UpdateJobProgressRequest represents a request to update job progress
|
||||
@@ -141,6 +153,7 @@ type RegisterRunnerRequest struct {
|
||||
Hostname string `json:"hostname"`
|
||||
IPAddress string `json:"ip_address"`
|
||||
Capabilities string `json:"capabilities"`
|
||||
Priority *int `json:"priority,omitempty"` // Optional, defaults to 100 if not provided
|
||||
}
|
||||
|
||||
// LogLevel represents the level of a log entry
|
||||
@@ -172,7 +185,7 @@ const (
|
||||
StepStatusRunning StepStatus = "running"
|
||||
StepStatusCompleted StepStatus = "completed"
|
||||
StepStatusFailed StepStatus = "failed"
|
||||
StepStatusSkipped StepStatus = "skipped"
|
||||
StepStatusSkipped StepStatus = "skipped"
|
||||
)
|
||||
|
||||
// TaskStep represents an execution step within a task
|
||||
@@ -212,19 +225,19 @@ type TaskLogEntry struct {
|
||||
|
||||
// BlendMetadata represents extracted metadata from a blend file
|
||||
type BlendMetadata struct {
|
||||
FrameStart int `json:"frame_start"`
|
||||
FrameEnd int `json:"frame_end"`
|
||||
RenderSettings RenderSettings `json:"render_settings"`
|
||||
SceneInfo SceneInfo `json:"scene_info"`
|
||||
FrameStart int `json:"frame_start"`
|
||||
FrameEnd int `json:"frame_end"`
|
||||
RenderSettings RenderSettings `json:"render_settings"`
|
||||
SceneInfo SceneInfo `json:"scene_info"`
|
||||
}
|
||||
|
||||
// RenderSettings represents render settings from a blend file
|
||||
type RenderSettings struct {
|
||||
ResolutionX int `json:"resolution_x"`
|
||||
ResolutionY int `json:"resolution_y"`
|
||||
Samples int `json:"samples"`
|
||||
OutputFormat string `json:"output_format"`
|
||||
Engine string `json:"engine"`
|
||||
ResolutionX int `json:"resolution_x"`
|
||||
ResolutionY int `json:"resolution_y"`
|
||||
Samples int `json:"samples"`
|
||||
OutputFormat string `json:"output_format"`
|
||||
Engine string `json:"engine"`
|
||||
}
|
||||
|
||||
// SceneInfo represents scene information from a blend file
|
||||
@@ -233,4 +246,3 @@ type SceneInfo struct {
|
||||
ObjectCount int `json:"object_count"`
|
||||
MaterialCount int `json:"material_count"`
|
||||
}
|
||||
|
||||
|
||||
8
web/dist/assets/index-DJO8pTHt.js
vendored
8
web/dist/assets/index-DJO8pTHt.js
vendored
File diff suppressed because one or more lines are too long
1
web/dist/assets/index-rerlyxof.css
vendored
1
web/dist/assets/index-rerlyxof.css
vendored
File diff suppressed because one or more lines are too long
14
web/dist/index.html
vendored
14
web/dist/index.html
vendored
@@ -1,14 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Fuego Render Farm</title>
|
||||
<script type="module" crossorigin src="/assets/index-DJO8pTHt.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-rerlyxof.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Fuego Render Farm</title>
|
||||
<title>JiggaBlend</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
1
web/node_modules/.bin/autoprefixer
generated
vendored
1
web/node_modules/.bin/autoprefixer
generated
vendored
@@ -1 +0,0 @@
|
||||
../autoprefixer/bin/autoprefixer
|
||||
1
web/node_modules/.bin/baseline-browser-mapping
generated
vendored
1
web/node_modules/.bin/baseline-browser-mapping
generated
vendored
@@ -1 +0,0 @@
|
||||
../baseline-browser-mapping/dist/cli.js
|
||||
1
web/node_modules/.bin/browserslist
generated
vendored
1
web/node_modules/.bin/browserslist
generated
vendored
@@ -1 +0,0 @@
|
||||
../browserslist/cli.js
|
||||
1
web/node_modules/.bin/cssesc
generated
vendored
1
web/node_modules/.bin/cssesc
generated
vendored
@@ -1 +0,0 @@
|
||||
../cssesc/bin/cssesc
|
||||
1
web/node_modules/.bin/esbuild
generated
vendored
1
web/node_modules/.bin/esbuild
generated
vendored
@@ -1 +0,0 @@
|
||||
../esbuild/bin/esbuild
|
||||
1
web/node_modules/.bin/jiti
generated
vendored
1
web/node_modules/.bin/jiti
generated
vendored
@@ -1 +0,0 @@
|
||||
../jiti/bin/jiti.js
|
||||
1
web/node_modules/.bin/jsesc
generated
vendored
1
web/node_modules/.bin/jsesc
generated
vendored
@@ -1 +0,0 @@
|
||||
../jsesc/bin/jsesc
|
||||
1
web/node_modules/.bin/json5
generated
vendored
1
web/node_modules/.bin/json5
generated
vendored
@@ -1 +0,0 @@
|
||||
../json5/lib/cli.js
|
||||
1
web/node_modules/.bin/loose-envify
generated
vendored
1
web/node_modules/.bin/loose-envify
generated
vendored
@@ -1 +0,0 @@
|
||||
../loose-envify/cli.js
|
||||
1
web/node_modules/.bin/nanoid
generated
vendored
1
web/node_modules/.bin/nanoid
generated
vendored
@@ -1 +0,0 @@
|
||||
../nanoid/bin/nanoid.cjs
|
||||
1
web/node_modules/.bin/parser
generated
vendored
1
web/node_modules/.bin/parser
generated
vendored
@@ -1 +0,0 @@
|
||||
../@babel/parser/bin/babel-parser.js
|
||||
1
web/node_modules/.bin/resolve
generated
vendored
1
web/node_modules/.bin/resolve
generated
vendored
@@ -1 +0,0 @@
|
||||
../resolve/bin/resolve
|
||||
1
web/node_modules/.bin/rollup
generated
vendored
1
web/node_modules/.bin/rollup
generated
vendored
@@ -1 +0,0 @@
|
||||
../rollup/dist/bin/rollup
|
||||
1
web/node_modules/.bin/semver
generated
vendored
1
web/node_modules/.bin/semver
generated
vendored
@@ -1 +0,0 @@
|
||||
../semver/bin/semver.js
|
||||
1
web/node_modules/.bin/sucrase
generated
vendored
1
web/node_modules/.bin/sucrase
generated
vendored
@@ -1 +0,0 @@
|
||||
../sucrase/bin/sucrase
|
||||
1
web/node_modules/.bin/sucrase-node
generated
vendored
1
web/node_modules/.bin/sucrase-node
generated
vendored
@@ -1 +0,0 @@
|
||||
../sucrase/bin/sucrase-node
|
||||
1
web/node_modules/.bin/tailwind
generated
vendored
1
web/node_modules/.bin/tailwind
generated
vendored
@@ -1 +0,0 @@
|
||||
../tailwindcss/lib/cli.js
|
||||
1
web/node_modules/.bin/tailwindcss
generated
vendored
1
web/node_modules/.bin/tailwindcss
generated
vendored
@@ -1 +0,0 @@
|
||||
../tailwindcss/lib/cli.js
|
||||
1
web/node_modules/.bin/update-browserslist-db
generated
vendored
1
web/node_modules/.bin/update-browserslist-db
generated
vendored
@@ -1 +0,0 @@
|
||||
../update-browserslist-db/cli.js
|
||||
1
web/node_modules/.bin/vite
generated
vendored
1
web/node_modules/.bin/vite
generated
vendored
@@ -1 +0,0 @@
|
||||
../vite/bin/vite.js
|
||||
1942
web/node_modules/.package-lock.json
generated
vendored
1942
web/node_modules/.package-lock.json
generated
vendored
File diff suppressed because it is too large
Load Diff
128
web/node_modules/@alloc/quick-lru/index.d.ts
generated
vendored
128
web/node_modules/@alloc/quick-lru/index.d.ts
generated
vendored
@@ -1,128 +0,0 @@
|
||||
declare namespace QuickLRU {
|
||||
interface Options<KeyType, ValueType> {
|
||||
/**
|
||||
The maximum number of milliseconds an item should remain in the cache.
|
||||
|
||||
@default Infinity
|
||||
|
||||
By default, `maxAge` will be `Infinity`, which means that items will never expire.
|
||||
Lazy expiration upon the next write or read call.
|
||||
|
||||
Individual expiration of an item can be specified by the `set(key, value, maxAge)` method.
|
||||
*/
|
||||
readonly maxAge?: number;
|
||||
|
||||
/**
|
||||
The maximum number of items before evicting the least recently used items.
|
||||
*/
|
||||
readonly maxSize: number;
|
||||
|
||||
/**
|
||||
Called right before an item is evicted from the cache.
|
||||
|
||||
Useful for side effects or for items like object URLs that need explicit cleanup (`revokeObjectURL`).
|
||||
*/
|
||||
onEviction?: (key: KeyType, value: ValueType) => void;
|
||||
}
|
||||
}
|
||||
|
||||
declare class QuickLRU<KeyType, ValueType>
|
||||
implements Iterable<[KeyType, ValueType]> {
|
||||
/**
|
||||
The stored item count.
|
||||
*/
|
||||
readonly size: number;
|
||||
|
||||
/**
|
||||
Simple ["Least Recently Used" (LRU) cache](https://en.m.wikipedia.org/wiki/Cache_replacement_policies#Least_Recently_Used_.28LRU.29).
|
||||
|
||||
The instance is [`iterable`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols) so you can use it directly in a [`for…of`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/for...of) loop.
|
||||
|
||||
@example
|
||||
```
|
||||
import QuickLRU = require('quick-lru');
|
||||
|
||||
const lru = new QuickLRU({maxSize: 1000});
|
||||
|
||||
lru.set('🦄', '🌈');
|
||||
|
||||
lru.has('🦄');
|
||||
//=> true
|
||||
|
||||
lru.get('🦄');
|
||||
//=> '🌈'
|
||||
```
|
||||
*/
|
||||
constructor(options: QuickLRU.Options<KeyType, ValueType>);
|
||||
|
||||
[Symbol.iterator](): IterableIterator<[KeyType, ValueType]>;
|
||||
|
||||
/**
|
||||
Set an item. Returns the instance.
|
||||
|
||||
Individual expiration of an item can be specified with the `maxAge` option. If not specified, the global `maxAge` value will be used in case it is specified in the constructor, otherwise the item will never expire.
|
||||
|
||||
@returns The list instance.
|
||||
*/
|
||||
set(key: KeyType, value: ValueType, options?: {maxAge?: number}): this;
|
||||
|
||||
/**
|
||||
Get an item.
|
||||
|
||||
@returns The stored item or `undefined`.
|
||||
*/
|
||||
get(key: KeyType): ValueType | undefined;
|
||||
|
||||
/**
|
||||
Check if an item exists.
|
||||
*/
|
||||
has(key: KeyType): boolean;
|
||||
|
||||
/**
|
||||
Get an item without marking it as recently used.
|
||||
|
||||
@returns The stored item or `undefined`.
|
||||
*/
|
||||
peek(key: KeyType): ValueType | undefined;
|
||||
|
||||
/**
|
||||
Delete an item.
|
||||
|
||||
@returns `true` if the item is removed or `false` if the item doesn't exist.
|
||||
*/
|
||||
delete(key: KeyType): boolean;
|
||||
|
||||
/**
|
||||
Delete all items.
|
||||
*/
|
||||
clear(): void;
|
||||
|
||||
/**
|
||||
Update the `maxSize` in-place, discarding items as necessary. Insertion order is mostly preserved, though this is not a strong guarantee.
|
||||
|
||||
Useful for on-the-fly tuning of cache sizes in live systems.
|
||||
*/
|
||||
resize(maxSize: number): void;
|
||||
|
||||
/**
|
||||
Iterable for all the keys.
|
||||
*/
|
||||
keys(): IterableIterator<KeyType>;
|
||||
|
||||
/**
|
||||
Iterable for all the values.
|
||||
*/
|
||||
values(): IterableIterator<ValueType>;
|
||||
|
||||
/**
|
||||
Iterable for all entries, starting with the oldest (ascending in recency).
|
||||
*/
|
||||
entriesAscending(): IterableIterator<[KeyType, ValueType]>;
|
||||
|
||||
/**
|
||||
Iterable for all entries, starting with the newest (descending in recency).
|
||||
*/
|
||||
entriesDescending(): IterableIterator<[KeyType, ValueType]>;
|
||||
}
|
||||
|
||||
export = QuickLRU;
|
||||
263
web/node_modules/@alloc/quick-lru/index.js
generated
vendored
263
web/node_modules/@alloc/quick-lru/index.js
generated
vendored
@@ -1,263 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
class QuickLRU {
|
||||
constructor(options = {}) {
|
||||
if (!(options.maxSize && options.maxSize > 0)) {
|
||||
throw new TypeError('`maxSize` must be a number greater than 0');
|
||||
}
|
||||
|
||||
if (typeof options.maxAge === 'number' && options.maxAge === 0) {
|
||||
throw new TypeError('`maxAge` must be a number greater than 0');
|
||||
}
|
||||
|
||||
this.maxSize = options.maxSize;
|
||||
this.maxAge = options.maxAge || Infinity;
|
||||
this.onEviction = options.onEviction;
|
||||
this.cache = new Map();
|
||||
this.oldCache = new Map();
|
||||
this._size = 0;
|
||||
}
|
||||
|
||||
_emitEvictions(cache) {
|
||||
if (typeof this.onEviction !== 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const [key, item] of cache) {
|
||||
this.onEviction(key, item.value);
|
||||
}
|
||||
}
|
||||
|
||||
_deleteIfExpired(key, item) {
|
||||
if (typeof item.expiry === 'number' && item.expiry <= Date.now()) {
|
||||
if (typeof this.onEviction === 'function') {
|
||||
this.onEviction(key, item.value);
|
||||
}
|
||||
|
||||
return this.delete(key);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_getOrDeleteIfExpired(key, item) {
|
||||
const deleted = this._deleteIfExpired(key, item);
|
||||
if (deleted === false) {
|
||||
return item.value;
|
||||
}
|
||||
}
|
||||
|
||||
_getItemValue(key, item) {
|
||||
return item.expiry ? this._getOrDeleteIfExpired(key, item) : item.value;
|
||||
}
|
||||
|
||||
_peek(key, cache) {
|
||||
const item = cache.get(key);
|
||||
|
||||
return this._getItemValue(key, item);
|
||||
}
|
||||
|
||||
_set(key, value) {
|
||||
this.cache.set(key, value);
|
||||
this._size++;
|
||||
|
||||
if (this._size >= this.maxSize) {
|
||||
this._size = 0;
|
||||
this._emitEvictions(this.oldCache);
|
||||
this.oldCache = this.cache;
|
||||
this.cache = new Map();
|
||||
}
|
||||
}
|
||||
|
||||
_moveToRecent(key, item) {
|
||||
this.oldCache.delete(key);
|
||||
this._set(key, item);
|
||||
}
|
||||
|
||||
* _entriesAscending() {
|
||||
for (const item of this.oldCache) {
|
||||
const [key, value] = item;
|
||||
if (!this.cache.has(key)) {
|
||||
const deleted = this._deleteIfExpired(key, value);
|
||||
if (deleted === false) {
|
||||
yield item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const item of this.cache) {
|
||||
const [key, value] = item;
|
||||
const deleted = this._deleteIfExpired(key, value);
|
||||
if (deleted === false) {
|
||||
yield item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get(key) {
|
||||
if (this.cache.has(key)) {
|
||||
const item = this.cache.get(key);
|
||||
|
||||
return this._getItemValue(key, item);
|
||||
}
|
||||
|
||||
if (this.oldCache.has(key)) {
|
||||
const item = this.oldCache.get(key);
|
||||
if (this._deleteIfExpired(key, item) === false) {
|
||||
this._moveToRecent(key, item);
|
||||
return item.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set(key, value, {maxAge = this.maxAge === Infinity ? undefined : Date.now() + this.maxAge} = {}) {
|
||||
if (this.cache.has(key)) {
|
||||
this.cache.set(key, {
|
||||
value,
|
||||
maxAge
|
||||
});
|
||||
} else {
|
||||
this._set(key, {value, expiry: maxAge});
|
||||
}
|
||||
}
|
||||
|
||||
has(key) {
|
||||
if (this.cache.has(key)) {
|
||||
return !this._deleteIfExpired(key, this.cache.get(key));
|
||||
}
|
||||
|
||||
if (this.oldCache.has(key)) {
|
||||
return !this._deleteIfExpired(key, this.oldCache.get(key));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
peek(key) {
|
||||
if (this.cache.has(key)) {
|
||||
return this._peek(key, this.cache);
|
||||
}
|
||||
|
||||
if (this.oldCache.has(key)) {
|
||||
return this._peek(key, this.oldCache);
|
||||
}
|
||||
}
|
||||
|
||||
delete(key) {
|
||||
const deleted = this.cache.delete(key);
|
||||
if (deleted) {
|
||||
this._size--;
|
||||
}
|
||||
|
||||
return this.oldCache.delete(key) || deleted;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.cache.clear();
|
||||
this.oldCache.clear();
|
||||
this._size = 0;
|
||||
}
|
||||
|
||||
resize(newSize) {
|
||||
if (!(newSize && newSize > 0)) {
|
||||
throw new TypeError('`maxSize` must be a number greater than 0');
|
||||
}
|
||||
|
||||
const items = [...this._entriesAscending()];
|
||||
const removeCount = items.length - newSize;
|
||||
if (removeCount < 0) {
|
||||
this.cache = new Map(items);
|
||||
this.oldCache = new Map();
|
||||
this._size = items.length;
|
||||
} else {
|
||||
if (removeCount > 0) {
|
||||
this._emitEvictions(items.slice(0, removeCount));
|
||||
}
|
||||
|
||||
this.oldCache = new Map(items.slice(removeCount));
|
||||
this.cache = new Map();
|
||||
this._size = 0;
|
||||
}
|
||||
|
||||
this.maxSize = newSize;
|
||||
}
|
||||
|
||||
* keys() {
|
||||
for (const [key] of this) {
|
||||
yield key;
|
||||
}
|
||||
}
|
||||
|
||||
* values() {
|
||||
for (const [, value] of this) {
|
||||
yield value;
|
||||
}
|
||||
}
|
||||
|
||||
* [Symbol.iterator]() {
|
||||
for (const item of this.cache) {
|
||||
const [key, value] = item;
|
||||
const deleted = this._deleteIfExpired(key, value);
|
||||
if (deleted === false) {
|
||||
yield [key, value.value];
|
||||
}
|
||||
}
|
||||
|
||||
for (const item of this.oldCache) {
|
||||
const [key, value] = item;
|
||||
if (!this.cache.has(key)) {
|
||||
const deleted = this._deleteIfExpired(key, value);
|
||||
if (deleted === false) {
|
||||
yield [key, value.value];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
* entriesDescending() {
|
||||
let items = [...this.cache];
|
||||
for (let i = items.length - 1; i >= 0; --i) {
|
||||
const item = items[i];
|
||||
const [key, value] = item;
|
||||
const deleted = this._deleteIfExpired(key, value);
|
||||
if (deleted === false) {
|
||||
yield [key, value.value];
|
||||
}
|
||||
}
|
||||
|
||||
items = [...this.oldCache];
|
||||
for (let i = items.length - 1; i >= 0; --i) {
|
||||
const item = items[i];
|
||||
const [key, value] = item;
|
||||
if (!this.cache.has(key)) {
|
||||
const deleted = this._deleteIfExpired(key, value);
|
||||
if (deleted === false) {
|
||||
yield [key, value.value];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
* entriesAscending() {
|
||||
for (const [key, value] of this._entriesAscending()) {
|
||||
yield [key, value.value];
|
||||
}
|
||||
}
|
||||
|
||||
get size() {
|
||||
if (!this._size) {
|
||||
return this.oldCache.size;
|
||||
}
|
||||
|
||||
let oldCacheSize = 0;
|
||||
for (const key of this.oldCache.keys()) {
|
||||
if (!this.cache.has(key)) {
|
||||
oldCacheSize++;
|
||||
}
|
||||
}
|
||||
|
||||
return Math.min(this._size + oldCacheSize, this.maxSize);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = QuickLRU;
|
||||
9
web/node_modules/@alloc/quick-lru/license
generated
vendored
9
web/node_modules/@alloc/quick-lru/license
generated
vendored
@@ -1,9 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
43
web/node_modules/@alloc/quick-lru/package.json
generated
vendored
43
web/node_modules/@alloc/quick-lru/package.json
generated
vendored
@@ -1,43 +0,0 @@
|
||||
{
|
||||
"name": "@alloc/quick-lru",
|
||||
"version": "5.2.0",
|
||||
"description": "Simple “Least Recently Used” (LRU) cache",
|
||||
"license": "MIT",
|
||||
"repository": "sindresorhus/quick-lru",
|
||||
"funding": "https://github.com/sponsors/sindresorhus",
|
||||
"author": {
|
||||
"name": "Sindre Sorhus",
|
||||
"email": "sindresorhus@gmail.com",
|
||||
"url": "https://sindresorhus.com"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "xo && nyc ava && tsd"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"index.d.ts"
|
||||
],
|
||||
"keywords": [
|
||||
"lru",
|
||||
"quick",
|
||||
"cache",
|
||||
"caching",
|
||||
"least",
|
||||
"recently",
|
||||
"used",
|
||||
"fast",
|
||||
"map",
|
||||
"hash",
|
||||
"buffer"
|
||||
],
|
||||
"devDependencies": {
|
||||
"ava": "^2.0.0",
|
||||
"coveralls": "^3.0.3",
|
||||
"nyc": "^15.0.0",
|
||||
"tsd": "^0.11.0",
|
||||
"xo": "^0.26.0"
|
||||
}
|
||||
}
|
||||
139
web/node_modules/@alloc/quick-lru/readme.md
generated
vendored
139
web/node_modules/@alloc/quick-lru/readme.md
generated
vendored
@@ -1,139 +0,0 @@
|
||||
# quick-lru [](https://travis-ci.org/sindresorhus/quick-lru) [](https://coveralls.io/github/sindresorhus/quick-lru?branch=master)
|
||||
|
||||
> Simple [“Least Recently Used” (LRU) cache](https://en.m.wikipedia.org/wiki/Cache_replacement_policies#Least_Recently_Used_.28LRU.29)
|
||||
|
||||
Useful when you need to cache something and limit memory usage.
|
||||
|
||||
Inspired by the [`hashlru` algorithm](https://github.com/dominictarr/hashlru#algorithm), but instead uses [`Map`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map) to support keys of any type, not just strings, and values can be `undefined`.
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
$ npm install quick-lru
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
const QuickLRU = require('quick-lru');
|
||||
|
||||
const lru = new QuickLRU({maxSize: 1000});
|
||||
|
||||
lru.set('🦄', '🌈');
|
||||
|
||||
lru.has('🦄');
|
||||
//=> true
|
||||
|
||||
lru.get('🦄');
|
||||
//=> '🌈'
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### new QuickLRU(options?)
|
||||
|
||||
Returns a new instance.
|
||||
|
||||
### options
|
||||
|
||||
Type: `object`
|
||||
|
||||
#### maxSize
|
||||
|
||||
*Required*\
|
||||
Type: `number`
|
||||
|
||||
The maximum number of items before evicting the least recently used items.
|
||||
|
||||
#### maxAge
|
||||
|
||||
Type: `number`\
|
||||
Default: `Infinity`
|
||||
|
||||
The maximum number of milliseconds an item should remain in cache.
|
||||
By default maxAge will be Infinity, which means that items will never expire.
|
||||
|
||||
Lazy expiration happens upon the next `write` or `read` call.
|
||||
|
||||
Individual expiration of an item can be specified by the `set(key, value, options)` method.
|
||||
|
||||
#### onEviction
|
||||
|
||||
*Optional*\
|
||||
Type: `(key, value) => void`
|
||||
|
||||
Called right before an item is evicted from the cache.
|
||||
|
||||
Useful for side effects or for items like object URLs that need explicit cleanup (`revokeObjectURL`).
|
||||
|
||||
### Instance
|
||||
|
||||
The instance is [`iterable`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols) so you can use it directly in a [`for…of`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/for...of) loop.
|
||||
|
||||
Both `key` and `value` can be of any type.
|
||||
|
||||
#### .set(key, value, options?)
|
||||
|
||||
Set an item. Returns the instance.
|
||||
|
||||
Individual expiration of an item can be specified with the `maxAge` option. If not specified, the global `maxAge` value will be used in case it is specified on the constructor, otherwise the item will never expire.
|
||||
|
||||
#### .get(key)
|
||||
|
||||
Get an item.
|
||||
|
||||
#### .has(key)
|
||||
|
||||
Check if an item exists.
|
||||
|
||||
#### .peek(key)
|
||||
|
||||
Get an item without marking it as recently used.
|
||||
|
||||
#### .delete(key)
|
||||
|
||||
Delete an item.
|
||||
|
||||
Returns `true` if the item is removed or `false` if the item doesn't exist.
|
||||
|
||||
#### .clear()
|
||||
|
||||
Delete all items.
|
||||
|
||||
#### .resize(maxSize)
|
||||
|
||||
Update the `maxSize`, discarding items as necessary. Insertion order is mostly preserved, though this is not a strong guarantee.
|
||||
|
||||
Useful for on-the-fly tuning of cache sizes in live systems.
|
||||
|
||||
#### .keys()
|
||||
|
||||
Iterable for all the keys.
|
||||
|
||||
#### .values()
|
||||
|
||||
Iterable for all the values.
|
||||
|
||||
#### .entriesAscending()
|
||||
|
||||
Iterable for all entries, starting with the oldest (ascending in recency).
|
||||
|
||||
#### .entriesDescending()
|
||||
|
||||
Iterable for all entries, starting with the newest (descending in recency).
|
||||
|
||||
#### .size
|
||||
|
||||
The stored item count.
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<b>
|
||||
<a href="https://tidelift.com/subscription/pkg/npm-quick-lru?utm_source=npm-quick-lru&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a>
|
||||
</b>
|
||||
<br>
|
||||
<sub>
|
||||
Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies.
|
||||
</sub>
|
||||
</div>
|
||||
22
web/node_modules/@babel/code-frame/LICENSE
generated
vendored
22
web/node_modules/@babel/code-frame/LICENSE
generated
vendored
@@ -1,22 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2014-present Sebastian McKenzie and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
19
web/node_modules/@babel/code-frame/README.md
generated
vendored
19
web/node_modules/@babel/code-frame/README.md
generated
vendored
@@ -1,19 +0,0 @@
|
||||
# @babel/code-frame
|
||||
|
||||
> Generate errors that contain a code frame that point to source locations.
|
||||
|
||||
See our website [@babel/code-frame](https://babeljs.io/docs/babel-code-frame) for more information.
|
||||
|
||||
## Install
|
||||
|
||||
Using npm:
|
||||
|
||||
```sh
|
||||
npm install --save-dev @babel/code-frame
|
||||
```
|
||||
|
||||
or using yarn:
|
||||
|
||||
```sh
|
||||
yarn add @babel/code-frame --dev
|
||||
```
|
||||
216
web/node_modules/@babel/code-frame/lib/index.js
generated
vendored
216
web/node_modules/@babel/code-frame/lib/index.js
generated
vendored
@@ -1,216 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
var picocolors = require('picocolors');
|
||||
var jsTokens = require('js-tokens');
|
||||
var helperValidatorIdentifier = require('@babel/helper-validator-identifier');
|
||||
|
||||
function isColorSupported() {
|
||||
return (typeof process === "object" && (process.env.FORCE_COLOR === "0" || process.env.FORCE_COLOR === "false") ? false : picocolors.isColorSupported
|
||||
);
|
||||
}
|
||||
const compose = (f, g) => v => f(g(v));
|
||||
function buildDefs(colors) {
|
||||
return {
|
||||
keyword: colors.cyan,
|
||||
capitalized: colors.yellow,
|
||||
jsxIdentifier: colors.yellow,
|
||||
punctuator: colors.yellow,
|
||||
number: colors.magenta,
|
||||
string: colors.green,
|
||||
regex: colors.magenta,
|
||||
comment: colors.gray,
|
||||
invalid: compose(compose(colors.white, colors.bgRed), colors.bold),
|
||||
gutter: colors.gray,
|
||||
marker: compose(colors.red, colors.bold),
|
||||
message: compose(colors.red, colors.bold),
|
||||
reset: colors.reset
|
||||
};
|
||||
}
|
||||
const defsOn = buildDefs(picocolors.createColors(true));
|
||||
const defsOff = buildDefs(picocolors.createColors(false));
|
||||
function getDefs(enabled) {
|
||||
return enabled ? defsOn : defsOff;
|
||||
}
|
||||
|
||||
const sometimesKeywords = new Set(["as", "async", "from", "get", "of", "set"]);
|
||||
const NEWLINE$1 = /\r\n|[\n\r\u2028\u2029]/;
|
||||
const BRACKET = /^[()[\]{}]$/;
|
||||
let tokenize;
|
||||
{
|
||||
const JSX_TAG = /^[a-z][\w-]*$/i;
|
||||
const getTokenType = function (token, offset, text) {
|
||||
if (token.type === "name") {
|
||||
if (helperValidatorIdentifier.isKeyword(token.value) || helperValidatorIdentifier.isStrictReservedWord(token.value, true) || sometimesKeywords.has(token.value)) {
|
||||
return "keyword";
|
||||
}
|
||||
if (JSX_TAG.test(token.value) && (text[offset - 1] === "<" || text.slice(offset - 2, offset) === "</")) {
|
||||
return "jsxIdentifier";
|
||||
}
|
||||
if (token.value[0] !== token.value[0].toLowerCase()) {
|
||||
return "capitalized";
|
||||
}
|
||||
}
|
||||
if (token.type === "punctuator" && BRACKET.test(token.value)) {
|
||||
return "bracket";
|
||||
}
|
||||
if (token.type === "invalid" && (token.value === "@" || token.value === "#")) {
|
||||
return "punctuator";
|
||||
}
|
||||
return token.type;
|
||||
};
|
||||
tokenize = function* (text) {
|
||||
let match;
|
||||
while (match = jsTokens.default.exec(text)) {
|
||||
const token = jsTokens.matchToToken(match);
|
||||
yield {
|
||||
type: getTokenType(token, match.index, text),
|
||||
value: token.value
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
function highlight(text) {
|
||||
if (text === "") return "";
|
||||
const defs = getDefs(true);
|
||||
let highlighted = "";
|
||||
for (const {
|
||||
type,
|
||||
value
|
||||
} of tokenize(text)) {
|
||||
if (type in defs) {
|
||||
highlighted += value.split(NEWLINE$1).map(str => defs[type](str)).join("\n");
|
||||
} else {
|
||||
highlighted += value;
|
||||
}
|
||||
}
|
||||
return highlighted;
|
||||
}
|
||||
|
||||
let deprecationWarningShown = false;
|
||||
const NEWLINE = /\r\n|[\n\r\u2028\u2029]/;
|
||||
function getMarkerLines(loc, source, opts) {
|
||||
const startLoc = Object.assign({
|
||||
column: 0,
|
||||
line: -1
|
||||
}, loc.start);
|
||||
const endLoc = Object.assign({}, startLoc, loc.end);
|
||||
const {
|
||||
linesAbove = 2,
|
||||
linesBelow = 3
|
||||
} = opts || {};
|
||||
const startLine = startLoc.line;
|
||||
const startColumn = startLoc.column;
|
||||
const endLine = endLoc.line;
|
||||
const endColumn = endLoc.column;
|
||||
let start = Math.max(startLine - (linesAbove + 1), 0);
|
||||
let end = Math.min(source.length, endLine + linesBelow);
|
||||
if (startLine === -1) {
|
||||
start = 0;
|
||||
}
|
||||
if (endLine === -1) {
|
||||
end = source.length;
|
||||
}
|
||||
const lineDiff = endLine - startLine;
|
||||
const markerLines = {};
|
||||
if (lineDiff) {
|
||||
for (let i = 0; i <= lineDiff; i++) {
|
||||
const lineNumber = i + startLine;
|
||||
if (!startColumn) {
|
||||
markerLines[lineNumber] = true;
|
||||
} else if (i === 0) {
|
||||
const sourceLength = source[lineNumber - 1].length;
|
||||
markerLines[lineNumber] = [startColumn, sourceLength - startColumn + 1];
|
||||
} else if (i === lineDiff) {
|
||||
markerLines[lineNumber] = [0, endColumn];
|
||||
} else {
|
||||
const sourceLength = source[lineNumber - i].length;
|
||||
markerLines[lineNumber] = [0, sourceLength];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (startColumn === endColumn) {
|
||||
if (startColumn) {
|
||||
markerLines[startLine] = [startColumn, 0];
|
||||
} else {
|
||||
markerLines[startLine] = true;
|
||||
}
|
||||
} else {
|
||||
markerLines[startLine] = [startColumn, endColumn - startColumn];
|
||||
}
|
||||
}
|
||||
return {
|
||||
start,
|
||||
end,
|
||||
markerLines
|
||||
};
|
||||
}
|
||||
function codeFrameColumns(rawLines, loc, opts = {}) {
|
||||
const shouldHighlight = opts.forceColor || isColorSupported() && opts.highlightCode;
|
||||
const defs = getDefs(shouldHighlight);
|
||||
const lines = rawLines.split(NEWLINE);
|
||||
const {
|
||||
start,
|
||||
end,
|
||||
markerLines
|
||||
} = getMarkerLines(loc, lines, opts);
|
||||
const hasColumns = loc.start && typeof loc.start.column === "number";
|
||||
const numberMaxWidth = String(end).length;
|
||||
const highlightedLines = shouldHighlight ? highlight(rawLines) : rawLines;
|
||||
let frame = highlightedLines.split(NEWLINE, end).slice(start, end).map((line, index) => {
|
||||
const number = start + 1 + index;
|
||||
const paddedNumber = ` ${number}`.slice(-numberMaxWidth);
|
||||
const gutter = ` ${paddedNumber} |`;
|
||||
const hasMarker = markerLines[number];
|
||||
const lastMarkerLine = !markerLines[number + 1];
|
||||
if (hasMarker) {
|
||||
let markerLine = "";
|
||||
if (Array.isArray(hasMarker)) {
|
||||
const markerSpacing = line.slice(0, Math.max(hasMarker[0] - 1, 0)).replace(/[^\t]/g, " ");
|
||||
const numberOfMarkers = hasMarker[1] || 1;
|
||||
markerLine = ["\n ", defs.gutter(gutter.replace(/\d/g, " ")), " ", markerSpacing, defs.marker("^").repeat(numberOfMarkers)].join("");
|
||||
if (lastMarkerLine && opts.message) {
|
||||
markerLine += " " + defs.message(opts.message);
|
||||
}
|
||||
}
|
||||
return [defs.marker(">"), defs.gutter(gutter), line.length > 0 ? ` ${line}` : "", markerLine].join("");
|
||||
} else {
|
||||
return ` ${defs.gutter(gutter)}${line.length > 0 ? ` ${line}` : ""}`;
|
||||
}
|
||||
}).join("\n");
|
||||
if (opts.message && !hasColumns) {
|
||||
frame = `${" ".repeat(numberMaxWidth + 1)}${opts.message}\n${frame}`;
|
||||
}
|
||||
if (shouldHighlight) {
|
||||
return defs.reset(frame);
|
||||
} else {
|
||||
return frame;
|
||||
}
|
||||
}
|
||||
function index (rawLines, lineNumber, colNumber, opts = {}) {
|
||||
if (!deprecationWarningShown) {
|
||||
deprecationWarningShown = true;
|
||||
const message = "Passing lineNumber and colNumber is deprecated to @babel/code-frame. Please use `codeFrameColumns`.";
|
||||
if (process.emitWarning) {
|
||||
process.emitWarning(message, "DeprecationWarning");
|
||||
} else {
|
||||
const deprecationError = new Error(message);
|
||||
deprecationError.name = "DeprecationWarning";
|
||||
console.warn(new Error(message));
|
||||
}
|
||||
}
|
||||
colNumber = Math.max(colNumber, 0);
|
||||
const location = {
|
||||
start: {
|
||||
column: colNumber,
|
||||
line: lineNumber
|
||||
}
|
||||
};
|
||||
return codeFrameColumns(rawLines, location, opts);
|
||||
}
|
||||
|
||||
exports.codeFrameColumns = codeFrameColumns;
|
||||
exports.default = index;
|
||||
exports.highlight = highlight;
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
web/node_modules/@babel/code-frame/lib/index.js.map
generated
vendored
1
web/node_modules/@babel/code-frame/lib/index.js.map
generated
vendored
File diff suppressed because one or more lines are too long
31
web/node_modules/@babel/code-frame/package.json
generated
vendored
31
web/node_modules/@babel/code-frame/package.json
generated
vendored
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"name": "@babel/code-frame",
|
||||
"version": "7.27.1",
|
||||
"description": "Generate errors that contain a code frame that point to source locations.",
|
||||
"author": "The Babel Team (https://babel.dev/team)",
|
||||
"homepage": "https://babel.dev/docs/en/next/babel-code-frame",
|
||||
"bugs": "https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/babel/babel.git",
|
||||
"directory": "packages/babel-code-frame"
|
||||
},
|
||||
"main": "./lib/index.js",
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.27.1",
|
||||
"js-tokens": "^4.0.0",
|
||||
"picocolors": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"import-meta-resolve": "^4.1.0",
|
||||
"strip-ansi": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"type": "commonjs"
|
||||
}
|
||||
22
web/node_modules/@babel/compat-data/LICENSE
generated
vendored
22
web/node_modules/@babel/compat-data/LICENSE
generated
vendored
@@ -1,22 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2014-present Sebastian McKenzie and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
19
web/node_modules/@babel/compat-data/README.md
generated
vendored
19
web/node_modules/@babel/compat-data/README.md
generated
vendored
@@ -1,19 +0,0 @@
|
||||
# @babel/compat-data
|
||||
|
||||
> The compat-data to determine required Babel plugins
|
||||
|
||||
See our website [@babel/compat-data](https://babeljs.io/docs/babel-compat-data) for more information.
|
||||
|
||||
## Install
|
||||
|
||||
Using npm:
|
||||
|
||||
```sh
|
||||
npm install --save @babel/compat-data
|
||||
```
|
||||
|
||||
or using yarn:
|
||||
|
||||
```sh
|
||||
yarn add @babel/compat-data
|
||||
```
|
||||
2
web/node_modules/@babel/compat-data/corejs2-built-ins.js
generated
vendored
2
web/node_modules/@babel/compat-data/corejs2-built-ins.js
generated
vendored
@@ -1,2 +0,0 @@
|
||||
// Todo (Babel 8): remove this file as Babel 8 drop support of core-js 2
|
||||
module.exports = require("./data/corejs2-built-ins.json");
|
||||
2
web/node_modules/@babel/compat-data/corejs3-shipped-proposals.js
generated
vendored
2
web/node_modules/@babel/compat-data/corejs3-shipped-proposals.js
generated
vendored
@@ -1,2 +0,0 @@
|
||||
// Todo (Babel 8): remove this file now that it is included in babel-plugin-polyfill-corejs3
|
||||
module.exports = require("./data/corejs3-shipped-proposals.json");
|
||||
2106
web/node_modules/@babel/compat-data/data/corejs2-built-ins.json
generated
vendored
2106
web/node_modules/@babel/compat-data/data/corejs2-built-ins.json
generated
vendored
File diff suppressed because it is too large
Load Diff
5
web/node_modules/@babel/compat-data/data/corejs3-shipped-proposals.json
generated
vendored
5
web/node_modules/@babel/compat-data/data/corejs3-shipped-proposals.json
generated
vendored
@@ -1,5 +0,0 @@
|
||||
[
|
||||
"esnext.promise.all-settled",
|
||||
"esnext.string.match-all",
|
||||
"esnext.global-this"
|
||||
]
|
||||
18
web/node_modules/@babel/compat-data/data/native-modules.json
generated
vendored
18
web/node_modules/@babel/compat-data/data/native-modules.json
generated
vendored
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"es6.module": {
|
||||
"chrome": "61",
|
||||
"and_chr": "61",
|
||||
"edge": "16",
|
||||
"firefox": "60",
|
||||
"and_ff": "60",
|
||||
"node": "13.2.0",
|
||||
"opera": "48",
|
||||
"op_mob": "45",
|
||||
"safari": "10.1",
|
||||
"ios": "10.3",
|
||||
"samsung": "8.2",
|
||||
"android": "61",
|
||||
"electron": "2.0",
|
||||
"ios_saf": "10.3"
|
||||
}
|
||||
}
|
||||
35
web/node_modules/@babel/compat-data/data/overlapping-plugins.json
generated
vendored
35
web/node_modules/@babel/compat-data/data/overlapping-plugins.json
generated
vendored
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"transform-async-to-generator": [
|
||||
"bugfix/transform-async-arrows-in-class"
|
||||
],
|
||||
"transform-parameters": [
|
||||
"bugfix/transform-edge-default-parameters",
|
||||
"bugfix/transform-safari-id-destructuring-collision-in-function-expression"
|
||||
],
|
||||
"transform-function-name": [
|
||||
"bugfix/transform-edge-function-name"
|
||||
],
|
||||
"transform-block-scoping": [
|
||||
"bugfix/transform-safari-block-shadowing",
|
||||
"bugfix/transform-safari-for-shadowing"
|
||||
],
|
||||
"transform-template-literals": [
|
||||
"bugfix/transform-tagged-template-caching"
|
||||
],
|
||||
"transform-optional-chaining": [
|
||||
"bugfix/transform-v8-spread-parameters-in-optional-chaining"
|
||||
],
|
||||
"proposal-optional-chaining": [
|
||||
"bugfix/transform-v8-spread-parameters-in-optional-chaining"
|
||||
],
|
||||
"transform-class-properties": [
|
||||
"bugfix/transform-v8-static-class-fields-redefine-readonly",
|
||||
"bugfix/transform-firefox-class-in-computed-class-key",
|
||||
"bugfix/transform-safari-class-field-initializer-scope"
|
||||
],
|
||||
"proposal-class-properties": [
|
||||
"bugfix/transform-v8-static-class-fields-redefine-readonly",
|
||||
"bugfix/transform-firefox-class-in-computed-class-key",
|
||||
"bugfix/transform-safari-class-field-initializer-scope"
|
||||
]
|
||||
}
|
||||
203
web/node_modules/@babel/compat-data/data/plugin-bugfixes.json
generated
vendored
203
web/node_modules/@babel/compat-data/data/plugin-bugfixes.json
generated
vendored
@@ -1,203 +0,0 @@
|
||||
{
|
||||
"bugfix/transform-async-arrows-in-class": {
|
||||
"chrome": "55",
|
||||
"opera": "42",
|
||||
"edge": "15",
|
||||
"firefox": "52",
|
||||
"safari": "11",
|
||||
"node": "7.6",
|
||||
"deno": "1",
|
||||
"ios": "11",
|
||||
"samsung": "6",
|
||||
"opera_mobile": "42",
|
||||
"electron": "1.6"
|
||||
},
|
||||
"bugfix/transform-edge-default-parameters": {
|
||||
"chrome": "49",
|
||||
"opera": "36",
|
||||
"edge": "18",
|
||||
"firefox": "52",
|
||||
"safari": "10",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "36",
|
||||
"electron": "0.37"
|
||||
},
|
||||
"bugfix/transform-edge-function-name": {
|
||||
"chrome": "51",
|
||||
"opera": "38",
|
||||
"edge": "79",
|
||||
"firefox": "53",
|
||||
"safari": "10",
|
||||
"node": "6.5",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "41",
|
||||
"electron": "1.2"
|
||||
},
|
||||
"bugfix/transform-safari-block-shadowing": {
|
||||
"chrome": "49",
|
||||
"opera": "36",
|
||||
"edge": "12",
|
||||
"firefox": "44",
|
||||
"safari": "11",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ie": "11",
|
||||
"ios": "11",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "36",
|
||||
"electron": "0.37"
|
||||
},
|
||||
"bugfix/transform-safari-for-shadowing": {
|
||||
"chrome": "49",
|
||||
"opera": "36",
|
||||
"edge": "12",
|
||||
"firefox": "4",
|
||||
"safari": "11",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ie": "11",
|
||||
"ios": "11",
|
||||
"samsung": "5",
|
||||
"rhino": "1.7.13",
|
||||
"opera_mobile": "36",
|
||||
"electron": "0.37"
|
||||
},
|
||||
"bugfix/transform-safari-id-destructuring-collision-in-function-expression": {
|
||||
"chrome": "49",
|
||||
"opera": "36",
|
||||
"edge": "14",
|
||||
"firefox": "2",
|
||||
"safari": "16.3",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "16.3",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "36",
|
||||
"electron": "0.37"
|
||||
},
|
||||
"bugfix/transform-tagged-template-caching": {
|
||||
"chrome": "41",
|
||||
"opera": "28",
|
||||
"edge": "12",
|
||||
"firefox": "34",
|
||||
"safari": "13",
|
||||
"node": "4",
|
||||
"deno": "1",
|
||||
"ios": "13",
|
||||
"samsung": "3.4",
|
||||
"rhino": "1.7.14",
|
||||
"opera_mobile": "28",
|
||||
"electron": "0.21"
|
||||
},
|
||||
"bugfix/transform-v8-spread-parameters-in-optional-chaining": {
|
||||
"chrome": "91",
|
||||
"opera": "77",
|
||||
"edge": "91",
|
||||
"firefox": "74",
|
||||
"safari": "13.1",
|
||||
"node": "16.9",
|
||||
"deno": "1.9",
|
||||
"ios": "13.4",
|
||||
"samsung": "16",
|
||||
"opera_mobile": "64",
|
||||
"electron": "13.0"
|
||||
},
|
||||
"transform-optional-chaining": {
|
||||
"chrome": "80",
|
||||
"opera": "67",
|
||||
"edge": "80",
|
||||
"firefox": "74",
|
||||
"safari": "13.1",
|
||||
"node": "14",
|
||||
"deno": "1",
|
||||
"ios": "13.4",
|
||||
"samsung": "13",
|
||||
"rhino": "1.8",
|
||||
"opera_mobile": "57",
|
||||
"electron": "8.0"
|
||||
},
|
||||
"proposal-optional-chaining": {
|
||||
"chrome": "80",
|
||||
"opera": "67",
|
||||
"edge": "80",
|
||||
"firefox": "74",
|
||||
"safari": "13.1",
|
||||
"node": "14",
|
||||
"deno": "1",
|
||||
"ios": "13.4",
|
||||
"samsung": "13",
|
||||
"rhino": "1.8",
|
||||
"opera_mobile": "57",
|
||||
"electron": "8.0"
|
||||
},
|
||||
"transform-parameters": {
|
||||
"chrome": "49",
|
||||
"opera": "36",
|
||||
"edge": "15",
|
||||
"firefox": "52",
|
||||
"safari": "10",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "36",
|
||||
"electron": "0.37"
|
||||
},
|
||||
"transform-async-to-generator": {
|
||||
"chrome": "55",
|
||||
"opera": "42",
|
||||
"edge": "15",
|
||||
"firefox": "52",
|
||||
"safari": "10.1",
|
||||
"node": "7.6",
|
||||
"deno": "1",
|
||||
"ios": "10.3",
|
||||
"samsung": "6",
|
||||
"opera_mobile": "42",
|
||||
"electron": "1.6"
|
||||
},
|
||||
"transform-template-literals": {
|
||||
"chrome": "41",
|
||||
"opera": "28",
|
||||
"edge": "13",
|
||||
"firefox": "34",
|
||||
"safari": "9",
|
||||
"node": "4",
|
||||
"deno": "1",
|
||||
"ios": "9",
|
||||
"samsung": "3.4",
|
||||
"opera_mobile": "28",
|
||||
"electron": "0.21"
|
||||
},
|
||||
"transform-function-name": {
|
||||
"chrome": "51",
|
||||
"opera": "38",
|
||||
"edge": "14",
|
||||
"firefox": "53",
|
||||
"safari": "10",
|
||||
"node": "6.5",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "41",
|
||||
"electron": "1.2"
|
||||
},
|
||||
"transform-block-scoping": {
|
||||
"chrome": "50",
|
||||
"opera": "37",
|
||||
"edge": "14",
|
||||
"firefox": "53",
|
||||
"safari": "10",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "37",
|
||||
"electron": "1.1"
|
||||
}
|
||||
}
|
||||
838
web/node_modules/@babel/compat-data/data/plugins.json
generated
vendored
838
web/node_modules/@babel/compat-data/data/plugins.json
generated
vendored
@@ -1,838 +0,0 @@
|
||||
{
|
||||
"transform-explicit-resource-management": {
|
||||
"chrome": "134",
|
||||
"edge": "134",
|
||||
"firefox": "141",
|
||||
"node": "24",
|
||||
"electron": "35.0"
|
||||
},
|
||||
"transform-duplicate-named-capturing-groups-regex": {
|
||||
"chrome": "126",
|
||||
"opera": "112",
|
||||
"edge": "126",
|
||||
"firefox": "129",
|
||||
"safari": "17.4",
|
||||
"node": "23",
|
||||
"ios": "17.4",
|
||||
"electron": "31.0"
|
||||
},
|
||||
"transform-regexp-modifiers": {
|
||||
"chrome": "125",
|
||||
"opera": "111",
|
||||
"edge": "125",
|
||||
"firefox": "132",
|
||||
"node": "23",
|
||||
"samsung": "27",
|
||||
"electron": "31.0"
|
||||
},
|
||||
"transform-unicode-sets-regex": {
|
||||
"chrome": "112",
|
||||
"opera": "98",
|
||||
"edge": "112",
|
||||
"firefox": "116",
|
||||
"safari": "17",
|
||||
"node": "20",
|
||||
"deno": "1.32",
|
||||
"ios": "17",
|
||||
"samsung": "23",
|
||||
"opera_mobile": "75",
|
||||
"electron": "24.0"
|
||||
},
|
||||
"bugfix/transform-v8-static-class-fields-redefine-readonly": {
|
||||
"chrome": "98",
|
||||
"opera": "84",
|
||||
"edge": "98",
|
||||
"firefox": "75",
|
||||
"safari": "15",
|
||||
"node": "12",
|
||||
"deno": "1.18",
|
||||
"ios": "15",
|
||||
"samsung": "11",
|
||||
"opera_mobile": "52",
|
||||
"electron": "17.0"
|
||||
},
|
||||
"bugfix/transform-firefox-class-in-computed-class-key": {
|
||||
"chrome": "74",
|
||||
"opera": "62",
|
||||
"edge": "79",
|
||||
"firefox": "126",
|
||||
"safari": "16",
|
||||
"node": "12",
|
||||
"deno": "1",
|
||||
"ios": "16",
|
||||
"samsung": "11",
|
||||
"opera_mobile": "53",
|
||||
"electron": "6.0"
|
||||
},
|
||||
"bugfix/transform-safari-class-field-initializer-scope": {
|
||||
"chrome": "74",
|
||||
"opera": "62",
|
||||
"edge": "79",
|
||||
"firefox": "69",
|
||||
"safari": "16",
|
||||
"node": "12",
|
||||
"deno": "1",
|
||||
"ios": "16",
|
||||
"samsung": "11",
|
||||
"opera_mobile": "53",
|
||||
"electron": "6.0"
|
||||
},
|
||||
"transform-class-static-block": {
|
||||
"chrome": "94",
|
||||
"opera": "80",
|
||||
"edge": "94",
|
||||
"firefox": "93",
|
||||
"safari": "16.4",
|
||||
"node": "16.11",
|
||||
"deno": "1.14",
|
||||
"ios": "16.4",
|
||||
"samsung": "17",
|
||||
"opera_mobile": "66",
|
||||
"electron": "15.0"
|
||||
},
|
||||
"proposal-class-static-block": {
|
||||
"chrome": "94",
|
||||
"opera": "80",
|
||||
"edge": "94",
|
||||
"firefox": "93",
|
||||
"safari": "16.4",
|
||||
"node": "16.11",
|
||||
"deno": "1.14",
|
||||
"ios": "16.4",
|
||||
"samsung": "17",
|
||||
"opera_mobile": "66",
|
||||
"electron": "15.0"
|
||||
},
|
||||
"transform-private-property-in-object": {
|
||||
"chrome": "91",
|
||||
"opera": "77",
|
||||
"edge": "91",
|
||||
"firefox": "90",
|
||||
"safari": "15",
|
||||
"node": "16.9",
|
||||
"deno": "1.9",
|
||||
"ios": "15",
|
||||
"samsung": "16",
|
||||
"opera_mobile": "64",
|
||||
"electron": "13.0"
|
||||
},
|
||||
"proposal-private-property-in-object": {
|
||||
"chrome": "91",
|
||||
"opera": "77",
|
||||
"edge": "91",
|
||||
"firefox": "90",
|
||||
"safari": "15",
|
||||
"node": "16.9",
|
||||
"deno": "1.9",
|
||||
"ios": "15",
|
||||
"samsung": "16",
|
||||
"opera_mobile": "64",
|
||||
"electron": "13.0"
|
||||
},
|
||||
"transform-class-properties": {
|
||||
"chrome": "74",
|
||||
"opera": "62",
|
||||
"edge": "79",
|
||||
"firefox": "90",
|
||||
"safari": "14.1",
|
||||
"node": "12",
|
||||
"deno": "1",
|
||||
"ios": "14.5",
|
||||
"samsung": "11",
|
||||
"opera_mobile": "53",
|
||||
"electron": "6.0"
|
||||
},
|
||||
"proposal-class-properties": {
|
||||
"chrome": "74",
|
||||
"opera": "62",
|
||||
"edge": "79",
|
||||
"firefox": "90",
|
||||
"safari": "14.1",
|
||||
"node": "12",
|
||||
"deno": "1",
|
||||
"ios": "14.5",
|
||||
"samsung": "11",
|
||||
"opera_mobile": "53",
|
||||
"electron": "6.0"
|
||||
},
|
||||
"transform-private-methods": {
|
||||
"chrome": "84",
|
||||
"opera": "70",
|
||||
"edge": "84",
|
||||
"firefox": "90",
|
||||
"safari": "15",
|
||||
"node": "14.6",
|
||||
"deno": "1",
|
||||
"ios": "15",
|
||||
"samsung": "14",
|
||||
"opera_mobile": "60",
|
||||
"electron": "10.0"
|
||||
},
|
||||
"proposal-private-methods": {
|
||||
"chrome": "84",
|
||||
"opera": "70",
|
||||
"edge": "84",
|
||||
"firefox": "90",
|
||||
"safari": "15",
|
||||
"node": "14.6",
|
||||
"deno": "1",
|
||||
"ios": "15",
|
||||
"samsung": "14",
|
||||
"opera_mobile": "60",
|
||||
"electron": "10.0"
|
||||
},
|
||||
"transform-numeric-separator": {
|
||||
"chrome": "75",
|
||||
"opera": "62",
|
||||
"edge": "79",
|
||||
"firefox": "70",
|
||||
"safari": "13",
|
||||
"node": "12.5",
|
||||
"deno": "1",
|
||||
"ios": "13",
|
||||
"samsung": "11",
|
||||
"rhino": "1.7.14",
|
||||
"opera_mobile": "54",
|
||||
"electron": "6.0"
|
||||
},
|
||||
"proposal-numeric-separator": {
|
||||
"chrome": "75",
|
||||
"opera": "62",
|
||||
"edge": "79",
|
||||
"firefox": "70",
|
||||
"safari": "13",
|
||||
"node": "12.5",
|
||||
"deno": "1",
|
||||
"ios": "13",
|
||||
"samsung": "11",
|
||||
"rhino": "1.7.14",
|
||||
"opera_mobile": "54",
|
||||
"electron": "6.0"
|
||||
},
|
||||
"transform-logical-assignment-operators": {
|
||||
"chrome": "85",
|
||||
"opera": "71",
|
||||
"edge": "85",
|
||||
"firefox": "79",
|
||||
"safari": "14",
|
||||
"node": "15",
|
||||
"deno": "1.2",
|
||||
"ios": "14",
|
||||
"samsung": "14",
|
||||
"opera_mobile": "60",
|
||||
"electron": "10.0"
|
||||
},
|
||||
"proposal-logical-assignment-operators": {
|
||||
"chrome": "85",
|
||||
"opera": "71",
|
||||
"edge": "85",
|
||||
"firefox": "79",
|
||||
"safari": "14",
|
||||
"node": "15",
|
||||
"deno": "1.2",
|
||||
"ios": "14",
|
||||
"samsung": "14",
|
||||
"opera_mobile": "60",
|
||||
"electron": "10.0"
|
||||
},
|
||||
"transform-nullish-coalescing-operator": {
|
||||
"chrome": "80",
|
||||
"opera": "67",
|
||||
"edge": "80",
|
||||
"firefox": "72",
|
||||
"safari": "13.1",
|
||||
"node": "14",
|
||||
"deno": "1",
|
||||
"ios": "13.4",
|
||||
"samsung": "13",
|
||||
"rhino": "1.8",
|
||||
"opera_mobile": "57",
|
||||
"electron": "8.0"
|
||||
},
|
||||
"proposal-nullish-coalescing-operator": {
|
||||
"chrome": "80",
|
||||
"opera": "67",
|
||||
"edge": "80",
|
||||
"firefox": "72",
|
||||
"safari": "13.1",
|
||||
"node": "14",
|
||||
"deno": "1",
|
||||
"ios": "13.4",
|
||||
"samsung": "13",
|
||||
"rhino": "1.8",
|
||||
"opera_mobile": "57",
|
||||
"electron": "8.0"
|
||||
},
|
||||
"transform-optional-chaining": {
|
||||
"chrome": "91",
|
||||
"opera": "77",
|
||||
"edge": "91",
|
||||
"firefox": "74",
|
||||
"safari": "13.1",
|
||||
"node": "16.9",
|
||||
"deno": "1.9",
|
||||
"ios": "13.4",
|
||||
"samsung": "16",
|
||||
"opera_mobile": "64",
|
||||
"electron": "13.0"
|
||||
},
|
||||
"proposal-optional-chaining": {
|
||||
"chrome": "91",
|
||||
"opera": "77",
|
||||
"edge": "91",
|
||||
"firefox": "74",
|
||||
"safari": "13.1",
|
||||
"node": "16.9",
|
||||
"deno": "1.9",
|
||||
"ios": "13.4",
|
||||
"samsung": "16",
|
||||
"opera_mobile": "64",
|
||||
"electron": "13.0"
|
||||
},
|
||||
"transform-json-strings": {
|
||||
"chrome": "66",
|
||||
"opera": "53",
|
||||
"edge": "79",
|
||||
"firefox": "62",
|
||||
"safari": "12",
|
||||
"node": "10",
|
||||
"deno": "1",
|
||||
"ios": "12",
|
||||
"samsung": "9",
|
||||
"rhino": "1.7.14",
|
||||
"opera_mobile": "47",
|
||||
"electron": "3.0"
|
||||
},
|
||||
"proposal-json-strings": {
|
||||
"chrome": "66",
|
||||
"opera": "53",
|
||||
"edge": "79",
|
||||
"firefox": "62",
|
||||
"safari": "12",
|
||||
"node": "10",
|
||||
"deno": "1",
|
||||
"ios": "12",
|
||||
"samsung": "9",
|
||||
"rhino": "1.7.14",
|
||||
"opera_mobile": "47",
|
||||
"electron": "3.0"
|
||||
},
|
||||
"transform-optional-catch-binding": {
|
||||
"chrome": "66",
|
||||
"opera": "53",
|
||||
"edge": "79",
|
||||
"firefox": "58",
|
||||
"safari": "11.1",
|
||||
"node": "10",
|
||||
"deno": "1",
|
||||
"ios": "11.3",
|
||||
"samsung": "9",
|
||||
"opera_mobile": "47",
|
||||
"electron": "3.0"
|
||||
},
|
||||
"proposal-optional-catch-binding": {
|
||||
"chrome": "66",
|
||||
"opera": "53",
|
||||
"edge": "79",
|
||||
"firefox": "58",
|
||||
"safari": "11.1",
|
||||
"node": "10",
|
||||
"deno": "1",
|
||||
"ios": "11.3",
|
||||
"samsung": "9",
|
||||
"opera_mobile": "47",
|
||||
"electron": "3.0"
|
||||
},
|
||||
"transform-parameters": {
|
||||
"chrome": "49",
|
||||
"opera": "36",
|
||||
"edge": "18",
|
||||
"firefox": "52",
|
||||
"safari": "16.3",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "16.3",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "36",
|
||||
"electron": "0.37"
|
||||
},
|
||||
"transform-async-generator-functions": {
|
||||
"chrome": "63",
|
||||
"opera": "50",
|
||||
"edge": "79",
|
||||
"firefox": "57",
|
||||
"safari": "12",
|
||||
"node": "10",
|
||||
"deno": "1",
|
||||
"ios": "12",
|
||||
"samsung": "8",
|
||||
"opera_mobile": "46",
|
||||
"electron": "3.0"
|
||||
},
|
||||
"proposal-async-generator-functions": {
|
||||
"chrome": "63",
|
||||
"opera": "50",
|
||||
"edge": "79",
|
||||
"firefox": "57",
|
||||
"safari": "12",
|
||||
"node": "10",
|
||||
"deno": "1",
|
||||
"ios": "12",
|
||||
"samsung": "8",
|
||||
"opera_mobile": "46",
|
||||
"electron": "3.0"
|
||||
},
|
||||
"transform-object-rest-spread": {
|
||||
"chrome": "60",
|
||||
"opera": "47",
|
||||
"edge": "79",
|
||||
"firefox": "55",
|
||||
"safari": "11.1",
|
||||
"node": "8.3",
|
||||
"deno": "1",
|
||||
"ios": "11.3",
|
||||
"samsung": "8",
|
||||
"opera_mobile": "44",
|
||||
"electron": "2.0"
|
||||
},
|
||||
"proposal-object-rest-spread": {
|
||||
"chrome": "60",
|
||||
"opera": "47",
|
||||
"edge": "79",
|
||||
"firefox": "55",
|
||||
"safari": "11.1",
|
||||
"node": "8.3",
|
||||
"deno": "1",
|
||||
"ios": "11.3",
|
||||
"samsung": "8",
|
||||
"opera_mobile": "44",
|
||||
"electron": "2.0"
|
||||
},
|
||||
"transform-dotall-regex": {
|
||||
"chrome": "62",
|
||||
"opera": "49",
|
||||
"edge": "79",
|
||||
"firefox": "78",
|
||||
"safari": "11.1",
|
||||
"node": "8.10",
|
||||
"deno": "1",
|
||||
"ios": "11.3",
|
||||
"samsung": "8",
|
||||
"rhino": "1.7.15",
|
||||
"opera_mobile": "46",
|
||||
"electron": "3.0"
|
||||
},
|
||||
"transform-unicode-property-regex": {
|
||||
"chrome": "64",
|
||||
"opera": "51",
|
||||
"edge": "79",
|
||||
"firefox": "78",
|
||||
"safari": "11.1",
|
||||
"node": "10",
|
||||
"deno": "1",
|
||||
"ios": "11.3",
|
||||
"samsung": "9",
|
||||
"opera_mobile": "47",
|
||||
"electron": "3.0"
|
||||
},
|
||||
"proposal-unicode-property-regex": {
|
||||
"chrome": "64",
|
||||
"opera": "51",
|
||||
"edge": "79",
|
||||
"firefox": "78",
|
||||
"safari": "11.1",
|
||||
"node": "10",
|
||||
"deno": "1",
|
||||
"ios": "11.3",
|
||||
"samsung": "9",
|
||||
"opera_mobile": "47",
|
||||
"electron": "3.0"
|
||||
},
|
||||
"transform-named-capturing-groups-regex": {
|
||||
"chrome": "64",
|
||||
"opera": "51",
|
||||
"edge": "79",
|
||||
"firefox": "78",
|
||||
"safari": "11.1",
|
||||
"node": "10",
|
||||
"deno": "1",
|
||||
"ios": "11.3",
|
||||
"samsung": "9",
|
||||
"opera_mobile": "47",
|
||||
"electron": "3.0"
|
||||
},
|
||||
"transform-async-to-generator": {
|
||||
"chrome": "55",
|
||||
"opera": "42",
|
||||
"edge": "15",
|
||||
"firefox": "52",
|
||||
"safari": "11",
|
||||
"node": "7.6",
|
||||
"deno": "1",
|
||||
"ios": "11",
|
||||
"samsung": "6",
|
||||
"opera_mobile": "42",
|
||||
"electron": "1.6"
|
||||
},
|
||||
"transform-exponentiation-operator": {
|
||||
"chrome": "52",
|
||||
"opera": "39",
|
||||
"edge": "14",
|
||||
"firefox": "52",
|
||||
"safari": "10.1",
|
||||
"node": "7",
|
||||
"deno": "1",
|
||||
"ios": "10.3",
|
||||
"samsung": "6",
|
||||
"rhino": "1.7.14",
|
||||
"opera_mobile": "41",
|
||||
"electron": "1.3"
|
||||
},
|
||||
"transform-template-literals": {
|
||||
"chrome": "41",
|
||||
"opera": "28",
|
||||
"edge": "13",
|
||||
"firefox": "34",
|
||||
"safari": "13",
|
||||
"node": "4",
|
||||
"deno": "1",
|
||||
"ios": "13",
|
||||
"samsung": "3.4",
|
||||
"opera_mobile": "28",
|
||||
"electron": "0.21"
|
||||
},
|
||||
"transform-literals": {
|
||||
"chrome": "44",
|
||||
"opera": "31",
|
||||
"edge": "12",
|
||||
"firefox": "53",
|
||||
"safari": "9",
|
||||
"node": "4",
|
||||
"deno": "1",
|
||||
"ios": "9",
|
||||
"samsung": "4",
|
||||
"rhino": "1.7.15",
|
||||
"opera_mobile": "32",
|
||||
"electron": "0.30"
|
||||
},
|
||||
"transform-function-name": {
|
||||
"chrome": "51",
|
||||
"opera": "38",
|
||||
"edge": "79",
|
||||
"firefox": "53",
|
||||
"safari": "10",
|
||||
"node": "6.5",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "41",
|
||||
"electron": "1.2"
|
||||
},
|
||||
"transform-arrow-functions": {
|
||||
"chrome": "47",
|
||||
"opera": "34",
|
||||
"edge": "13",
|
||||
"firefox": "43",
|
||||
"safari": "10",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"rhino": "1.7.13",
|
||||
"opera_mobile": "34",
|
||||
"electron": "0.36"
|
||||
},
|
||||
"transform-block-scoped-functions": {
|
||||
"chrome": "41",
|
||||
"opera": "28",
|
||||
"edge": "12",
|
||||
"firefox": "46",
|
||||
"safari": "10",
|
||||
"node": "4",
|
||||
"deno": "1",
|
||||
"ie": "11",
|
||||
"ios": "10",
|
||||
"samsung": "3.4",
|
||||
"opera_mobile": "28",
|
||||
"electron": "0.21"
|
||||
},
|
||||
"transform-classes": {
|
||||
"chrome": "46",
|
||||
"opera": "33",
|
||||
"edge": "13",
|
||||
"firefox": "45",
|
||||
"safari": "10",
|
||||
"node": "5",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "33",
|
||||
"electron": "0.36"
|
||||
},
|
||||
"transform-object-super": {
|
||||
"chrome": "46",
|
||||
"opera": "33",
|
||||
"edge": "13",
|
||||
"firefox": "45",
|
||||
"safari": "10",
|
||||
"node": "5",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "33",
|
||||
"electron": "0.36"
|
||||
},
|
||||
"transform-shorthand-properties": {
|
||||
"chrome": "43",
|
||||
"opera": "30",
|
||||
"edge": "12",
|
||||
"firefox": "33",
|
||||
"safari": "9",
|
||||
"node": "4",
|
||||
"deno": "1",
|
||||
"ios": "9",
|
||||
"samsung": "4",
|
||||
"rhino": "1.7.14",
|
||||
"opera_mobile": "30",
|
||||
"electron": "0.27"
|
||||
},
|
||||
"transform-duplicate-keys": {
|
||||
"chrome": "42",
|
||||
"opera": "29",
|
||||
"edge": "12",
|
||||
"firefox": "34",
|
||||
"safari": "9",
|
||||
"node": "4",
|
||||
"deno": "1",
|
||||
"ios": "9",
|
||||
"samsung": "3.4",
|
||||
"opera_mobile": "29",
|
||||
"electron": "0.25"
|
||||
},
|
||||
"transform-computed-properties": {
|
||||
"chrome": "44",
|
||||
"opera": "31",
|
||||
"edge": "12",
|
||||
"firefox": "34",
|
||||
"safari": "7.1",
|
||||
"node": "4",
|
||||
"deno": "1",
|
||||
"ios": "8",
|
||||
"samsung": "4",
|
||||
"rhino": "1.8",
|
||||
"opera_mobile": "32",
|
||||
"electron": "0.30"
|
||||
},
|
||||
"transform-for-of": {
|
||||
"chrome": "51",
|
||||
"opera": "38",
|
||||
"edge": "15",
|
||||
"firefox": "53",
|
||||
"safari": "10",
|
||||
"node": "6.5",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "41",
|
||||
"electron": "1.2"
|
||||
},
|
||||
"transform-sticky-regex": {
|
||||
"chrome": "49",
|
||||
"opera": "36",
|
||||
"edge": "13",
|
||||
"firefox": "3",
|
||||
"safari": "10",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"rhino": "1.7.15",
|
||||
"opera_mobile": "36",
|
||||
"electron": "0.37"
|
||||
},
|
||||
"transform-unicode-escapes": {
|
||||
"chrome": "44",
|
||||
"opera": "31",
|
||||
"edge": "12",
|
||||
"firefox": "53",
|
||||
"safari": "9",
|
||||
"node": "4",
|
||||
"deno": "1",
|
||||
"ios": "9",
|
||||
"samsung": "4",
|
||||
"rhino": "1.7.15",
|
||||
"opera_mobile": "32",
|
||||
"electron": "0.30"
|
||||
},
|
||||
"transform-unicode-regex": {
|
||||
"chrome": "50",
|
||||
"opera": "37",
|
||||
"edge": "13",
|
||||
"firefox": "46",
|
||||
"safari": "12",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "12",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "37",
|
||||
"electron": "1.1"
|
||||
},
|
||||
"transform-spread": {
|
||||
"chrome": "46",
|
||||
"opera": "33",
|
||||
"edge": "13",
|
||||
"firefox": "45",
|
||||
"safari": "10",
|
||||
"node": "5",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "33",
|
||||
"electron": "0.36"
|
||||
},
|
||||
"transform-destructuring": {
|
||||
"chrome": "51",
|
||||
"opera": "38",
|
||||
"edge": "15",
|
||||
"firefox": "53",
|
||||
"safari": "10",
|
||||
"node": "6.5",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "41",
|
||||
"electron": "1.2"
|
||||
},
|
||||
"transform-block-scoping": {
|
||||
"chrome": "50",
|
||||
"opera": "37",
|
||||
"edge": "14",
|
||||
"firefox": "53",
|
||||
"safari": "11",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "11",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "37",
|
||||
"electron": "1.1"
|
||||
},
|
||||
"transform-typeof-symbol": {
|
||||
"chrome": "48",
|
||||
"opera": "35",
|
||||
"edge": "12",
|
||||
"firefox": "36",
|
||||
"safari": "9",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "9",
|
||||
"samsung": "5",
|
||||
"rhino": "1.8",
|
||||
"opera_mobile": "35",
|
||||
"electron": "0.37"
|
||||
},
|
||||
"transform-new-target": {
|
||||
"chrome": "46",
|
||||
"opera": "33",
|
||||
"edge": "14",
|
||||
"firefox": "41",
|
||||
"safari": "10",
|
||||
"node": "5",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "33",
|
||||
"electron": "0.36"
|
||||
},
|
||||
"transform-regenerator": {
|
||||
"chrome": "50",
|
||||
"opera": "37",
|
||||
"edge": "13",
|
||||
"firefox": "53",
|
||||
"safari": "10",
|
||||
"node": "6",
|
||||
"deno": "1",
|
||||
"ios": "10",
|
||||
"samsung": "5",
|
||||
"opera_mobile": "37",
|
||||
"electron": "1.1"
|
||||
},
|
||||
"transform-member-expression-literals": {
|
||||
"chrome": "7",
|
||||
"opera": "12",
|
||||
"edge": "12",
|
||||
"firefox": "2",
|
||||
"safari": "5.1",
|
||||
"node": "0.4",
|
||||
"deno": "1",
|
||||
"ie": "9",
|
||||
"android": "4",
|
||||
"ios": "6",
|
||||
"phantom": "1.9",
|
||||
"samsung": "1",
|
||||
"rhino": "1.7.13",
|
||||
"opera_mobile": "12",
|
||||
"electron": "0.20"
|
||||
},
|
||||
"transform-property-literals": {
|
||||
"chrome": "7",
|
||||
"opera": "12",
|
||||
"edge": "12",
|
||||
"firefox": "2",
|
||||
"safari": "5.1",
|
||||
"node": "0.4",
|
||||
"deno": "1",
|
||||
"ie": "9",
|
||||
"android": "4",
|
||||
"ios": "6",
|
||||
"phantom": "1.9",
|
||||
"samsung": "1",
|
||||
"rhino": "1.7.13",
|
||||
"opera_mobile": "12",
|
||||
"electron": "0.20"
|
||||
},
|
||||
"transform-reserved-words": {
|
||||
"chrome": "13",
|
||||
"opera": "10.50",
|
||||
"edge": "12",
|
||||
"firefox": "2",
|
||||
"safari": "3.1",
|
||||
"node": "0.6",
|
||||
"deno": "1",
|
||||
"ie": "9",
|
||||
"android": "4.4",
|
||||
"ios": "6",
|
||||
"phantom": "1.9",
|
||||
"samsung": "1",
|
||||
"rhino": "1.7.13",
|
||||
"opera_mobile": "10.1",
|
||||
"electron": "0.20"
|
||||
},
|
||||
"transform-export-namespace-from": {
|
||||
"chrome": "72",
|
||||
"deno": "1.0",
|
||||
"edge": "79",
|
||||
"firefox": "80",
|
||||
"node": "13.2.0",
|
||||
"opera": "60",
|
||||
"opera_mobile": "51",
|
||||
"safari": "14.1",
|
||||
"ios": "14.5",
|
||||
"samsung": "11.0",
|
||||
"android": "72",
|
||||
"electron": "5.0"
|
||||
},
|
||||
"proposal-export-namespace-from": {
|
||||
"chrome": "72",
|
||||
"deno": "1.0",
|
||||
"edge": "79",
|
||||
"firefox": "80",
|
||||
"node": "13.2.0",
|
||||
"opera": "60",
|
||||
"opera_mobile": "51",
|
||||
"safari": "14.1",
|
||||
"ios": "14.5",
|
||||
"samsung": "11.0",
|
||||
"android": "72",
|
||||
"electron": "5.0"
|
||||
}
|
||||
}
|
||||
2
web/node_modules/@babel/compat-data/native-modules.js
generated
vendored
2
web/node_modules/@babel/compat-data/native-modules.js
generated
vendored
@@ -1,2 +0,0 @@
|
||||
// Todo (Babel 8): remove this file, in Babel 8 users import the .json directly
|
||||
module.exports = require("./data/native-modules.json");
|
||||
2
web/node_modules/@babel/compat-data/overlapping-plugins.js
generated
vendored
2
web/node_modules/@babel/compat-data/overlapping-plugins.js
generated
vendored
@@ -1,2 +0,0 @@
|
||||
// Todo (Babel 8): remove this file, in Babel 8 users import the .json directly
|
||||
module.exports = require("./data/overlapping-plugins.json");
|
||||
40
web/node_modules/@babel/compat-data/package.json
generated
vendored
40
web/node_modules/@babel/compat-data/package.json
generated
vendored
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"name": "@babel/compat-data",
|
||||
"version": "7.28.5",
|
||||
"author": "The Babel Team (https://babel.dev/team)",
|
||||
"license": "MIT",
|
||||
"description": "The compat-data to determine required Babel plugins",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/babel/babel.git",
|
||||
"directory": "packages/babel-compat-data"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"exports": {
|
||||
"./plugins": "./plugins.js",
|
||||
"./native-modules": "./native-modules.js",
|
||||
"./corejs2-built-ins": "./corejs2-built-ins.js",
|
||||
"./corejs3-shipped-proposals": "./corejs3-shipped-proposals.js",
|
||||
"./overlapping-plugins": "./overlapping-plugins.js",
|
||||
"./plugin-bugfixes": "./plugin-bugfixes.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build-data": "./scripts/download-compat-table.sh && node ./scripts/build-data.mjs && node ./scripts/build-modules-support.mjs && node ./scripts/build-bugfixes-targets.mjs"
|
||||
},
|
||||
"keywords": [
|
||||
"babel",
|
||||
"compat-table",
|
||||
"compat-data"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@mdn/browser-compat-data": "^6.0.8",
|
||||
"core-js-compat": "^3.43.0",
|
||||
"electron-to-chromium": "^1.5.140"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"type": "commonjs"
|
||||
}
|
||||
2
web/node_modules/@babel/compat-data/plugin-bugfixes.js
generated
vendored
2
web/node_modules/@babel/compat-data/plugin-bugfixes.js
generated
vendored
@@ -1,2 +0,0 @@
|
||||
// Todo (Babel 8): remove this file, in Babel 8 users import the .json directly
|
||||
module.exports = require("./data/plugin-bugfixes.json");
|
||||
2
web/node_modules/@babel/compat-data/plugins.js
generated
vendored
2
web/node_modules/@babel/compat-data/plugins.js
generated
vendored
@@ -1,2 +0,0 @@
|
||||
// Todo (Babel 8): remove this file, in Babel 8 users import the .json directly
|
||||
module.exports = require("./data/plugins.json");
|
||||
22
web/node_modules/@babel/core/LICENSE
generated
vendored
22
web/node_modules/@babel/core/LICENSE
generated
vendored
@@ -1,22 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2014-present Sebastian McKenzie and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
19
web/node_modules/@babel/core/README.md
generated
vendored
19
web/node_modules/@babel/core/README.md
generated
vendored
@@ -1,19 +0,0 @@
|
||||
# @babel/core
|
||||
|
||||
> Babel compiler core.
|
||||
|
||||
See our website [@babel/core](https://babeljs.io/docs/babel-core) for more information or the [issues](https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A%20core%22+is%3Aopen) associated with this package.
|
||||
|
||||
## Install
|
||||
|
||||
Using npm:
|
||||
|
||||
```sh
|
||||
npm install --save-dev @babel/core
|
||||
```
|
||||
|
||||
or using yarn:
|
||||
|
||||
```sh
|
||||
yarn add @babel/core --dev
|
||||
```
|
||||
5
web/node_modules/@babel/core/lib/config/cache-contexts.js
generated
vendored
5
web/node_modules/@babel/core/lib/config/cache-contexts.js
generated
vendored
@@ -1,5 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
0 && 0;
|
||||
|
||||
//# sourceMappingURL=cache-contexts.js.map
|
||||
1
web/node_modules/@babel/core/lib/config/cache-contexts.js.map
generated
vendored
1
web/node_modules/@babel/core/lib/config/cache-contexts.js.map
generated
vendored
@@ -1 +0,0 @@
|
||||
{"version":3,"names":[],"sources":["../../src/config/cache-contexts.ts"],"sourcesContent":["import type { ConfigContext } from \"./config-chain.ts\";\nimport type {\n CallerMetadata,\n TargetsListOrObject,\n} from \"./validation/options.ts\";\n\nexport type { ConfigContext as FullConfig };\n\nexport type FullPreset = {\n targets: TargetsListOrObject;\n} & ConfigContext;\nexport type FullPlugin = {\n assumptions: { [name: string]: boolean };\n} & FullPreset;\n\n// Context not including filename since it is used in places that cannot\n// process 'ignore'/'only' and other filename-based logic.\nexport type SimpleConfig = {\n envName: string;\n caller: CallerMetadata | undefined;\n};\nexport type SimplePreset = {\n targets: TargetsListOrObject;\n} & SimpleConfig;\nexport type SimplePlugin = {\n assumptions: {\n [name: string]: boolean;\n };\n} & SimplePreset;\n"],"mappings":"","ignoreList":[]}
|
||||
261
web/node_modules/@babel/core/lib/config/caching.js
generated
vendored
261
web/node_modules/@babel/core/lib/config/caching.js
generated
vendored
@@ -1,261 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.assertSimpleType = assertSimpleType;
|
||||
exports.makeStrongCache = makeStrongCache;
|
||||
exports.makeStrongCacheSync = makeStrongCacheSync;
|
||||
exports.makeWeakCache = makeWeakCache;
|
||||
exports.makeWeakCacheSync = makeWeakCacheSync;
|
||||
function _gensync() {
|
||||
const data = require("gensync");
|
||||
_gensync = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
var _async = require("../gensync-utils/async.js");
|
||||
var _util = require("./util.js");
|
||||
const synchronize = gen => {
|
||||
return _gensync()(gen).sync;
|
||||
};
|
||||
function* genTrue() {
|
||||
return true;
|
||||
}
|
||||
function makeWeakCache(handler) {
|
||||
return makeCachedFunction(WeakMap, handler);
|
||||
}
|
||||
function makeWeakCacheSync(handler) {
|
||||
return synchronize(makeWeakCache(handler));
|
||||
}
|
||||
function makeStrongCache(handler) {
|
||||
return makeCachedFunction(Map, handler);
|
||||
}
|
||||
function makeStrongCacheSync(handler) {
|
||||
return synchronize(makeStrongCache(handler));
|
||||
}
|
||||
function makeCachedFunction(CallCache, handler) {
|
||||
const callCacheSync = new CallCache();
|
||||
const callCacheAsync = new CallCache();
|
||||
const futureCache = new CallCache();
|
||||
return function* cachedFunction(arg, data) {
|
||||
const asyncContext = yield* (0, _async.isAsync)();
|
||||
const callCache = asyncContext ? callCacheAsync : callCacheSync;
|
||||
const cached = yield* getCachedValueOrWait(asyncContext, callCache, futureCache, arg, data);
|
||||
if (cached.valid) return cached.value;
|
||||
const cache = new CacheConfigurator(data);
|
||||
const handlerResult = handler(arg, cache);
|
||||
let finishLock;
|
||||
let value;
|
||||
if ((0, _util.isIterableIterator)(handlerResult)) {
|
||||
value = yield* (0, _async.onFirstPause)(handlerResult, () => {
|
||||
finishLock = setupAsyncLocks(cache, futureCache, arg);
|
||||
});
|
||||
} else {
|
||||
value = handlerResult;
|
||||
}
|
||||
updateFunctionCache(callCache, cache, arg, value);
|
||||
if (finishLock) {
|
||||
futureCache.delete(arg);
|
||||
finishLock.release(value);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
}
|
||||
function* getCachedValue(cache, arg, data) {
|
||||
const cachedValue = cache.get(arg);
|
||||
if (cachedValue) {
|
||||
for (const {
|
||||
value,
|
||||
valid
|
||||
} of cachedValue) {
|
||||
if (yield* valid(data)) return {
|
||||
valid: true,
|
||||
value
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
valid: false,
|
||||
value: null
|
||||
};
|
||||
}
|
||||
function* getCachedValueOrWait(asyncContext, callCache, futureCache, arg, data) {
|
||||
const cached = yield* getCachedValue(callCache, arg, data);
|
||||
if (cached.valid) {
|
||||
return cached;
|
||||
}
|
||||
if (asyncContext) {
|
||||
const cached = yield* getCachedValue(futureCache, arg, data);
|
||||
if (cached.valid) {
|
||||
const value = yield* (0, _async.waitFor)(cached.value.promise);
|
||||
return {
|
||||
valid: true,
|
||||
value
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
valid: false,
|
||||
value: null
|
||||
};
|
||||
}
|
||||
function setupAsyncLocks(config, futureCache, arg) {
|
||||
const finishLock = new Lock();
|
||||
updateFunctionCache(futureCache, config, arg, finishLock);
|
||||
return finishLock;
|
||||
}
|
||||
function updateFunctionCache(cache, config, arg, value) {
|
||||
if (!config.configured()) config.forever();
|
||||
let cachedValue = cache.get(arg);
|
||||
config.deactivate();
|
||||
switch (config.mode()) {
|
||||
case "forever":
|
||||
cachedValue = [{
|
||||
value,
|
||||
valid: genTrue
|
||||
}];
|
||||
cache.set(arg, cachedValue);
|
||||
break;
|
||||
case "invalidate":
|
||||
cachedValue = [{
|
||||
value,
|
||||
valid: config.validator()
|
||||
}];
|
||||
cache.set(arg, cachedValue);
|
||||
break;
|
||||
case "valid":
|
||||
if (cachedValue) {
|
||||
cachedValue.push({
|
||||
value,
|
||||
valid: config.validator()
|
||||
});
|
||||
} else {
|
||||
cachedValue = [{
|
||||
value,
|
||||
valid: config.validator()
|
||||
}];
|
||||
cache.set(arg, cachedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
class CacheConfigurator {
|
||||
constructor(data) {
|
||||
this._active = true;
|
||||
this._never = false;
|
||||
this._forever = false;
|
||||
this._invalidate = false;
|
||||
this._configured = false;
|
||||
this._pairs = [];
|
||||
this._data = void 0;
|
||||
this._data = data;
|
||||
}
|
||||
simple() {
|
||||
return makeSimpleConfigurator(this);
|
||||
}
|
||||
mode() {
|
||||
if (this._never) return "never";
|
||||
if (this._forever) return "forever";
|
||||
if (this._invalidate) return "invalidate";
|
||||
return "valid";
|
||||
}
|
||||
forever() {
|
||||
if (!this._active) {
|
||||
throw new Error("Cannot change caching after evaluation has completed.");
|
||||
}
|
||||
if (this._never) {
|
||||
throw new Error("Caching has already been configured with .never()");
|
||||
}
|
||||
this._forever = true;
|
||||
this._configured = true;
|
||||
}
|
||||
never() {
|
||||
if (!this._active) {
|
||||
throw new Error("Cannot change caching after evaluation has completed.");
|
||||
}
|
||||
if (this._forever) {
|
||||
throw new Error("Caching has already been configured with .forever()");
|
||||
}
|
||||
this._never = true;
|
||||
this._configured = true;
|
||||
}
|
||||
using(handler) {
|
||||
if (!this._active) {
|
||||
throw new Error("Cannot change caching after evaluation has completed.");
|
||||
}
|
||||
if (this._never || this._forever) {
|
||||
throw new Error("Caching has already been configured with .never or .forever()");
|
||||
}
|
||||
this._configured = true;
|
||||
const key = handler(this._data);
|
||||
const fn = (0, _async.maybeAsync)(handler, `You appear to be using an async cache handler, but Babel has been called synchronously`);
|
||||
if ((0, _async.isThenable)(key)) {
|
||||
return key.then(key => {
|
||||
this._pairs.push([key, fn]);
|
||||
return key;
|
||||
});
|
||||
}
|
||||
this._pairs.push([key, fn]);
|
||||
return key;
|
||||
}
|
||||
invalidate(handler) {
|
||||
this._invalidate = true;
|
||||
return this.using(handler);
|
||||
}
|
||||
validator() {
|
||||
const pairs = this._pairs;
|
||||
return function* (data) {
|
||||
for (const [key, fn] of pairs) {
|
||||
if (key !== (yield* fn(data))) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
deactivate() {
|
||||
this._active = false;
|
||||
}
|
||||
configured() {
|
||||
return this._configured;
|
||||
}
|
||||
}
|
||||
function makeSimpleConfigurator(cache) {
|
||||
function cacheFn(val) {
|
||||
if (typeof val === "boolean") {
|
||||
if (val) cache.forever();else cache.never();
|
||||
return;
|
||||
}
|
||||
return cache.using(() => assertSimpleType(val()));
|
||||
}
|
||||
cacheFn.forever = () => cache.forever();
|
||||
cacheFn.never = () => cache.never();
|
||||
cacheFn.using = cb => cache.using(() => assertSimpleType(cb()));
|
||||
cacheFn.invalidate = cb => cache.invalidate(() => assertSimpleType(cb()));
|
||||
return cacheFn;
|
||||
}
|
||||
function assertSimpleType(value) {
|
||||
if ((0, _async.isThenable)(value)) {
|
||||
throw new Error(`You appear to be using an async cache handler, ` + `which your current version of Babel does not support. ` + `We may add support for this in the future, ` + `but if you're on the most recent version of @babel/core and still ` + `seeing this error, then you'll need to synchronously handle your caching logic.`);
|
||||
}
|
||||
if (value != null && typeof value !== "string" && typeof value !== "boolean" && typeof value !== "number") {
|
||||
throw new Error("Cache keys must be either string, boolean, number, null, or undefined.");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
class Lock {
|
||||
constructor() {
|
||||
this.released = false;
|
||||
this.promise = void 0;
|
||||
this._resolve = void 0;
|
||||
this.promise = new Promise(resolve => {
|
||||
this._resolve = resolve;
|
||||
});
|
||||
}
|
||||
release(value) {
|
||||
this.released = true;
|
||||
this._resolve(value);
|
||||
}
|
||||
}
|
||||
0 && 0;
|
||||
|
||||
//# sourceMappingURL=caching.js.map
|
||||
1
web/node_modules/@babel/core/lib/config/caching.js.map
generated
vendored
1
web/node_modules/@babel/core/lib/config/caching.js.map
generated
vendored
File diff suppressed because one or more lines are too long
469
web/node_modules/@babel/core/lib/config/config-chain.js
generated
vendored
469
web/node_modules/@babel/core/lib/config/config-chain.js
generated
vendored
@@ -1,469 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.buildPresetChain = buildPresetChain;
|
||||
exports.buildPresetChainWalker = void 0;
|
||||
exports.buildRootChain = buildRootChain;
|
||||
function _path() {
|
||||
const data = require("path");
|
||||
_path = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
function _debug() {
|
||||
const data = require("debug");
|
||||
_debug = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
var _options = require("./validation/options.js");
|
||||
var _patternToRegex = require("./pattern-to-regex.js");
|
||||
var _printer = require("./printer.js");
|
||||
var _rewriteStackTrace = require("../errors/rewrite-stack-trace.js");
|
||||
var _configError = require("../errors/config-error.js");
|
||||
var _index = require("./files/index.js");
|
||||
var _caching = require("./caching.js");
|
||||
var _configDescriptors = require("./config-descriptors.js");
|
||||
const debug = _debug()("babel:config:config-chain");
|
||||
function* buildPresetChain(arg, context) {
|
||||
const chain = yield* buildPresetChainWalker(arg, context);
|
||||
if (!chain) return null;
|
||||
return {
|
||||
plugins: dedupDescriptors(chain.plugins),
|
||||
presets: dedupDescriptors(chain.presets),
|
||||
options: chain.options.map(o => createConfigChainOptions(o)),
|
||||
files: new Set()
|
||||
};
|
||||
}
|
||||
const buildPresetChainWalker = exports.buildPresetChainWalker = makeChainWalker({
|
||||
root: preset => loadPresetDescriptors(preset),
|
||||
env: (preset, envName) => loadPresetEnvDescriptors(preset)(envName),
|
||||
overrides: (preset, index) => loadPresetOverridesDescriptors(preset)(index),
|
||||
overridesEnv: (preset, index, envName) => loadPresetOverridesEnvDescriptors(preset)(index)(envName),
|
||||
createLogger: () => () => {}
|
||||
});
|
||||
const loadPresetDescriptors = (0, _caching.makeWeakCacheSync)(preset => buildRootDescriptors(preset, preset.alias, _configDescriptors.createUncachedDescriptors));
|
||||
const loadPresetEnvDescriptors = (0, _caching.makeWeakCacheSync)(preset => (0, _caching.makeStrongCacheSync)(envName => buildEnvDescriptors(preset, preset.alias, _configDescriptors.createUncachedDescriptors, envName)));
|
||||
const loadPresetOverridesDescriptors = (0, _caching.makeWeakCacheSync)(preset => (0, _caching.makeStrongCacheSync)(index => buildOverrideDescriptors(preset, preset.alias, _configDescriptors.createUncachedDescriptors, index)));
|
||||
const loadPresetOverridesEnvDescriptors = (0, _caching.makeWeakCacheSync)(preset => (0, _caching.makeStrongCacheSync)(index => (0, _caching.makeStrongCacheSync)(envName => buildOverrideEnvDescriptors(preset, preset.alias, _configDescriptors.createUncachedDescriptors, index, envName))));
|
||||
function* buildRootChain(opts, context) {
|
||||
let configReport, babelRcReport;
|
||||
const programmaticLogger = new _printer.ConfigPrinter();
|
||||
const programmaticChain = yield* loadProgrammaticChain({
|
||||
options: opts,
|
||||
dirname: context.cwd
|
||||
}, context, undefined, programmaticLogger);
|
||||
if (!programmaticChain) return null;
|
||||
const programmaticReport = yield* programmaticLogger.output();
|
||||
let configFile;
|
||||
if (typeof opts.configFile === "string") {
|
||||
configFile = yield* (0, _index.loadConfig)(opts.configFile, context.cwd, context.envName, context.caller);
|
||||
} else if (opts.configFile !== false) {
|
||||
configFile = yield* (0, _index.findRootConfig)(context.root, context.envName, context.caller);
|
||||
}
|
||||
let {
|
||||
babelrc,
|
||||
babelrcRoots
|
||||
} = opts;
|
||||
let babelrcRootsDirectory = context.cwd;
|
||||
const configFileChain = emptyChain();
|
||||
const configFileLogger = new _printer.ConfigPrinter();
|
||||
if (configFile) {
|
||||
const validatedFile = validateConfigFile(configFile);
|
||||
const result = yield* loadFileChain(validatedFile, context, undefined, configFileLogger);
|
||||
if (!result) return null;
|
||||
configReport = yield* configFileLogger.output();
|
||||
if (babelrc === undefined) {
|
||||
babelrc = validatedFile.options.babelrc;
|
||||
}
|
||||
if (babelrcRoots === undefined) {
|
||||
babelrcRootsDirectory = validatedFile.dirname;
|
||||
babelrcRoots = validatedFile.options.babelrcRoots;
|
||||
}
|
||||
mergeChain(configFileChain, result);
|
||||
}
|
||||
let ignoreFile, babelrcFile;
|
||||
let isIgnored = false;
|
||||
const fileChain = emptyChain();
|
||||
if ((babelrc === true || babelrc === undefined) && typeof context.filename === "string") {
|
||||
const pkgData = yield* (0, _index.findPackageData)(context.filename);
|
||||
if (pkgData && babelrcLoadEnabled(context, pkgData, babelrcRoots, babelrcRootsDirectory)) {
|
||||
({
|
||||
ignore: ignoreFile,
|
||||
config: babelrcFile
|
||||
} = yield* (0, _index.findRelativeConfig)(pkgData, context.envName, context.caller));
|
||||
if (ignoreFile) {
|
||||
fileChain.files.add(ignoreFile.filepath);
|
||||
}
|
||||
if (ignoreFile && shouldIgnore(context, ignoreFile.ignore, null, ignoreFile.dirname)) {
|
||||
isIgnored = true;
|
||||
}
|
||||
if (babelrcFile && !isIgnored) {
|
||||
const validatedFile = validateBabelrcFile(babelrcFile);
|
||||
const babelrcLogger = new _printer.ConfigPrinter();
|
||||
const result = yield* loadFileChain(validatedFile, context, undefined, babelrcLogger);
|
||||
if (!result) {
|
||||
isIgnored = true;
|
||||
} else {
|
||||
babelRcReport = yield* babelrcLogger.output();
|
||||
mergeChain(fileChain, result);
|
||||
}
|
||||
}
|
||||
if (babelrcFile && isIgnored) {
|
||||
fileChain.files.add(babelrcFile.filepath);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (context.showConfig) {
|
||||
console.log(`Babel configs on "${context.filename}" (ascending priority):\n` + [configReport, babelRcReport, programmaticReport].filter(x => !!x).join("\n\n") + "\n-----End Babel configs-----");
|
||||
}
|
||||
const chain = mergeChain(mergeChain(mergeChain(emptyChain(), configFileChain), fileChain), programmaticChain);
|
||||
return {
|
||||
plugins: isIgnored ? [] : dedupDescriptors(chain.plugins),
|
||||
presets: isIgnored ? [] : dedupDescriptors(chain.presets),
|
||||
options: isIgnored ? [] : chain.options.map(o => createConfigChainOptions(o)),
|
||||
fileHandling: isIgnored ? "ignored" : "transpile",
|
||||
ignore: ignoreFile || undefined,
|
||||
babelrc: babelrcFile || undefined,
|
||||
config: configFile || undefined,
|
||||
files: chain.files
|
||||
};
|
||||
}
|
||||
function babelrcLoadEnabled(context, pkgData, babelrcRoots, babelrcRootsDirectory) {
|
||||
if (typeof babelrcRoots === "boolean") return babelrcRoots;
|
||||
const absoluteRoot = context.root;
|
||||
if (babelrcRoots === undefined) {
|
||||
return pkgData.directories.includes(absoluteRoot);
|
||||
}
|
||||
let babelrcPatterns = babelrcRoots;
|
||||
if (!Array.isArray(babelrcPatterns)) {
|
||||
babelrcPatterns = [babelrcPatterns];
|
||||
}
|
||||
babelrcPatterns = babelrcPatterns.map(pat => {
|
||||
return typeof pat === "string" ? _path().resolve(babelrcRootsDirectory, pat) : pat;
|
||||
});
|
||||
if (babelrcPatterns.length === 1 && babelrcPatterns[0] === absoluteRoot) {
|
||||
return pkgData.directories.includes(absoluteRoot);
|
||||
}
|
||||
return babelrcPatterns.some(pat => {
|
||||
if (typeof pat === "string") {
|
||||
pat = (0, _patternToRegex.default)(pat, babelrcRootsDirectory);
|
||||
}
|
||||
return pkgData.directories.some(directory => {
|
||||
return matchPattern(pat, babelrcRootsDirectory, directory, context);
|
||||
});
|
||||
});
|
||||
}
|
||||
const validateConfigFile = (0, _caching.makeWeakCacheSync)(file => ({
|
||||
filepath: file.filepath,
|
||||
dirname: file.dirname,
|
||||
options: (0, _options.validate)("configfile", file.options, file.filepath)
|
||||
}));
|
||||
const validateBabelrcFile = (0, _caching.makeWeakCacheSync)(file => ({
|
||||
filepath: file.filepath,
|
||||
dirname: file.dirname,
|
||||
options: (0, _options.validate)("babelrcfile", file.options, file.filepath)
|
||||
}));
|
||||
const validateExtendFile = (0, _caching.makeWeakCacheSync)(file => ({
|
||||
filepath: file.filepath,
|
||||
dirname: file.dirname,
|
||||
options: (0, _options.validate)("extendsfile", file.options, file.filepath)
|
||||
}));
|
||||
const loadProgrammaticChain = makeChainWalker({
|
||||
root: input => buildRootDescriptors(input, "base", _configDescriptors.createCachedDescriptors),
|
||||
env: (input, envName) => buildEnvDescriptors(input, "base", _configDescriptors.createCachedDescriptors, envName),
|
||||
overrides: (input, index) => buildOverrideDescriptors(input, "base", _configDescriptors.createCachedDescriptors, index),
|
||||
overridesEnv: (input, index, envName) => buildOverrideEnvDescriptors(input, "base", _configDescriptors.createCachedDescriptors, index, envName),
|
||||
createLogger: (input, context, baseLogger) => buildProgrammaticLogger(input, context, baseLogger)
|
||||
});
|
||||
const loadFileChainWalker = makeChainWalker({
|
||||
root: file => loadFileDescriptors(file),
|
||||
env: (file, envName) => loadFileEnvDescriptors(file)(envName),
|
||||
overrides: (file, index) => loadFileOverridesDescriptors(file)(index),
|
||||
overridesEnv: (file, index, envName) => loadFileOverridesEnvDescriptors(file)(index)(envName),
|
||||
createLogger: (file, context, baseLogger) => buildFileLogger(file.filepath, context, baseLogger)
|
||||
});
|
||||
function* loadFileChain(input, context, files, baseLogger) {
|
||||
const chain = yield* loadFileChainWalker(input, context, files, baseLogger);
|
||||
chain == null || chain.files.add(input.filepath);
|
||||
return chain;
|
||||
}
|
||||
const loadFileDescriptors = (0, _caching.makeWeakCacheSync)(file => buildRootDescriptors(file, file.filepath, _configDescriptors.createUncachedDescriptors));
|
||||
const loadFileEnvDescriptors = (0, _caching.makeWeakCacheSync)(file => (0, _caching.makeStrongCacheSync)(envName => buildEnvDescriptors(file, file.filepath, _configDescriptors.createUncachedDescriptors, envName)));
|
||||
const loadFileOverridesDescriptors = (0, _caching.makeWeakCacheSync)(file => (0, _caching.makeStrongCacheSync)(index => buildOverrideDescriptors(file, file.filepath, _configDescriptors.createUncachedDescriptors, index)));
|
||||
const loadFileOverridesEnvDescriptors = (0, _caching.makeWeakCacheSync)(file => (0, _caching.makeStrongCacheSync)(index => (0, _caching.makeStrongCacheSync)(envName => buildOverrideEnvDescriptors(file, file.filepath, _configDescriptors.createUncachedDescriptors, index, envName))));
|
||||
function buildFileLogger(filepath, context, baseLogger) {
|
||||
if (!baseLogger) {
|
||||
return () => {};
|
||||
}
|
||||
return baseLogger.configure(context.showConfig, _printer.ChainFormatter.Config, {
|
||||
filepath
|
||||
});
|
||||
}
|
||||
function buildRootDescriptors({
|
||||
dirname,
|
||||
options
|
||||
}, alias, descriptors) {
|
||||
return descriptors(dirname, options, alias);
|
||||
}
|
||||
function buildProgrammaticLogger(_, context, baseLogger) {
|
||||
var _context$caller;
|
||||
if (!baseLogger) {
|
||||
return () => {};
|
||||
}
|
||||
return baseLogger.configure(context.showConfig, _printer.ChainFormatter.Programmatic, {
|
||||
callerName: (_context$caller = context.caller) == null ? void 0 : _context$caller.name
|
||||
});
|
||||
}
|
||||
function buildEnvDescriptors({
|
||||
dirname,
|
||||
options
|
||||
}, alias, descriptors, envName) {
|
||||
var _options$env;
|
||||
const opts = (_options$env = options.env) == null ? void 0 : _options$env[envName];
|
||||
return opts ? descriptors(dirname, opts, `${alias}.env["${envName}"]`) : null;
|
||||
}
|
||||
function buildOverrideDescriptors({
|
||||
dirname,
|
||||
options
|
||||
}, alias, descriptors, index) {
|
||||
var _options$overrides;
|
||||
const opts = (_options$overrides = options.overrides) == null ? void 0 : _options$overrides[index];
|
||||
if (!opts) throw new Error("Assertion failure - missing override");
|
||||
return descriptors(dirname, opts, `${alias}.overrides[${index}]`);
|
||||
}
|
||||
function buildOverrideEnvDescriptors({
|
||||
dirname,
|
||||
options
|
||||
}, alias, descriptors, index, envName) {
|
||||
var _options$overrides2, _override$env;
|
||||
const override = (_options$overrides2 = options.overrides) == null ? void 0 : _options$overrides2[index];
|
||||
if (!override) throw new Error("Assertion failure - missing override");
|
||||
const opts = (_override$env = override.env) == null ? void 0 : _override$env[envName];
|
||||
return opts ? descriptors(dirname, opts, `${alias}.overrides[${index}].env["${envName}"]`) : null;
|
||||
}
|
||||
function makeChainWalker({
|
||||
root,
|
||||
env,
|
||||
overrides,
|
||||
overridesEnv,
|
||||
createLogger
|
||||
}) {
|
||||
return function* chainWalker(input, context, files = new Set(), baseLogger) {
|
||||
const {
|
||||
dirname
|
||||
} = input;
|
||||
const flattenedConfigs = [];
|
||||
const rootOpts = root(input);
|
||||
if (configIsApplicable(rootOpts, dirname, context, input.filepath)) {
|
||||
flattenedConfigs.push({
|
||||
config: rootOpts,
|
||||
envName: undefined,
|
||||
index: undefined
|
||||
});
|
||||
const envOpts = env(input, context.envName);
|
||||
if (envOpts && configIsApplicable(envOpts, dirname, context, input.filepath)) {
|
||||
flattenedConfigs.push({
|
||||
config: envOpts,
|
||||
envName: context.envName,
|
||||
index: undefined
|
||||
});
|
||||
}
|
||||
(rootOpts.options.overrides || []).forEach((_, index) => {
|
||||
const overrideOps = overrides(input, index);
|
||||
if (configIsApplicable(overrideOps, dirname, context, input.filepath)) {
|
||||
flattenedConfigs.push({
|
||||
config: overrideOps,
|
||||
index,
|
||||
envName: undefined
|
||||
});
|
||||
const overrideEnvOpts = overridesEnv(input, index, context.envName);
|
||||
if (overrideEnvOpts && configIsApplicable(overrideEnvOpts, dirname, context, input.filepath)) {
|
||||
flattenedConfigs.push({
|
||||
config: overrideEnvOpts,
|
||||
index,
|
||||
envName: context.envName
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (flattenedConfigs.some(({
|
||||
config: {
|
||||
options: {
|
||||
ignore,
|
||||
only
|
||||
}
|
||||
}
|
||||
}) => shouldIgnore(context, ignore, only, dirname))) {
|
||||
return null;
|
||||
}
|
||||
const chain = emptyChain();
|
||||
const logger = createLogger(input, context, baseLogger);
|
||||
for (const {
|
||||
config,
|
||||
index,
|
||||
envName
|
||||
} of flattenedConfigs) {
|
||||
if (!(yield* mergeExtendsChain(chain, config.options, dirname, context, files, baseLogger))) {
|
||||
return null;
|
||||
}
|
||||
logger(config, index, envName);
|
||||
yield* mergeChainOpts(chain, config);
|
||||
}
|
||||
return chain;
|
||||
};
|
||||
}
|
||||
function* mergeExtendsChain(chain, opts, dirname, context, files, baseLogger) {
|
||||
if (opts.extends === undefined) return true;
|
||||
const file = yield* (0, _index.loadConfig)(opts.extends, dirname, context.envName, context.caller);
|
||||
if (files.has(file)) {
|
||||
throw new Error(`Configuration cycle detected loading ${file.filepath}.\n` + `File already loaded following the config chain:\n` + Array.from(files, file => ` - ${file.filepath}`).join("\n"));
|
||||
}
|
||||
files.add(file);
|
||||
const fileChain = yield* loadFileChain(validateExtendFile(file), context, files, baseLogger);
|
||||
files.delete(file);
|
||||
if (!fileChain) return false;
|
||||
mergeChain(chain, fileChain);
|
||||
return true;
|
||||
}
|
||||
function mergeChain(target, source) {
|
||||
target.options.push(...source.options);
|
||||
target.plugins.push(...source.plugins);
|
||||
target.presets.push(...source.presets);
|
||||
for (const file of source.files) {
|
||||
target.files.add(file);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
function* mergeChainOpts(target, {
|
||||
options,
|
||||
plugins,
|
||||
presets
|
||||
}) {
|
||||
target.options.push(options);
|
||||
target.plugins.push(...(yield* plugins()));
|
||||
target.presets.push(...(yield* presets()));
|
||||
return target;
|
||||
}
|
||||
function emptyChain() {
|
||||
return {
|
||||
options: [],
|
||||
presets: [],
|
||||
plugins: [],
|
||||
files: new Set()
|
||||
};
|
||||
}
|
||||
function createConfigChainOptions(opts) {
|
||||
const options = Object.assign({}, opts);
|
||||
delete options.extends;
|
||||
delete options.env;
|
||||
delete options.overrides;
|
||||
delete options.plugins;
|
||||
delete options.presets;
|
||||
delete options.passPerPreset;
|
||||
delete options.ignore;
|
||||
delete options.only;
|
||||
delete options.test;
|
||||
delete options.include;
|
||||
delete options.exclude;
|
||||
if (hasOwnProperty.call(options, "sourceMap")) {
|
||||
options.sourceMaps = options.sourceMap;
|
||||
delete options.sourceMap;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
function dedupDescriptors(items) {
|
||||
const map = new Map();
|
||||
const descriptors = [];
|
||||
for (const item of items) {
|
||||
if (typeof item.value === "function") {
|
||||
const fnKey = item.value;
|
||||
let nameMap = map.get(fnKey);
|
||||
if (!nameMap) {
|
||||
nameMap = new Map();
|
||||
map.set(fnKey, nameMap);
|
||||
}
|
||||
let desc = nameMap.get(item.name);
|
||||
if (!desc) {
|
||||
desc = {
|
||||
value: item
|
||||
};
|
||||
descriptors.push(desc);
|
||||
if (!item.ownPass) nameMap.set(item.name, desc);
|
||||
} else {
|
||||
desc.value = item;
|
||||
}
|
||||
} else {
|
||||
descriptors.push({
|
||||
value: item
|
||||
});
|
||||
}
|
||||
}
|
||||
return descriptors.reduce((acc, desc) => {
|
||||
acc.push(desc.value);
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
function configIsApplicable({
|
||||
options
|
||||
}, dirname, context, configName) {
|
||||
return (options.test === undefined || configFieldIsApplicable(context, options.test, dirname, configName)) && (options.include === undefined || configFieldIsApplicable(context, options.include, dirname, configName)) && (options.exclude === undefined || !configFieldIsApplicable(context, options.exclude, dirname, configName));
|
||||
}
|
||||
function configFieldIsApplicable(context, test, dirname, configName) {
|
||||
const patterns = Array.isArray(test) ? test : [test];
|
||||
return matchesPatterns(context, patterns, dirname, configName);
|
||||
}
|
||||
function ignoreListReplacer(_key, value) {
|
||||
if (value instanceof RegExp) {
|
||||
return String(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function shouldIgnore(context, ignore, only, dirname) {
|
||||
if (ignore && matchesPatterns(context, ignore, dirname)) {
|
||||
var _context$filename;
|
||||
const message = `No config is applied to "${(_context$filename = context.filename) != null ? _context$filename : "(unknown)"}" because it matches one of \`ignore: ${JSON.stringify(ignore, ignoreListReplacer)}\` from "${dirname}"`;
|
||||
debug(message);
|
||||
if (context.showConfig) {
|
||||
console.log(message);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (only && !matchesPatterns(context, only, dirname)) {
|
||||
var _context$filename2;
|
||||
const message = `No config is applied to "${(_context$filename2 = context.filename) != null ? _context$filename2 : "(unknown)"}" because it fails to match one of \`only: ${JSON.stringify(only, ignoreListReplacer)}\` from "${dirname}"`;
|
||||
debug(message);
|
||||
if (context.showConfig) {
|
||||
console.log(message);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function matchesPatterns(context, patterns, dirname, configName) {
|
||||
return patterns.some(pattern => matchPattern(pattern, dirname, context.filename, context, configName));
|
||||
}
|
||||
function matchPattern(pattern, dirname, pathToTest, context, configName) {
|
||||
if (typeof pattern === "function") {
|
||||
return !!(0, _rewriteStackTrace.endHiddenCallStack)(pattern)(pathToTest, {
|
||||
dirname,
|
||||
envName: context.envName,
|
||||
caller: context.caller
|
||||
});
|
||||
}
|
||||
if (typeof pathToTest !== "string") {
|
||||
throw new _configError.default(`Configuration contains string/RegExp pattern, but no filename was passed to Babel`, configName);
|
||||
}
|
||||
if (typeof pattern === "string") {
|
||||
pattern = (0, _patternToRegex.default)(pattern, dirname);
|
||||
}
|
||||
return pattern.test(pathToTest);
|
||||
}
|
||||
0 && 0;
|
||||
|
||||
//# sourceMappingURL=config-chain.js.map
|
||||
1
web/node_modules/@babel/core/lib/config/config-chain.js.map
generated
vendored
1
web/node_modules/@babel/core/lib/config/config-chain.js.map
generated
vendored
File diff suppressed because one or more lines are too long
190
web/node_modules/@babel/core/lib/config/config-descriptors.js
generated
vendored
190
web/node_modules/@babel/core/lib/config/config-descriptors.js
generated
vendored
@@ -1,190 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.createCachedDescriptors = createCachedDescriptors;
|
||||
exports.createDescriptor = createDescriptor;
|
||||
exports.createUncachedDescriptors = createUncachedDescriptors;
|
||||
function _gensync() {
|
||||
const data = require("gensync");
|
||||
_gensync = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
var _functional = require("../gensync-utils/functional.js");
|
||||
var _index = require("./files/index.js");
|
||||
var _item = require("./item.js");
|
||||
var _caching = require("./caching.js");
|
||||
var _resolveTargets = require("./resolve-targets.js");
|
||||
function isEqualDescriptor(a, b) {
|
||||
var _a$file, _b$file, _a$file2, _b$file2;
|
||||
return a.name === b.name && a.value === b.value && a.options === b.options && a.dirname === b.dirname && a.alias === b.alias && a.ownPass === b.ownPass && ((_a$file = a.file) == null ? void 0 : _a$file.request) === ((_b$file = b.file) == null ? void 0 : _b$file.request) && ((_a$file2 = a.file) == null ? void 0 : _a$file2.resolved) === ((_b$file2 = b.file) == null ? void 0 : _b$file2.resolved);
|
||||
}
|
||||
function* handlerOf(value) {
|
||||
return value;
|
||||
}
|
||||
function optionsWithResolvedBrowserslistConfigFile(options, dirname) {
|
||||
if (typeof options.browserslistConfigFile === "string") {
|
||||
options.browserslistConfigFile = (0, _resolveTargets.resolveBrowserslistConfigFile)(options.browserslistConfigFile, dirname);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
function createCachedDescriptors(dirname, options, alias) {
|
||||
const {
|
||||
plugins,
|
||||
presets,
|
||||
passPerPreset
|
||||
} = options;
|
||||
return {
|
||||
options: optionsWithResolvedBrowserslistConfigFile(options, dirname),
|
||||
plugins: plugins ? () => createCachedPluginDescriptors(plugins, dirname)(alias) : () => handlerOf([]),
|
||||
presets: presets ? () => createCachedPresetDescriptors(presets, dirname)(alias)(!!passPerPreset) : () => handlerOf([])
|
||||
};
|
||||
}
|
||||
function createUncachedDescriptors(dirname, options, alias) {
|
||||
return {
|
||||
options: optionsWithResolvedBrowserslistConfigFile(options, dirname),
|
||||
plugins: (0, _functional.once)(() => createPluginDescriptors(options.plugins || [], dirname, alias)),
|
||||
presets: (0, _functional.once)(() => createPresetDescriptors(options.presets || [], dirname, alias, !!options.passPerPreset))
|
||||
};
|
||||
}
|
||||
const PRESET_DESCRIPTOR_CACHE = new WeakMap();
|
||||
const createCachedPresetDescriptors = (0, _caching.makeWeakCacheSync)((items, cache) => {
|
||||
const dirname = cache.using(dir => dir);
|
||||
return (0, _caching.makeStrongCacheSync)(alias => (0, _caching.makeStrongCache)(function* (passPerPreset) {
|
||||
const descriptors = yield* createPresetDescriptors(items, dirname, alias, passPerPreset);
|
||||
return descriptors.map(desc => loadCachedDescriptor(PRESET_DESCRIPTOR_CACHE, desc));
|
||||
}));
|
||||
});
|
||||
const PLUGIN_DESCRIPTOR_CACHE = new WeakMap();
|
||||
const createCachedPluginDescriptors = (0, _caching.makeWeakCacheSync)((items, cache) => {
|
||||
const dirname = cache.using(dir => dir);
|
||||
return (0, _caching.makeStrongCache)(function* (alias) {
|
||||
const descriptors = yield* createPluginDescriptors(items, dirname, alias);
|
||||
return descriptors.map(desc => loadCachedDescriptor(PLUGIN_DESCRIPTOR_CACHE, desc));
|
||||
});
|
||||
});
|
||||
const DEFAULT_OPTIONS = {};
|
||||
function loadCachedDescriptor(cache, desc) {
|
||||
const {
|
||||
value,
|
||||
options = DEFAULT_OPTIONS
|
||||
} = desc;
|
||||
if (options === false) return desc;
|
||||
let cacheByOptions = cache.get(value);
|
||||
if (!cacheByOptions) {
|
||||
cacheByOptions = new WeakMap();
|
||||
cache.set(value, cacheByOptions);
|
||||
}
|
||||
let possibilities = cacheByOptions.get(options);
|
||||
if (!possibilities) {
|
||||
possibilities = [];
|
||||
cacheByOptions.set(options, possibilities);
|
||||
}
|
||||
if (!possibilities.includes(desc)) {
|
||||
const matches = possibilities.filter(possibility => isEqualDescriptor(possibility, desc));
|
||||
if (matches.length > 0) {
|
||||
return matches[0];
|
||||
}
|
||||
possibilities.push(desc);
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
function* createPresetDescriptors(items, dirname, alias, passPerPreset) {
|
||||
return yield* createDescriptors("preset", items, dirname, alias, passPerPreset);
|
||||
}
|
||||
function* createPluginDescriptors(items, dirname, alias) {
|
||||
return yield* createDescriptors("plugin", items, dirname, alias);
|
||||
}
|
||||
function* createDescriptors(type, items, dirname, alias, ownPass) {
|
||||
const descriptors = yield* _gensync().all(items.map((item, index) => createDescriptor(item, dirname, {
|
||||
type,
|
||||
alias: `${alias}$${index}`,
|
||||
ownPass: !!ownPass
|
||||
})));
|
||||
assertNoDuplicates(descriptors);
|
||||
return descriptors;
|
||||
}
|
||||
function* createDescriptor(pair, dirname, {
|
||||
type,
|
||||
alias,
|
||||
ownPass
|
||||
}) {
|
||||
const desc = (0, _item.getItemDescriptor)(pair);
|
||||
if (desc) {
|
||||
return desc;
|
||||
}
|
||||
let name;
|
||||
let options;
|
||||
let value = pair;
|
||||
if (Array.isArray(value)) {
|
||||
if (value.length === 3) {
|
||||
[value, options, name] = value;
|
||||
} else {
|
||||
[value, options] = value;
|
||||
}
|
||||
}
|
||||
let file = undefined;
|
||||
let filepath = null;
|
||||
if (typeof value === "string") {
|
||||
if (typeof type !== "string") {
|
||||
throw new Error("To resolve a string-based item, the type of item must be given");
|
||||
}
|
||||
const resolver = type === "plugin" ? _index.loadPlugin : _index.loadPreset;
|
||||
const request = value;
|
||||
({
|
||||
filepath,
|
||||
value
|
||||
} = yield* resolver(value, dirname));
|
||||
file = {
|
||||
request,
|
||||
resolved: filepath
|
||||
};
|
||||
}
|
||||
if (!value) {
|
||||
throw new Error(`Unexpected falsy value: ${String(value)}`);
|
||||
}
|
||||
if (typeof value === "object" && value.__esModule) {
|
||||
if (value.default) {
|
||||
value = value.default;
|
||||
} else {
|
||||
throw new Error("Must export a default export when using ES6 modules.");
|
||||
}
|
||||
}
|
||||
if (typeof value !== "object" && typeof value !== "function") {
|
||||
throw new Error(`Unsupported format: ${typeof value}. Expected an object or a function.`);
|
||||
}
|
||||
if (filepath !== null && typeof value === "object" && value) {
|
||||
throw new Error(`Plugin/Preset files are not allowed to export objects, only functions. In ${filepath}`);
|
||||
}
|
||||
return {
|
||||
name,
|
||||
alias: filepath || alias,
|
||||
value,
|
||||
options,
|
||||
dirname,
|
||||
ownPass,
|
||||
file
|
||||
};
|
||||
}
|
||||
function assertNoDuplicates(items) {
|
||||
const map = new Map();
|
||||
for (const item of items) {
|
||||
if (typeof item.value !== "function") continue;
|
||||
let nameMap = map.get(item.value);
|
||||
if (!nameMap) {
|
||||
nameMap = new Set();
|
||||
map.set(item.value, nameMap);
|
||||
}
|
||||
if (nameMap.has(item.name)) {
|
||||
const conflicts = items.filter(i => i.value === item.value);
|
||||
throw new Error([`Duplicate plugin/preset detected.`, `If you'd like to use two separate instances of a plugin,`, `they need separate names, e.g.`, ``, ` plugins: [`, ` ['some-plugin', {}],`, ` ['some-plugin', {}, 'some unique name'],`, ` ]`, ``, `Duplicates detected are:`, `${JSON.stringify(conflicts, null, 2)}`].join("\n"));
|
||||
}
|
||||
nameMap.add(item.name);
|
||||
}
|
||||
}
|
||||
0 && 0;
|
||||
|
||||
//# sourceMappingURL=config-descriptors.js.map
|
||||
1
web/node_modules/@babel/core/lib/config/config-descriptors.js.map
generated
vendored
1
web/node_modules/@babel/core/lib/config/config-descriptors.js.map
generated
vendored
File diff suppressed because one or more lines are too long
290
web/node_modules/@babel/core/lib/config/files/configuration.js
generated
vendored
290
web/node_modules/@babel/core/lib/config/files/configuration.js
generated
vendored
@@ -1,290 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.ROOT_CONFIG_FILENAMES = void 0;
|
||||
exports.findConfigUpwards = findConfigUpwards;
|
||||
exports.findRelativeConfig = findRelativeConfig;
|
||||
exports.findRootConfig = findRootConfig;
|
||||
exports.loadConfig = loadConfig;
|
||||
exports.resolveShowConfigPath = resolveShowConfigPath;
|
||||
function _debug() {
|
||||
const data = require("debug");
|
||||
_debug = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
function _fs() {
|
||||
const data = require("fs");
|
||||
_fs = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
function _path() {
|
||||
const data = require("path");
|
||||
_path = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
function _json() {
|
||||
const data = require("json5");
|
||||
_json = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
function _gensync() {
|
||||
const data = require("gensync");
|
||||
_gensync = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
var _caching = require("../caching.js");
|
||||
var _configApi = require("../helpers/config-api.js");
|
||||
var _utils = require("./utils.js");
|
||||
var _moduleTypes = require("./module-types.js");
|
||||
var _patternToRegex = require("../pattern-to-regex.js");
|
||||
var _configError = require("../../errors/config-error.js");
|
||||
var fs = require("../../gensync-utils/fs.js");
|
||||
require("module");
|
||||
var _rewriteStackTrace = require("../../errors/rewrite-stack-trace.js");
|
||||
var _async = require("../../gensync-utils/async.js");
|
||||
const debug = _debug()("babel:config:loading:files:configuration");
|
||||
const ROOT_CONFIG_FILENAMES = exports.ROOT_CONFIG_FILENAMES = ["babel.config.js", "babel.config.cjs", "babel.config.mjs", "babel.config.json", "babel.config.cts", "babel.config.ts", "babel.config.mts"];
|
||||
const RELATIVE_CONFIG_FILENAMES = [".babelrc", ".babelrc.js", ".babelrc.cjs", ".babelrc.mjs", ".babelrc.json", ".babelrc.cts"];
|
||||
const BABELIGNORE_FILENAME = ".babelignore";
|
||||
const runConfig = (0, _caching.makeWeakCache)(function* runConfig(options, cache) {
|
||||
yield* [];
|
||||
return {
|
||||
options: (0, _rewriteStackTrace.endHiddenCallStack)(options)((0, _configApi.makeConfigAPI)(cache)),
|
||||
cacheNeedsConfiguration: !cache.configured()
|
||||
};
|
||||
});
|
||||
function* readConfigCode(filepath, data) {
|
||||
if (!_fs().existsSync(filepath)) return null;
|
||||
let options = yield* (0, _moduleTypes.default)(filepath, (yield* (0, _async.isAsync)()) ? "auto" : "require", "You appear to be using a native ECMAScript module configuration " + "file, which is only supported when running Babel asynchronously " + "or when using the Node.js `--experimental-require-module` flag.", "You appear to be using a configuration file that contains top-level " + "await, which is only supported when running Babel asynchronously.");
|
||||
let cacheNeedsConfiguration = false;
|
||||
if (typeof options === "function") {
|
||||
({
|
||||
options,
|
||||
cacheNeedsConfiguration
|
||||
} = yield* runConfig(options, data));
|
||||
}
|
||||
if (!options || typeof options !== "object" || Array.isArray(options)) {
|
||||
throw new _configError.default(`Configuration should be an exported JavaScript object.`, filepath);
|
||||
}
|
||||
if (typeof options.then === "function") {
|
||||
options.catch == null || options.catch(() => {});
|
||||
throw new _configError.default(`You appear to be using an async configuration, ` + `which your current version of Babel does not support. ` + `We may add support for this in the future, ` + `but if you're on the most recent version of @babel/core and still ` + `seeing this error, then you'll need to synchronously return your config.`, filepath);
|
||||
}
|
||||
if (cacheNeedsConfiguration) throwConfigError(filepath);
|
||||
return buildConfigFileObject(options, filepath);
|
||||
}
|
||||
const cfboaf = new WeakMap();
|
||||
function buildConfigFileObject(options, filepath) {
|
||||
let configFilesByFilepath = cfboaf.get(options);
|
||||
if (!configFilesByFilepath) {
|
||||
cfboaf.set(options, configFilesByFilepath = new Map());
|
||||
}
|
||||
let configFile = configFilesByFilepath.get(filepath);
|
||||
if (!configFile) {
|
||||
configFile = {
|
||||
filepath,
|
||||
dirname: _path().dirname(filepath),
|
||||
options
|
||||
};
|
||||
configFilesByFilepath.set(filepath, configFile);
|
||||
}
|
||||
return configFile;
|
||||
}
|
||||
const packageToBabelConfig = (0, _caching.makeWeakCacheSync)(file => {
|
||||
const babel = file.options.babel;
|
||||
if (babel === undefined) return null;
|
||||
if (typeof babel !== "object" || Array.isArray(babel) || babel === null) {
|
||||
throw new _configError.default(`.babel property must be an object`, file.filepath);
|
||||
}
|
||||
return {
|
||||
filepath: file.filepath,
|
||||
dirname: file.dirname,
|
||||
options: babel
|
||||
};
|
||||
});
|
||||
const readConfigJSON5 = (0, _utils.makeStaticFileCache)((filepath, content) => {
|
||||
let options;
|
||||
try {
|
||||
options = _json().parse(content);
|
||||
} catch (err) {
|
||||
throw new _configError.default(`Error while parsing config - ${err.message}`, filepath);
|
||||
}
|
||||
if (!options) throw new _configError.default(`No config detected`, filepath);
|
||||
if (typeof options !== "object") {
|
||||
throw new _configError.default(`Config returned typeof ${typeof options}`, filepath);
|
||||
}
|
||||
if (Array.isArray(options)) {
|
||||
throw new _configError.default(`Expected config object but found array`, filepath);
|
||||
}
|
||||
delete options.$schema;
|
||||
return {
|
||||
filepath,
|
||||
dirname: _path().dirname(filepath),
|
||||
options
|
||||
};
|
||||
});
|
||||
const readIgnoreConfig = (0, _utils.makeStaticFileCache)((filepath, content) => {
|
||||
const ignoreDir = _path().dirname(filepath);
|
||||
const ignorePatterns = content.split("\n").map(line => line.replace(/#.*$/, "").trim()).filter(Boolean);
|
||||
for (const pattern of ignorePatterns) {
|
||||
if (pattern[0] === "!") {
|
||||
throw new _configError.default(`Negation of file paths is not supported.`, filepath);
|
||||
}
|
||||
}
|
||||
return {
|
||||
filepath,
|
||||
dirname: _path().dirname(filepath),
|
||||
ignore: ignorePatterns.map(pattern => (0, _patternToRegex.default)(pattern, ignoreDir))
|
||||
};
|
||||
});
|
||||
function findConfigUpwards(rootDir) {
|
||||
let dirname = rootDir;
|
||||
for (;;) {
|
||||
for (const filename of ROOT_CONFIG_FILENAMES) {
|
||||
if (_fs().existsSync(_path().join(dirname, filename))) {
|
||||
return dirname;
|
||||
}
|
||||
}
|
||||
const nextDir = _path().dirname(dirname);
|
||||
if (dirname === nextDir) break;
|
||||
dirname = nextDir;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function* findRelativeConfig(packageData, envName, caller) {
|
||||
let config = null;
|
||||
let ignore = null;
|
||||
const dirname = _path().dirname(packageData.filepath);
|
||||
for (const loc of packageData.directories) {
|
||||
if (!config) {
|
||||
var _packageData$pkg;
|
||||
config = yield* loadOneConfig(RELATIVE_CONFIG_FILENAMES, loc, envName, caller, ((_packageData$pkg = packageData.pkg) == null ? void 0 : _packageData$pkg.dirname) === loc ? packageToBabelConfig(packageData.pkg) : null);
|
||||
}
|
||||
if (!ignore) {
|
||||
const ignoreLoc = _path().join(loc, BABELIGNORE_FILENAME);
|
||||
ignore = yield* readIgnoreConfig(ignoreLoc);
|
||||
if (ignore) {
|
||||
debug("Found ignore %o from %o.", ignore.filepath, dirname);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
config,
|
||||
ignore
|
||||
};
|
||||
}
|
||||
function findRootConfig(dirname, envName, caller) {
|
||||
return loadOneConfig(ROOT_CONFIG_FILENAMES, dirname, envName, caller);
|
||||
}
|
||||
function* loadOneConfig(names, dirname, envName, caller, previousConfig = null) {
|
||||
const configs = yield* _gensync().all(names.map(filename => readConfig(_path().join(dirname, filename), envName, caller)));
|
||||
const config = configs.reduce((previousConfig, config) => {
|
||||
if (config && previousConfig) {
|
||||
throw new _configError.default(`Multiple configuration files found. Please remove one:\n` + ` - ${_path().basename(previousConfig.filepath)}\n` + ` - ${config.filepath}\n` + `from ${dirname}`);
|
||||
}
|
||||
return config || previousConfig;
|
||||
}, previousConfig);
|
||||
if (config) {
|
||||
debug("Found configuration %o from %o.", config.filepath, dirname);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
function* loadConfig(name, dirname, envName, caller) {
|
||||
const filepath = (((v, w) => (v = v.split("."), w = w.split("."), +v[0] > +w[0] || v[0] == w[0] && +v[1] >= +w[1]))(process.versions.node, "8.9") ? require.resolve : (r, {
|
||||
paths: [b]
|
||||
}, M = require("module")) => {
|
||||
let f = M._findPath(r, M._nodeModulePaths(b).concat(b));
|
||||
if (f) return f;
|
||||
f = new Error(`Cannot resolve module '${r}'`);
|
||||
f.code = "MODULE_NOT_FOUND";
|
||||
throw f;
|
||||
})(name, {
|
||||
paths: [dirname]
|
||||
});
|
||||
const conf = yield* readConfig(filepath, envName, caller);
|
||||
if (!conf) {
|
||||
throw new _configError.default(`Config file contains no configuration data`, filepath);
|
||||
}
|
||||
debug("Loaded config %o from %o.", name, dirname);
|
||||
return conf;
|
||||
}
|
||||
function readConfig(filepath, envName, caller) {
|
||||
const ext = _path().extname(filepath);
|
||||
switch (ext) {
|
||||
case ".js":
|
||||
case ".cjs":
|
||||
case ".mjs":
|
||||
case ".ts":
|
||||
case ".cts":
|
||||
case ".mts":
|
||||
return readConfigCode(filepath, {
|
||||
envName,
|
||||
caller
|
||||
});
|
||||
default:
|
||||
return readConfigJSON5(filepath);
|
||||
}
|
||||
}
|
||||
function* resolveShowConfigPath(dirname) {
|
||||
const targetPath = process.env.BABEL_SHOW_CONFIG_FOR;
|
||||
if (targetPath != null) {
|
||||
const absolutePath = _path().resolve(dirname, targetPath);
|
||||
const stats = yield* fs.stat(absolutePath);
|
||||
if (!stats.isFile()) {
|
||||
throw new Error(`${absolutePath}: BABEL_SHOW_CONFIG_FOR must refer to a regular file, directories are not supported.`);
|
||||
}
|
||||
return absolutePath;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function throwConfigError(filepath) {
|
||||
throw new _configError.default(`\
|
||||
Caching was left unconfigured. Babel's plugins, presets, and .babelrc.js files can be configured
|
||||
for various types of caching, using the first param of their handler functions:
|
||||
|
||||
module.exports = function(api) {
|
||||
// The API exposes the following:
|
||||
|
||||
// Cache the returned value forever and don't call this function again.
|
||||
api.cache(true);
|
||||
|
||||
// Don't cache at all. Not recommended because it will be very slow.
|
||||
api.cache(false);
|
||||
|
||||
// Cached based on the value of some function. If this function returns a value different from
|
||||
// a previously-encountered value, the plugins will re-evaluate.
|
||||
var env = api.cache(() => process.env.NODE_ENV);
|
||||
|
||||
// If testing for a specific env, we recommend specifics to avoid instantiating a plugin for
|
||||
// any possible NODE_ENV value that might come up during plugin execution.
|
||||
var isProd = api.cache(() => process.env.NODE_ENV === "production");
|
||||
|
||||
// .cache(fn) will perform a linear search though instances to find the matching plugin based
|
||||
// based on previous instantiated plugins. If you want to recreate the plugin and discard the
|
||||
// previous instance whenever something changes, you may use:
|
||||
var isProd = api.cache.invalidate(() => process.env.NODE_ENV === "production");
|
||||
|
||||
// Note, we also expose the following more-verbose versions of the above examples:
|
||||
api.cache.forever(); // api.cache(true)
|
||||
api.cache.never(); // api.cache(false)
|
||||
api.cache.using(fn); // api.cache(fn)
|
||||
|
||||
// Return the value that will be cached.
|
||||
return { };
|
||||
};`, filepath);
|
||||
}
|
||||
0 && 0;
|
||||
|
||||
//# sourceMappingURL=configuration.js.map
|
||||
1
web/node_modules/@babel/core/lib/config/files/configuration.js.map
generated
vendored
1
web/node_modules/@babel/core/lib/config/files/configuration.js.map
generated
vendored
File diff suppressed because one or more lines are too long
6
web/node_modules/@babel/core/lib/config/files/import.cjs
generated
vendored
6
web/node_modules/@babel/core/lib/config/files/import.cjs
generated
vendored
@@ -1,6 +0,0 @@
|
||||
module.exports = function import_(filepath) {
|
||||
return import(filepath);
|
||||
};
|
||||
0 && 0;
|
||||
|
||||
//# sourceMappingURL=import.cjs.map
|
||||
1
web/node_modules/@babel/core/lib/config/files/import.cjs.map
generated
vendored
1
web/node_modules/@babel/core/lib/config/files/import.cjs.map
generated
vendored
@@ -1 +0,0 @@
|
||||
{"version":3,"names":["module","exports","import_","filepath"],"sources":["../../../src/config/files/import.cjs"],"sourcesContent":["// We keep this in a separate file so that in older node versions, where\n// import() isn't supported, we can try/catch around the require() call\n// when loading this file.\n\nmodule.exports = function import_(filepath) {\n return import(filepath);\n};\n"],"mappings":"AAIAA,MAAM,CAACC,OAAO,GAAG,SAASC,OAAOA,CAACC,QAAQ,EAAE;EAC1C,OAAO,OAAOA,QAAQ,CAAC;AACzB,CAAC;AAAC","ignoreList":[]}
|
||||
58
web/node_modules/@babel/core/lib/config/files/index-browser.js
generated
vendored
58
web/node_modules/@babel/core/lib/config/files/index-browser.js
generated
vendored
@@ -1,58 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.ROOT_CONFIG_FILENAMES = void 0;
|
||||
exports.findConfigUpwards = findConfigUpwards;
|
||||
exports.findPackageData = findPackageData;
|
||||
exports.findRelativeConfig = findRelativeConfig;
|
||||
exports.findRootConfig = findRootConfig;
|
||||
exports.loadConfig = loadConfig;
|
||||
exports.loadPlugin = loadPlugin;
|
||||
exports.loadPreset = loadPreset;
|
||||
exports.resolvePlugin = resolvePlugin;
|
||||
exports.resolvePreset = resolvePreset;
|
||||
exports.resolveShowConfigPath = resolveShowConfigPath;
|
||||
function findConfigUpwards(rootDir) {
|
||||
return null;
|
||||
}
|
||||
function* findPackageData(filepath) {
|
||||
return {
|
||||
filepath,
|
||||
directories: [],
|
||||
pkg: null,
|
||||
isPackage: false
|
||||
};
|
||||
}
|
||||
function* findRelativeConfig(pkgData, envName, caller) {
|
||||
return {
|
||||
config: null,
|
||||
ignore: null
|
||||
};
|
||||
}
|
||||
function* findRootConfig(dirname, envName, caller) {
|
||||
return null;
|
||||
}
|
||||
function* loadConfig(name, dirname, envName, caller) {
|
||||
throw new Error(`Cannot load ${name} relative to ${dirname} in a browser`);
|
||||
}
|
||||
function* resolveShowConfigPath(dirname) {
|
||||
return null;
|
||||
}
|
||||
const ROOT_CONFIG_FILENAMES = exports.ROOT_CONFIG_FILENAMES = [];
|
||||
function resolvePlugin(name, dirname) {
|
||||
return null;
|
||||
}
|
||||
function resolvePreset(name, dirname) {
|
||||
return null;
|
||||
}
|
||||
function loadPlugin(name, dirname) {
|
||||
throw new Error(`Cannot load plugin ${name} relative to ${dirname} in a browser`);
|
||||
}
|
||||
function loadPreset(name, dirname) {
|
||||
throw new Error(`Cannot load preset ${name} relative to ${dirname} in a browser`);
|
||||
}
|
||||
0 && 0;
|
||||
|
||||
//# sourceMappingURL=index-browser.js.map
|
||||
1
web/node_modules/@babel/core/lib/config/files/index-browser.js.map
generated
vendored
1
web/node_modules/@babel/core/lib/config/files/index-browser.js.map
generated
vendored
@@ -1 +0,0 @@
|
||||
{"version":3,"names":["findConfigUpwards","rootDir","findPackageData","filepath","directories","pkg","isPackage","findRelativeConfig","pkgData","envName","caller","config","ignore","findRootConfig","dirname","loadConfig","name","Error","resolveShowConfigPath","ROOT_CONFIG_FILENAMES","exports","resolvePlugin","resolvePreset","loadPlugin","loadPreset"],"sources":["../../../src/config/files/index-browser.ts"],"sourcesContent":["/* c8 ignore start */\n\nimport type { Handler } from \"gensync\";\n\nimport type {\n ConfigFile,\n IgnoreFile,\n RelativeConfig,\n FilePackageData,\n} from \"./types.ts\";\n\nimport type { CallerMetadata } from \"../validation/options.ts\";\n\nexport type { ConfigFile, IgnoreFile, RelativeConfig, FilePackageData };\n\nexport function findConfigUpwards(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n rootDir: string,\n): string | null {\n return null;\n}\n\n// eslint-disable-next-line require-yield\nexport function* findPackageData(filepath: string): Handler<FilePackageData> {\n return {\n filepath,\n directories: [],\n pkg: null,\n isPackage: false,\n };\n}\n\n// eslint-disable-next-line require-yield\nexport function* findRelativeConfig(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n pkgData: FilePackageData,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n envName: string,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n caller: CallerMetadata | undefined,\n): Handler<RelativeConfig> {\n return { config: null, ignore: null };\n}\n\n// eslint-disable-next-line require-yield\nexport function* findRootConfig(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n dirname: string,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n envName: string,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n caller: CallerMetadata | undefined,\n): Handler<ConfigFile | null> {\n return null;\n}\n\n// eslint-disable-next-line require-yield\nexport function* loadConfig(\n name: string,\n dirname: string,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n envName: string,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n caller: CallerMetadata | undefined,\n): Handler<ConfigFile> {\n throw new Error(`Cannot load ${name} relative to ${dirname} in a browser`);\n}\n\n// eslint-disable-next-line require-yield\nexport function* resolveShowConfigPath(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n dirname: string,\n): Handler<string | null> {\n return null;\n}\n\nexport const ROOT_CONFIG_FILENAMES: string[] = [];\n\ntype Resolved =\n | { loader: \"require\"; filepath: string }\n | { loader: \"import\"; filepath: string };\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport function resolvePlugin(name: string, dirname: string): Resolved | null {\n return null;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport function resolvePreset(name: string, dirname: string): Resolved | null {\n return null;\n}\n\nexport function loadPlugin(\n name: string,\n dirname: string,\n): Handler<{\n filepath: string;\n value: unknown;\n}> {\n throw new Error(\n `Cannot load plugin ${name} relative to ${dirname} in a browser`,\n );\n}\n\nexport function loadPreset(\n name: string,\n dirname: string,\n): Handler<{\n filepath: string;\n value: unknown;\n}> {\n throw new Error(\n `Cannot load preset ${name} relative to ${dirname} in a browser`,\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAeO,SAASA,iBAAiBA,CAE/BC,OAAe,EACA;EACf,OAAO,IAAI;AACb;AAGO,UAAUC,eAAeA,CAACC,QAAgB,EAA4B;EAC3E,OAAO;IACLA,QAAQ;IACRC,WAAW,EAAE,EAAE;IACfC,GAAG,EAAE,IAAI;IACTC,SAAS,EAAE;EACb,CAAC;AACH;AAGO,UAAUC,kBAAkBA,CAEjCC,OAAwB,EAExBC,OAAe,EAEfC,MAAkC,EACT;EACzB,OAAO;IAAEC,MAAM,EAAE,IAAI;IAAEC,MAAM,EAAE;EAAK,CAAC;AACvC;AAGO,UAAUC,cAAcA,CAE7BC,OAAe,EAEfL,OAAe,EAEfC,MAAkC,EACN;EAC5B,OAAO,IAAI;AACb;AAGO,UAAUK,UAAUA,CACzBC,IAAY,EACZF,OAAe,EAEfL,OAAe,EAEfC,MAAkC,EACb;EACrB,MAAM,IAAIO,KAAK,CAAC,eAAeD,IAAI,gBAAgBF,OAAO,eAAe,CAAC;AAC5E;AAGO,UAAUI,qBAAqBA,CAEpCJ,OAAe,EACS;EACxB,OAAO,IAAI;AACb;AAEO,MAAMK,qBAA+B,GAAAC,OAAA,CAAAD,qBAAA,GAAG,EAAE;AAO1C,SAASE,aAAaA,CAACL,IAAY,EAAEF,OAAe,EAAmB;EAC5E,OAAO,IAAI;AACb;AAGO,SAASQ,aAAaA,CAACN,IAAY,EAAEF,OAAe,EAAmB;EAC5E,OAAO,IAAI;AACb;AAEO,SAASS,UAAUA,CACxBP,IAAY,EACZF,OAAe,EAId;EACD,MAAM,IAAIG,KAAK,CACb,sBAAsBD,IAAI,gBAAgBF,OAAO,eACnD,CAAC;AACH;AAEO,SAASU,UAAUA,CACxBR,IAAY,EACZF,OAAe,EAId;EACD,MAAM,IAAIG,KAAK,CACb,sBAAsBD,IAAI,gBAAgBF,OAAO,eACnD,CAAC;AACH;AAAC","ignoreList":[]}
|
||||
78
web/node_modules/@babel/core/lib/config/files/index.js
generated
vendored
78
web/node_modules/@babel/core/lib/config/files/index.js
generated
vendored
@@ -1,78 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
Object.defineProperty(exports, "ROOT_CONFIG_FILENAMES", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _configuration.ROOT_CONFIG_FILENAMES;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "findConfigUpwards", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _configuration.findConfigUpwards;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "findPackageData", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _package.findPackageData;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "findRelativeConfig", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _configuration.findRelativeConfig;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "findRootConfig", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _configuration.findRootConfig;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "loadConfig", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _configuration.loadConfig;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "loadPlugin", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _plugins.loadPlugin;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "loadPreset", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _plugins.loadPreset;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "resolvePlugin", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _plugins.resolvePlugin;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "resolvePreset", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _plugins.resolvePreset;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "resolveShowConfigPath", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _configuration.resolveShowConfigPath;
|
||||
}
|
||||
});
|
||||
var _package = require("./package.js");
|
||||
var _configuration = require("./configuration.js");
|
||||
var _plugins = require("./plugins.js");
|
||||
({});
|
||||
0 && 0;
|
||||
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
web/node_modules/@babel/core/lib/config/files/index.js.map
generated
vendored
1
web/node_modules/@babel/core/lib/config/files/index.js.map
generated
vendored
@@ -1 +0,0 @@
|
||||
{"version":3,"names":["_package","require","_configuration","_plugins"],"sources":["../../../src/config/files/index.ts"],"sourcesContent":["type indexBrowserType = typeof import(\"./index-browser\");\ntype indexType = typeof import(\"./index\");\n\n// Kind of gross, but essentially asserting that the exports of this module are the same as the\n// exports of index-browser, since this file may be replaced at bundle time with index-browser.\n({}) as any as indexBrowserType as indexType;\n\nexport { findPackageData } from \"./package.ts\";\n\nexport {\n findConfigUpwards,\n findRelativeConfig,\n findRootConfig,\n loadConfig,\n resolveShowConfigPath,\n ROOT_CONFIG_FILENAMES,\n} from \"./configuration.ts\";\nexport type {\n ConfigFile,\n IgnoreFile,\n RelativeConfig,\n FilePackageData,\n} from \"./types.ts\";\nexport {\n loadPlugin,\n loadPreset,\n resolvePlugin,\n resolvePreset,\n} from \"./plugins.ts\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOA,IAAAA,QAAA,GAAAC,OAAA;AAEA,IAAAC,cAAA,GAAAD,OAAA;AAcA,IAAAE,QAAA,GAAAF,OAAA;AAlBA,CAAC,CAAC,CAAC;AAA0C","ignoreList":[]}
|
||||
211
web/node_modules/@babel/core/lib/config/files/module-types.js
generated
vendored
211
web/node_modules/@babel/core/lib/config/files/module-types.js
generated
vendored
@@ -1,211 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = loadCodeDefault;
|
||||
exports.supportsESM = void 0;
|
||||
var _async = require("../../gensync-utils/async.js");
|
||||
function _path() {
|
||||
const data = require("path");
|
||||
_path = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
function _url() {
|
||||
const data = require("url");
|
||||
_url = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
require("module");
|
||||
function _semver() {
|
||||
const data = require("semver");
|
||||
_semver = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
function _debug() {
|
||||
const data = require("debug");
|
||||
_debug = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
var _rewriteStackTrace = require("../../errors/rewrite-stack-trace.js");
|
||||
var _configError = require("../../errors/config-error.js");
|
||||
var _transformFile = require("../../transform-file.js");
|
||||
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
||||
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
||||
const debug = _debug()("babel:config:loading:files:module-types");
|
||||
{
|
||||
try {
|
||||
var import_ = require("./import.cjs");
|
||||
} catch (_unused) {}
|
||||
}
|
||||
const supportsESM = exports.supportsESM = _semver().satisfies(process.versions.node, "^12.17 || >=13.2");
|
||||
const LOADING_CJS_FILES = new Set();
|
||||
function loadCjsDefault(filepath) {
|
||||
if (LOADING_CJS_FILES.has(filepath)) {
|
||||
debug("Auto-ignoring usage of config %o.", filepath);
|
||||
return {};
|
||||
}
|
||||
let module;
|
||||
try {
|
||||
LOADING_CJS_FILES.add(filepath);
|
||||
module = (0, _rewriteStackTrace.endHiddenCallStack)(require)(filepath);
|
||||
} finally {
|
||||
LOADING_CJS_FILES.delete(filepath);
|
||||
}
|
||||
{
|
||||
return module != null && (module.__esModule || module[Symbol.toStringTag] === "Module") ? module.default || (arguments[1] ? module : undefined) : module;
|
||||
}
|
||||
}
|
||||
const loadMjsFromPath = (0, _rewriteStackTrace.endHiddenCallStack)(function () {
|
||||
var _loadMjsFromPath = _asyncToGenerator(function* (filepath) {
|
||||
const url = (0, _url().pathToFileURL)(filepath).toString() + "?import";
|
||||
{
|
||||
if (!import_) {
|
||||
throw new _configError.default("Internal error: Native ECMAScript modules aren't supported by this platform.\n", filepath);
|
||||
}
|
||||
return yield import_(url);
|
||||
}
|
||||
});
|
||||
function loadMjsFromPath(_x) {
|
||||
return _loadMjsFromPath.apply(this, arguments);
|
||||
}
|
||||
return loadMjsFromPath;
|
||||
}());
|
||||
const tsNotSupportedError = ext => `\
|
||||
You are using a ${ext} config file, but Babel only supports transpiling .cts configs. Either:
|
||||
- Use a .cts config file
|
||||
- Update to Node.js 23.6.0, which has native TypeScript support
|
||||
- Install tsx to transpile ${ext} files on the fly\
|
||||
`;
|
||||
const SUPPORTED_EXTENSIONS = {
|
||||
".js": "unknown",
|
||||
".mjs": "esm",
|
||||
".cjs": "cjs",
|
||||
".ts": "unknown",
|
||||
".mts": "esm",
|
||||
".cts": "cjs"
|
||||
};
|
||||
const asyncModules = new Set();
|
||||
function* loadCodeDefault(filepath, loader, esmError, tlaError) {
|
||||
let async;
|
||||
const ext = _path().extname(filepath);
|
||||
const isTS = ext === ".ts" || ext === ".cts" || ext === ".mts";
|
||||
const type = SUPPORTED_EXTENSIONS[hasOwnProperty.call(SUPPORTED_EXTENSIONS, ext) ? ext : ".js"];
|
||||
const pattern = `${loader} ${type}`;
|
||||
switch (pattern) {
|
||||
case "require cjs":
|
||||
case "auto cjs":
|
||||
if (isTS) {
|
||||
return ensureTsSupport(filepath, ext, () => loadCjsDefault(filepath));
|
||||
} else {
|
||||
return loadCjsDefault(filepath, arguments[2]);
|
||||
}
|
||||
case "auto unknown":
|
||||
case "require unknown":
|
||||
case "require esm":
|
||||
try {
|
||||
if (isTS) {
|
||||
return ensureTsSupport(filepath, ext, () => loadCjsDefault(filepath));
|
||||
} else {
|
||||
return loadCjsDefault(filepath, arguments[2]);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.code === "ERR_REQUIRE_ASYNC_MODULE" || e.code === "ERR_REQUIRE_CYCLE_MODULE" && asyncModules.has(filepath)) {
|
||||
asyncModules.add(filepath);
|
||||
if (!(async != null ? async : async = yield* (0, _async.isAsync)())) {
|
||||
throw new _configError.default(tlaError, filepath);
|
||||
}
|
||||
} else if (e.code === "ERR_REQUIRE_ESM" || type === "esm") {} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
case "auto esm":
|
||||
if (async != null ? async : async = yield* (0, _async.isAsync)()) {
|
||||
const promise = isTS ? ensureTsSupport(filepath, ext, () => loadMjsFromPath(filepath)) : loadMjsFromPath(filepath);
|
||||
return (yield* (0, _async.waitFor)(promise)).default;
|
||||
}
|
||||
if (isTS) {
|
||||
throw new _configError.default(tsNotSupportedError(ext), filepath);
|
||||
} else {
|
||||
throw new _configError.default(esmError, filepath);
|
||||
}
|
||||
default:
|
||||
throw new Error("Internal Babel error: unreachable code.");
|
||||
}
|
||||
}
|
||||
function ensureTsSupport(filepath, ext, callback) {
|
||||
if (process.features.typescript || require.extensions[".ts"] || require.extensions[".cts"] || require.extensions[".mts"]) {
|
||||
return callback();
|
||||
}
|
||||
if (ext !== ".cts") {
|
||||
throw new _configError.default(tsNotSupportedError(ext), filepath);
|
||||
}
|
||||
const opts = {
|
||||
babelrc: false,
|
||||
configFile: false,
|
||||
sourceType: "unambiguous",
|
||||
sourceMaps: "inline",
|
||||
sourceFileName: _path().basename(filepath),
|
||||
presets: [[getTSPreset(filepath), Object.assign({
|
||||
onlyRemoveTypeImports: true,
|
||||
optimizeConstEnums: true
|
||||
}, {
|
||||
allowDeclareFields: true
|
||||
})]]
|
||||
};
|
||||
let handler = function (m, filename) {
|
||||
if (handler && filename.endsWith(".cts")) {
|
||||
try {
|
||||
return m._compile((0, _transformFile.transformFileSync)(filename, Object.assign({}, opts, {
|
||||
filename
|
||||
})).code, filename);
|
||||
} catch (error) {
|
||||
const packageJson = require("@babel/preset-typescript/package.json");
|
||||
if (_semver().lt(packageJson.version, "7.21.4")) {
|
||||
console.error("`.cts` configuration file failed to load, please try to update `@babel/preset-typescript`.");
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return require.extensions[".js"](m, filename);
|
||||
};
|
||||
require.extensions[ext] = handler;
|
||||
try {
|
||||
return callback();
|
||||
} finally {
|
||||
if (require.extensions[ext] === handler) delete require.extensions[ext];
|
||||
handler = undefined;
|
||||
}
|
||||
}
|
||||
function getTSPreset(filepath) {
|
||||
try {
|
||||
return require("@babel/preset-typescript");
|
||||
} catch (error) {
|
||||
if (error.code !== "MODULE_NOT_FOUND") throw error;
|
||||
let message = "You appear to be using a .cts file as Babel configuration, but the `@babel/preset-typescript` package was not found: please install it!";
|
||||
{
|
||||
if (process.versions.pnp) {
|
||||
message += `
|
||||
If you are using Yarn Plug'n'Play, you may also need to add the following configuration to your .yarnrc.yml file:
|
||||
|
||||
packageExtensions:
|
||||
\t"@babel/core@*":
|
||||
\t\tpeerDependencies:
|
||||
\t\t\t"@babel/preset-typescript": "*"
|
||||
`;
|
||||
}
|
||||
}
|
||||
throw new _configError.default(message, filepath);
|
||||
}
|
||||
}
|
||||
0 && 0;
|
||||
|
||||
//# sourceMappingURL=module-types.js.map
|
||||
1
web/node_modules/@babel/core/lib/config/files/module-types.js.map
generated
vendored
1
web/node_modules/@babel/core/lib/config/files/module-types.js.map
generated
vendored
File diff suppressed because one or more lines are too long
61
web/node_modules/@babel/core/lib/config/files/package.js
generated
vendored
61
web/node_modules/@babel/core/lib/config/files/package.js
generated
vendored
@@ -1,61 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.findPackageData = findPackageData;
|
||||
function _path() {
|
||||
const data = require("path");
|
||||
_path = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
var _utils = require("./utils.js");
|
||||
var _configError = require("../../errors/config-error.js");
|
||||
const PACKAGE_FILENAME = "package.json";
|
||||
const readConfigPackage = (0, _utils.makeStaticFileCache)((filepath, content) => {
|
||||
let options;
|
||||
try {
|
||||
options = JSON.parse(content);
|
||||
} catch (err) {
|
||||
throw new _configError.default(`Error while parsing JSON - ${err.message}`, filepath);
|
||||
}
|
||||
if (!options) throw new Error(`${filepath}: No config detected`);
|
||||
if (typeof options !== "object") {
|
||||
throw new _configError.default(`Config returned typeof ${typeof options}`, filepath);
|
||||
}
|
||||
if (Array.isArray(options)) {
|
||||
throw new _configError.default(`Expected config object but found array`, filepath);
|
||||
}
|
||||
return {
|
||||
filepath,
|
||||
dirname: _path().dirname(filepath),
|
||||
options
|
||||
};
|
||||
});
|
||||
function* findPackageData(filepath) {
|
||||
let pkg = null;
|
||||
const directories = [];
|
||||
let isPackage = true;
|
||||
let dirname = _path().dirname(filepath);
|
||||
while (!pkg && _path().basename(dirname) !== "node_modules") {
|
||||
directories.push(dirname);
|
||||
pkg = yield* readConfigPackage(_path().join(dirname, PACKAGE_FILENAME));
|
||||
const nextLoc = _path().dirname(dirname);
|
||||
if (dirname === nextLoc) {
|
||||
isPackage = false;
|
||||
break;
|
||||
}
|
||||
dirname = nextLoc;
|
||||
}
|
||||
return {
|
||||
filepath,
|
||||
directories,
|
||||
pkg,
|
||||
isPackage
|
||||
};
|
||||
}
|
||||
0 && 0;
|
||||
|
||||
//# sourceMappingURL=package.js.map
|
||||
1
web/node_modules/@babel/core/lib/config/files/package.js.map
generated
vendored
1
web/node_modules/@babel/core/lib/config/files/package.js.map
generated
vendored
@@ -1 +0,0 @@
|
||||
{"version":3,"names":["_path","data","require","_utils","_configError","PACKAGE_FILENAME","readConfigPackage","makeStaticFileCache","filepath","content","options","JSON","parse","err","ConfigError","message","Error","Array","isArray","dirname","path","findPackageData","pkg","directories","isPackage","basename","push","join","nextLoc"],"sources":["../../../src/config/files/package.ts"],"sourcesContent":["import path from \"node:path\";\nimport type { Handler } from \"gensync\";\nimport { makeStaticFileCache } from \"./utils.ts\";\n\nimport type { ConfigFile, FilePackageData } from \"./types.ts\";\n\nimport ConfigError from \"../../errors/config-error.ts\";\n\nconst PACKAGE_FILENAME = \"package.json\";\n\nconst readConfigPackage = makeStaticFileCache(\n (filepath, content): ConfigFile => {\n let options;\n try {\n options = JSON.parse(content) as unknown;\n } catch (err) {\n throw new ConfigError(\n `Error while parsing JSON - ${err.message}`,\n filepath,\n );\n }\n\n if (!options) throw new Error(`${filepath}: No config detected`);\n\n if (typeof options !== \"object\") {\n throw new ConfigError(\n `Config returned typeof ${typeof options}`,\n filepath,\n );\n }\n if (Array.isArray(options)) {\n throw new ConfigError(`Expected config object but found array`, filepath);\n }\n\n return {\n filepath,\n dirname: path.dirname(filepath),\n options,\n };\n },\n);\n\n/**\n * Find metadata about the package that this file is inside of. Resolution\n * of Babel's config requires general package information to decide when to\n * search for .babelrc files\n */\nexport function* findPackageData(filepath: string): Handler<FilePackageData> {\n let pkg = null;\n const directories = [];\n let isPackage = true;\n\n let dirname = path.dirname(filepath);\n while (!pkg && path.basename(dirname) !== \"node_modules\") {\n directories.push(dirname);\n\n pkg = yield* readConfigPackage(path.join(dirname, PACKAGE_FILENAME));\n\n const nextLoc = path.dirname(dirname);\n if (dirname === nextLoc) {\n isPackage = false;\n break;\n }\n dirname = nextLoc;\n }\n\n return { filepath, directories, pkg, isPackage };\n}\n"],"mappings":";;;;;;AAAA,SAAAA,MAAA;EAAA,MAAAC,IAAA,GAAAC,OAAA;EAAAF,KAAA,YAAAA,CAAA;IAAA,OAAAC,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAEA,IAAAE,MAAA,GAAAD,OAAA;AAIA,IAAAE,YAAA,GAAAF,OAAA;AAEA,MAAMG,gBAAgB,GAAG,cAAc;AAEvC,MAAMC,iBAAiB,GAAG,IAAAC,0BAAmB,EAC3C,CAACC,QAAQ,EAAEC,OAAO,KAAiB;EACjC,IAAIC,OAAO;EACX,IAAI;IACFA,OAAO,GAAGC,IAAI,CAACC,KAAK,CAACH,OAAO,CAAY;EAC1C,CAAC,CAAC,OAAOI,GAAG,EAAE;IACZ,MAAM,IAAIC,oBAAW,CACnB,8BAA8BD,GAAG,CAACE,OAAO,EAAE,EAC3CP,QACF,CAAC;EACH;EAEA,IAAI,CAACE,OAAO,EAAE,MAAM,IAAIM,KAAK,CAAC,GAAGR,QAAQ,sBAAsB,CAAC;EAEhE,IAAI,OAAOE,OAAO,KAAK,QAAQ,EAAE;IAC/B,MAAM,IAAII,oBAAW,CACnB,0BAA0B,OAAOJ,OAAO,EAAE,EAC1CF,QACF,CAAC;EACH;EACA,IAAIS,KAAK,CAACC,OAAO,CAACR,OAAO,CAAC,EAAE;IAC1B,MAAM,IAAII,oBAAW,CAAC,wCAAwC,EAAEN,QAAQ,CAAC;EAC3E;EAEA,OAAO;IACLA,QAAQ;IACRW,OAAO,EAAEC,MAAGA,CAAC,CAACD,OAAO,CAACX,QAAQ,CAAC;IAC/BE;EACF,CAAC;AACH,CACF,CAAC;AAOM,UAAUW,eAAeA,CAACb,QAAgB,EAA4B;EAC3E,IAAIc,GAAG,GAAG,IAAI;EACd,MAAMC,WAAW,GAAG,EAAE;EACtB,IAAIC,SAAS,GAAG,IAAI;EAEpB,IAAIL,OAAO,GAAGC,MAAGA,CAAC,CAACD,OAAO,CAACX,QAAQ,CAAC;EACpC,OAAO,CAACc,GAAG,IAAIF,MAAGA,CAAC,CAACK,QAAQ,CAACN,OAAO,CAAC,KAAK,cAAc,EAAE;IACxDI,WAAW,CAACG,IAAI,CAACP,OAAO,CAAC;IAEzBG,GAAG,GAAG,OAAOhB,iBAAiB,CAACc,MAAGA,CAAC,CAACO,IAAI,CAACR,OAAO,EAAEd,gBAAgB,CAAC,CAAC;IAEpE,MAAMuB,OAAO,GAAGR,MAAGA,CAAC,CAACD,OAAO,CAACA,OAAO,CAAC;IACrC,IAAIA,OAAO,KAAKS,OAAO,EAAE;MACvBJ,SAAS,GAAG,KAAK;MACjB;IACF;IACAL,OAAO,GAAGS,OAAO;EACnB;EAEA,OAAO;IAAEpB,QAAQ;IAAEe,WAAW;IAAED,GAAG;IAAEE;EAAU,CAAC;AAClD;AAAC","ignoreList":[]}
|
||||
230
web/node_modules/@babel/core/lib/config/files/plugins.js
generated
vendored
230
web/node_modules/@babel/core/lib/config/files/plugins.js
generated
vendored
@@ -1,230 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.loadPlugin = loadPlugin;
|
||||
exports.loadPreset = loadPreset;
|
||||
exports.resolvePreset = exports.resolvePlugin = void 0;
|
||||
function _debug() {
|
||||
const data = require("debug");
|
||||
_debug = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
function _path() {
|
||||
const data = require("path");
|
||||
_path = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
var _async = require("../../gensync-utils/async.js");
|
||||
var _moduleTypes = require("./module-types.js");
|
||||
function _url() {
|
||||
const data = require("url");
|
||||
_url = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
var _importMetaResolve = require("../../vendor/import-meta-resolve.js");
|
||||
require("module");
|
||||
function _fs() {
|
||||
const data = require("fs");
|
||||
_fs = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
const debug = _debug()("babel:config:loading:files:plugins");
|
||||
const EXACT_RE = /^module:/;
|
||||
const BABEL_PLUGIN_PREFIX_RE = /^(?!@|module:|[^/]+\/|babel-plugin-)/;
|
||||
const BABEL_PRESET_PREFIX_RE = /^(?!@|module:|[^/]+\/|babel-preset-)/;
|
||||
const BABEL_PLUGIN_ORG_RE = /^(@babel\/)(?!plugin-|[^/]+\/)/;
|
||||
const BABEL_PRESET_ORG_RE = /^(@babel\/)(?!preset-|[^/]+\/)/;
|
||||
const OTHER_PLUGIN_ORG_RE = /^(@(?!babel\/)[^/]+\/)(?![^/]*babel-plugin(?:-|\/|$)|[^/]+\/)/;
|
||||
const OTHER_PRESET_ORG_RE = /^(@(?!babel\/)[^/]+\/)(?![^/]*babel-preset(?:-|\/|$)|[^/]+\/)/;
|
||||
const OTHER_ORG_DEFAULT_RE = /^(@(?!babel$)[^/]+)$/;
|
||||
const resolvePlugin = exports.resolvePlugin = resolveStandardizedName.bind(null, "plugin");
|
||||
const resolvePreset = exports.resolvePreset = resolveStandardizedName.bind(null, "preset");
|
||||
function* loadPlugin(name, dirname) {
|
||||
const {
|
||||
filepath,
|
||||
loader
|
||||
} = resolvePlugin(name, dirname, yield* (0, _async.isAsync)());
|
||||
const value = yield* requireModule("plugin", loader, filepath);
|
||||
debug("Loaded plugin %o from %o.", name, dirname);
|
||||
return {
|
||||
filepath,
|
||||
value
|
||||
};
|
||||
}
|
||||
function* loadPreset(name, dirname) {
|
||||
const {
|
||||
filepath,
|
||||
loader
|
||||
} = resolvePreset(name, dirname, yield* (0, _async.isAsync)());
|
||||
const value = yield* requireModule("preset", loader, filepath);
|
||||
debug("Loaded preset %o from %o.", name, dirname);
|
||||
return {
|
||||
filepath,
|
||||
value
|
||||
};
|
||||
}
|
||||
function standardizeName(type, name) {
|
||||
if (_path().isAbsolute(name)) return name;
|
||||
const isPreset = type === "preset";
|
||||
return name.replace(isPreset ? BABEL_PRESET_PREFIX_RE : BABEL_PLUGIN_PREFIX_RE, `babel-${type}-`).replace(isPreset ? BABEL_PRESET_ORG_RE : BABEL_PLUGIN_ORG_RE, `$1${type}-`).replace(isPreset ? OTHER_PRESET_ORG_RE : OTHER_PLUGIN_ORG_RE, `$1babel-${type}-`).replace(OTHER_ORG_DEFAULT_RE, `$1/babel-${type}`).replace(EXACT_RE, "");
|
||||
}
|
||||
function* resolveAlternativesHelper(type, name) {
|
||||
const standardizedName = standardizeName(type, name);
|
||||
const {
|
||||
error,
|
||||
value
|
||||
} = yield standardizedName;
|
||||
if (!error) return value;
|
||||
if (error.code !== "MODULE_NOT_FOUND") throw error;
|
||||
if (standardizedName !== name && !(yield name).error) {
|
||||
error.message += `\n- If you want to resolve "${name}", use "module:${name}"`;
|
||||
}
|
||||
if (!(yield standardizeName(type, "@babel/" + name)).error) {
|
||||
error.message += `\n- Did you mean "@babel/${name}"?`;
|
||||
}
|
||||
const oppositeType = type === "preset" ? "plugin" : "preset";
|
||||
if (!(yield standardizeName(oppositeType, name)).error) {
|
||||
error.message += `\n- Did you accidentally pass a ${oppositeType} as a ${type}?`;
|
||||
}
|
||||
if (type === "plugin") {
|
||||
const transformName = standardizedName.replace("-proposal-", "-transform-");
|
||||
if (transformName !== standardizedName && !(yield transformName).error) {
|
||||
error.message += `\n- Did you mean "${transformName}"?`;
|
||||
}
|
||||
}
|
||||
error.message += `\n
|
||||
Make sure that all the Babel plugins and presets you are using
|
||||
are defined as dependencies or devDependencies in your package.json
|
||||
file. It's possible that the missing plugin is loaded by a preset
|
||||
you are using that forgot to add the plugin to its dependencies: you
|
||||
can workaround this problem by explicitly adding the missing package
|
||||
to your top-level package.json.
|
||||
`;
|
||||
throw error;
|
||||
}
|
||||
function tryRequireResolve(id, dirname) {
|
||||
try {
|
||||
if (dirname) {
|
||||
return {
|
||||
error: null,
|
||||
value: (((v, w) => (v = v.split("."), w = w.split("."), +v[0] > +w[0] || v[0] == w[0] && +v[1] >= +w[1]))(process.versions.node, "8.9") ? require.resolve : (r, {
|
||||
paths: [b]
|
||||
}, M = require("module")) => {
|
||||
let f = M._findPath(r, M._nodeModulePaths(b).concat(b));
|
||||
if (f) return f;
|
||||
f = new Error(`Cannot resolve module '${r}'`);
|
||||
f.code = "MODULE_NOT_FOUND";
|
||||
throw f;
|
||||
})(id, {
|
||||
paths: [dirname]
|
||||
})
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
error: null,
|
||||
value: require.resolve(id)
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
error,
|
||||
value: null
|
||||
};
|
||||
}
|
||||
}
|
||||
function tryImportMetaResolve(id, options) {
|
||||
try {
|
||||
return {
|
||||
error: null,
|
||||
value: (0, _importMetaResolve.resolve)(id, options)
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
error,
|
||||
value: null
|
||||
};
|
||||
}
|
||||
}
|
||||
function resolveStandardizedNameForRequire(type, name, dirname) {
|
||||
const it = resolveAlternativesHelper(type, name);
|
||||
let res = it.next();
|
||||
while (!res.done) {
|
||||
res = it.next(tryRequireResolve(res.value, dirname));
|
||||
}
|
||||
return {
|
||||
loader: "require",
|
||||
filepath: res.value
|
||||
};
|
||||
}
|
||||
function resolveStandardizedNameForImport(type, name, dirname) {
|
||||
const parentUrl = (0, _url().pathToFileURL)(_path().join(dirname, "./babel-virtual-resolve-base.js")).href;
|
||||
const it = resolveAlternativesHelper(type, name);
|
||||
let res = it.next();
|
||||
while (!res.done) {
|
||||
res = it.next(tryImportMetaResolve(res.value, parentUrl));
|
||||
}
|
||||
return {
|
||||
loader: "auto",
|
||||
filepath: (0, _url().fileURLToPath)(res.value)
|
||||
};
|
||||
}
|
||||
function resolveStandardizedName(type, name, dirname, allowAsync) {
|
||||
if (!_moduleTypes.supportsESM || !allowAsync) {
|
||||
return resolveStandardizedNameForRequire(type, name, dirname);
|
||||
}
|
||||
try {
|
||||
const resolved = resolveStandardizedNameForImport(type, name, dirname);
|
||||
if (!(0, _fs().existsSync)(resolved.filepath)) {
|
||||
throw Object.assign(new Error(`Could not resolve "${name}" in file ${dirname}.`), {
|
||||
type: "MODULE_NOT_FOUND"
|
||||
});
|
||||
}
|
||||
return resolved;
|
||||
} catch (e) {
|
||||
try {
|
||||
return resolveStandardizedNameForRequire(type, name, dirname);
|
||||
} catch (e2) {
|
||||
if (e.type === "MODULE_NOT_FOUND") throw e;
|
||||
if (e2.type === "MODULE_NOT_FOUND") throw e2;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
var LOADING_MODULES = new Set();
|
||||
}
|
||||
function* requireModule(type, loader, name) {
|
||||
{
|
||||
if (!(yield* (0, _async.isAsync)()) && LOADING_MODULES.has(name)) {
|
||||
throw new Error(`Reentrant ${type} detected trying to load "${name}". This module is not ignored ` + "and is trying to load itself while compiling itself, leading to a dependency cycle. " + 'We recommend adding it to your "ignore" list in your babelrc, or to a .babelignore.');
|
||||
}
|
||||
}
|
||||
try {
|
||||
{
|
||||
LOADING_MODULES.add(name);
|
||||
}
|
||||
{
|
||||
return yield* (0, _moduleTypes.default)(name, loader, `You appear to be using a native ECMAScript module ${type}, ` + "which is only supported when running Babel asynchronously " + "or when using the Node.js `--experimental-require-module` flag.", `You appear to be using a ${type} that contains top-level await, ` + "which is only supported when running Babel asynchronously.", true);
|
||||
}
|
||||
} catch (err) {
|
||||
err.message = `[BABEL]: ${err.message} (While processing: ${name})`;
|
||||
throw err;
|
||||
} finally {
|
||||
{
|
||||
LOADING_MODULES.delete(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
0 && 0;
|
||||
|
||||
//# sourceMappingURL=plugins.js.map
|
||||
1
web/node_modules/@babel/core/lib/config/files/plugins.js.map
generated
vendored
1
web/node_modules/@babel/core/lib/config/files/plugins.js.map
generated
vendored
File diff suppressed because one or more lines are too long
5
web/node_modules/@babel/core/lib/config/files/types.js
generated
vendored
5
web/node_modules/@babel/core/lib/config/files/types.js
generated
vendored
@@ -1,5 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
0 && 0;
|
||||
|
||||
//# sourceMappingURL=types.js.map
|
||||
1
web/node_modules/@babel/core/lib/config/files/types.js.map
generated
vendored
1
web/node_modules/@babel/core/lib/config/files/types.js.map
generated
vendored
@@ -1 +0,0 @@
|
||||
{"version":3,"names":[],"sources":["../../../src/config/files/types.ts"],"sourcesContent":["import type { InputOptions } from \"../index.ts\";\n\nexport type ConfigFile = {\n filepath: string;\n dirname: string;\n options: InputOptions & { babel?: unknown };\n};\n\nexport type IgnoreFile = {\n filepath: string;\n dirname: string;\n ignore: Array<RegExp>;\n};\n\nexport type RelativeConfig = {\n // The actual config, either from package.json#babel, .babelrc, or\n // .babelrc.js, if there was one.\n config: ConfigFile | null;\n // The .babelignore, if there was one.\n ignore: IgnoreFile | null;\n};\n\nexport type FilePackageData = {\n // The file in the package.\n filepath: string;\n // Any ancestor directories of the file that are within the package.\n directories: Array<string>;\n // The contents of the package.json. May not be found if the package just\n // terminated at a node_modules folder without finding one.\n pkg: ConfigFile | null;\n // True if a package.json or node_modules folder was found while traversing\n // the directory structure.\n isPackage: boolean;\n};\n"],"mappings":"","ignoreList":[]}
|
||||
36
web/node_modules/@babel/core/lib/config/files/utils.js
generated
vendored
36
web/node_modules/@babel/core/lib/config/files/utils.js
generated
vendored
@@ -1,36 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.makeStaticFileCache = makeStaticFileCache;
|
||||
var _caching = require("../caching.js");
|
||||
var fs = require("../../gensync-utils/fs.js");
|
||||
function _fs2() {
|
||||
const data = require("fs");
|
||||
_fs2 = function () {
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
function makeStaticFileCache(fn) {
|
||||
return (0, _caching.makeStrongCache)(function* (filepath, cache) {
|
||||
const cached = cache.invalidate(() => fileMtime(filepath));
|
||||
if (cached === null) {
|
||||
return null;
|
||||
}
|
||||
return fn(filepath, yield* fs.readFile(filepath, "utf8"));
|
||||
});
|
||||
}
|
||||
function fileMtime(filepath) {
|
||||
if (!_fs2().existsSync(filepath)) return null;
|
||||
try {
|
||||
return +_fs2().statSync(filepath).mtime;
|
||||
} catch (e) {
|
||||
if (e.code !== "ENOENT" && e.code !== "ENOTDIR") throw e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
0 && 0;
|
||||
|
||||
//# sourceMappingURL=utils.js.map
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user