Numerous Bug Fixes
Some checks failed
Automated Container Build / build-and-push (push) Failing after 8s
Some checks failed
Automated Container Build / build-and-push (push) Failing after 8s
This commit is contained in:
parent
02eefdac0e
commit
267d429122
959 changed files with 145571 additions and 221 deletions
|
|
@ -1,6 +1,8 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"bufio"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -89,13 +91,17 @@ func (h *FSHandler) ListDirectory(c *fiber.Ctx) error {
|
||||||
|
|
||||||
// Enrich with DB metadata (pinned status, etc.)
|
// Enrich with DB metadata (pinned status, etc.)
|
||||||
var isPinned, isTrashed int
|
var isPinned, isTrashed int
|
||||||
|
var checksum sql.NullString
|
||||||
err = h.DB.QueryRow(
|
err = h.DB.QueryRow(
|
||||||
"SELECT is_pinned, is_trashed FROM files WHERE path = ?",
|
"SELECT is_pinned, is_trashed, checksum FROM files WHERE path = ?",
|
||||||
filepath.ToSlash(entryPath),
|
filepath.ToSlash(entryPath),
|
||||||
).Scan(&isPinned, &isTrashed)
|
).Scan(&isPinned, &isTrashed, &checksum)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
fi.IsPinned = isPinned == 1
|
fi.IsPinned = isPinned == 1
|
||||||
fi.IsTrashed = isTrashed == 1
|
fi.IsTrashed = isTrashed == 1
|
||||||
|
if checksum.Valid {
|
||||||
|
fi.Checksum = checksum.String
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't show trashed items in normal listings
|
// Don't show trashed items in normal listings
|
||||||
|
|
@ -139,14 +145,16 @@ func (h *FSHandler) getFileInfo(c *fiber.Ctx, absPath string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var isPinned int
|
var isPinned int
|
||||||
var checksum string
|
var checksum sql.NullString
|
||||||
err = h.DB.QueryRow(
|
err = h.DB.QueryRow(
|
||||||
"SELECT is_pinned, checksum FROM files WHERE path = ?",
|
"SELECT is_pinned, checksum FROM files WHERE path = ?",
|
||||||
filepath.ToSlash(relativePath),
|
filepath.ToSlash(relativePath),
|
||||||
).Scan(&isPinned, &checksum)
|
).Scan(&isPinned, &checksum)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
fi.IsPinned = isPinned == 1
|
fi.IsPinned = isPinned == 1
|
||||||
fi.Checksum = checksum
|
if checksum.Valid {
|
||||||
|
fi.Checksum = checksum.String
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(fi)
|
return c.JSON(fi)
|
||||||
|
|
@ -166,21 +174,32 @@ func (h *FSHandler) CreateFolder(c *fiber.Ctx) error {
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "path is required"})
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "path is required"})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate path within jail
|
parentDir := filepath.Dir(body.Path)
|
||||||
fullPath, err := h.resolveSafe(body.Path)
|
folderName := filepath.Base(body.Path)
|
||||||
|
|
||||||
|
parentFullPath, err := h.resolveSafe(parentDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": err.Error()})
|
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": err.Error()})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uniqueName := GetUniquePath(parentFullPath, folderName)
|
||||||
|
fullPath := filepath.Join(parentFullPath, uniqueName)
|
||||||
|
cleanPath := filepath.ToSlash(filepath.Join(parentDir, uniqueName))
|
||||||
|
|
||||||
|
// Remove any prefix like "." if cleanPath was "." and it got joined.
|
||||||
|
// filepath.ToSlash cleans the path.
|
||||||
|
if cleanPath == "." {
|
||||||
|
// Just for safety, but base shouldn't be empty
|
||||||
|
}
|
||||||
|
|
||||||
if err := os.MkdirAll(fullPath, 0755); err != nil {
|
if err := os.MkdirAll(fullPath, 0755); err != nil {
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to create folder"})
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to create folder"})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record in DB
|
// Record in DB
|
||||||
cleanPath := filepath.ToSlash(body.Path)
|
|
||||||
h.DB.Exec(
|
h.DB.Exec(
|
||||||
"INSERT OR IGNORE INTO files (path, name, is_dir) VALUES (?, ?, 1)",
|
"INSERT OR IGNORE INTO files (path, name, is_dir) VALUES (?, ?, 1)",
|
||||||
cleanPath, filepath.Base(cleanPath),
|
cleanPath, uniqueName,
|
||||||
)
|
)
|
||||||
|
|
||||||
h.DB.AddAuditLog("folder_created", cleanPath, c.IP())
|
h.DB.AddAuditLog("folder_created", cleanPath, c.IP())
|
||||||
|
|
@ -207,7 +226,10 @@ func (h *FSHandler) Rename(c *fiber.Ctx) error {
|
||||||
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": err.Error()})
|
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": err.Error()})
|
||||||
}
|
}
|
||||||
|
|
||||||
newPath := filepath.Join(filepath.Dir(body.OldPath), body.NewName)
|
parentDir := filepath.Dir(oldFull)
|
||||||
|
uniqueName := GetUniquePath(parentDir, body.NewName)
|
||||||
|
newPath := filepath.Join(filepath.Dir(body.OldPath), uniqueName)
|
||||||
|
|
||||||
newFull, err := h.resolveSafe(newPath)
|
newFull, err := h.resolveSafe(newPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": err.Error()})
|
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": err.Error()})
|
||||||
|
|
@ -217,6 +239,9 @@ func (h *FSHandler) Rename(c *fiber.Ctx) error {
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to rename"})
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to rename"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update body.NewName to reflect the actual unique name used
|
||||||
|
body.NewName = uniqueName
|
||||||
|
|
||||||
// Update DB
|
// Update DB
|
||||||
h.DB.Exec(
|
h.DB.Exec(
|
||||||
"UPDATE files SET path = ?, name = ?, updated_at = CURRENT_TIMESTAMP WHERE path = ?",
|
"UPDATE files SET path = ?, name = ?, updated_at = CURRENT_TIMESTAMP WHERE path = ?",
|
||||||
|
|
@ -251,8 +276,17 @@ func (h *FSHandler) Move(c *fiber.Ctx) error {
|
||||||
|
|
||||||
// If destination is a directory, move into it
|
// If destination is a directory, move into it
|
||||||
if info, err := os.Stat(dstFull); err == nil && info.IsDir() {
|
if info, err := os.Stat(dstFull); err == nil && info.IsDir() {
|
||||||
dstFull = filepath.Join(dstFull, filepath.Base(srcFull))
|
uniqueName := GetUniquePath(dstFull, filepath.Base(srcFull))
|
||||||
body.DestPath = filepath.Join(body.DestPath, filepath.Base(body.SourcePath))
|
dstFull = filepath.Join(dstFull, uniqueName)
|
||||||
|
body.DestPath = filepath.Join(body.DestPath, uniqueName)
|
||||||
|
} else {
|
||||||
|
// If destination is not a directory but the file exists (rename), we don't overwrite if they want `(1)`
|
||||||
|
// Wait, if they intentionally rename to an existing file, should it append (1)?
|
||||||
|
// Yes, to prevent accidental overwrites.
|
||||||
|
parentDir := filepath.Dir(dstFull)
|
||||||
|
uniqueName := GetUniquePath(parentDir, filepath.Base(dstFull))
|
||||||
|
dstFull = filepath.Join(parentDir, uniqueName)
|
||||||
|
body.DestPath = filepath.Join(filepath.Dir(body.DestPath), uniqueName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.Rename(srcFull, dstFull); err != nil {
|
if err := os.Rename(srcFull, dstFull); err != nil {
|
||||||
|
|
@ -324,11 +358,16 @@ func (h *FSHandler) Delete(c *fiber.Ctx) error {
|
||||||
// Soft-delete: move to .trash directory
|
// Soft-delete: move to .trash directory
|
||||||
trashPath := filepath.Join(h.Config.TrashDir, relativePath)
|
trashPath := filepath.Join(h.Config.TrashDir, relativePath)
|
||||||
if err := os.MkdirAll(filepath.Dir(trashPath), 0755); err != nil {
|
if err := os.MkdirAll(filepath.Dir(trashPath), 0755); err != nil {
|
||||||
|
fmt.Printf("MkdirAll error: %v\n", err)
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to prepare trash"})
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to prepare trash"})
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.Rename(resolvedPath, trashPath); err != nil {
|
if err := os.Rename(resolvedPath, trashPath); err != nil {
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to move to trash"})
|
fmt.Printf("Rename error %s -> %s: %v. Attempting copy & delete fallback...\n", resolvedPath, trashPath, err)
|
||||||
|
if err := copyFile(resolvedPath, trashPath); err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to move to trash"})
|
||||||
|
}
|
||||||
|
os.RemoveAll(resolvedPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update DB
|
// Update DB
|
||||||
|
|
@ -438,12 +477,75 @@ func (h *FSHandler) EmptyTrash(c *fiber.Ctx) error {
|
||||||
os.MkdirAll(h.Config.TrashDir, 0755)
|
os.MkdirAll(h.Config.TrashDir, 0755)
|
||||||
|
|
||||||
h.DB.Exec("DELETE FROM files WHERE is_trashed = 1")
|
h.DB.Exec("DELETE FROM files WHERE is_trashed = 1")
|
||||||
|
h.DB.AddAuditLog("emptied_trash", "User emptied trash", c.IP())
|
||||||
h.DB.AddAuditLog("trash_emptied", "All trashed items permanently deleted", c.IP())
|
|
||||||
|
|
||||||
return c.JSON(fiber.Map{"message": "trash emptied"})
|
return c.JSON(fiber.Map{"message": "trash emptied"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DownloadFolder creates and streams a ZIP archive of a folder.
|
||||||
|
// GET /api/files/download-folder?path=...
|
||||||
|
func (h *FSHandler) DownloadFolder(c *fiber.Ctx) error {
|
||||||
|
folderPath := c.Query("path")
|
||||||
|
if folderPath == "" {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "path is required"})
|
||||||
|
}
|
||||||
|
|
||||||
|
fullPath, err := h.resolveSafe(folderPath)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": err.Error()})
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := os.Stat(fullPath)
|
||||||
|
if err != nil || !info.IsDir() {
|
||||||
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "folder not found"})
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set("Content-Type", "application/zip")
|
||||||
|
c.Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.zip\"", filepath.Base(fullPath)))
|
||||||
|
|
||||||
|
c.Context().SetBodyStreamWriter(func(w *bufio.Writer) {
|
||||||
|
zipWriter := zip.NewWriter(w)
|
||||||
|
defer zipWriter.Close()
|
||||||
|
|
||||||
|
filepath.Walk(fullPath, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if path == fullPath {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
relPath, err := filepath.Rel(fullPath, path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.IsDir() {
|
||||||
|
// add directory entry
|
||||||
|
relPath += "/"
|
||||||
|
_, err = zipWriter.Create(relPath)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// add file entry
|
||||||
|
zipFile, err := zipWriter.Create(relPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fsFile, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fsFile.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(zipFile, fsFile)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// TogglePin pins or unpins a file/folder.
|
// TogglePin pins or unpins a file/folder.
|
||||||
// POST /api/files/pin
|
// POST /api/files/pin
|
||||||
func (h *FSHandler) TogglePin(c *fiber.Ctx) error {
|
func (h *FSHandler) TogglePin(c *fiber.Ctx) error {
|
||||||
|
|
@ -585,8 +687,9 @@ func (h *FSHandler) Upload(c *fiber.Ctx) error {
|
||||||
|
|
||||||
// If destination is a directory, save into it
|
// If destination is a directory, save into it
|
||||||
if info, err := os.Stat(fullDest); err == nil && info.IsDir() {
|
if info, err := os.Stat(fullDest); err == nil && info.IsDir() {
|
||||||
fullDest = filepath.Join(fullDest, file.Filename)
|
uniqueName := GetUniquePath(fullDest, file.Filename)
|
||||||
destPath = filepath.Join(destPath, file.Filename)
|
fullDest = filepath.Join(fullDest, uniqueName)
|
||||||
|
destPath = filepath.Join(destPath, uniqueName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure parent directory exists
|
// Ensure parent directory exists
|
||||||
|
|
@ -594,9 +697,6 @@ func (h *FSHandler) Upload(c *fiber.Ctx) error {
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to create directory"})
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to create directory"})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Version existing file if it exists
|
|
||||||
CreateVersion(h.DB, h.Config, fullDest, filepath.ToSlash(destPath))
|
|
||||||
|
|
||||||
// Save the file
|
// Save the file
|
||||||
if err := c.SaveFile(file, fullDest); err != nil {
|
if err := c.SaveFile(file, fullDest); err != nil {
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to save file"})
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to save file"})
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,10 @@ func (h *TusHandler) Create(c *fiber.Ctx) error {
|
||||||
destPath = "."
|
destPath = "."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve destination directory and ensure unique filename
|
||||||
|
destFull := filepath.Join(h.Config.StorageDir, filepath.FromSlash(destPath))
|
||||||
|
uniqueFileName := GetUniquePath(destFull, fileName)
|
||||||
|
|
||||||
// Generate unique upload ID
|
// Generate unique upload ID
|
||||||
uploadID, err := generateUploadID()
|
uploadID, err := generateUploadID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -103,12 +107,12 @@ func (h *TusHandler) Create(c *fiber.Ctx) error {
|
||||||
|
|
||||||
upload := &tusUpload{
|
upload := &tusUpload{
|
||||||
ID: uploadID,
|
ID: uploadID,
|
||||||
FilePath: filepath.ToSlash(filepath.Join(destPath, fileName)),
|
FilePath: filepath.ToSlash(filepath.Join(destPath, uniqueFileName)),
|
||||||
FileName: fileName,
|
FileName: uniqueFileName,
|
||||||
FileSize: fileSize,
|
FileSize: fileSize,
|
||||||
Offset: 0,
|
Offset: 0,
|
||||||
TempPath: tempPath,
|
TempPath: tempPath,
|
||||||
MimeType: mime.TypeByExtension(filepath.Ext(fileName)),
|
MimeType: mime.TypeByExtension(filepath.Ext(uniqueFileName)),
|
||||||
}
|
}
|
||||||
|
|
||||||
h.mu.Lock()
|
h.mu.Lock()
|
||||||
|
|
@ -231,9 +235,6 @@ func (h *TusHandler) finalizeUpload(upload *tusUpload, ip string) {
|
||||||
// Ensure parent dir exists
|
// Ensure parent dir exists
|
||||||
os.MkdirAll(filepath.Dir(destFull), 0755)
|
os.MkdirAll(filepath.Dir(destFull), 0755)
|
||||||
|
|
||||||
// Version existing file if it exists
|
|
||||||
CreateVersion(h.DB, h.Config, destFull, filepath.ToSlash(upload.FilePath))
|
|
||||||
|
|
||||||
// Move temp file to final location
|
// Move temp file to final location
|
||||||
if err := os.Rename(upload.TempPath, destFull); err != nil {
|
if err := os.Rename(upload.TempPath, destFull); err != nil {
|
||||||
fmt.Printf("Error finalizing upload %s: %v\n", upload.ID, err)
|
fmt.Printf("Error finalizing upload %s: %v\n", upload.ID, err)
|
||||||
|
|
|
||||||
28
backend/handlers/utils.go
Normal file
28
backend/handlers/utils.go
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetUniquePath checks if a file or folder exists at the given baseDir with the given name.
|
||||||
|
// If it does, it systematically appends " (1)", " (2)", etc., until it finds an available name.
|
||||||
|
// It returns the unique filename.
|
||||||
|
func GetUniquePath(baseDir, name string) string {
|
||||||
|
ext := filepath.Ext(name)
|
||||||
|
base := name[:len(name)-len(ext)]
|
||||||
|
|
||||||
|
finalPath := filepath.Join(baseDir, name)
|
||||||
|
if _, err := os.Stat(finalPath); os.IsNotExist(err) {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 1; ; i++ {
|
||||||
|
newName := fmt.Sprintf("%s (%d)%s", base, i, ext)
|
||||||
|
finalPath = filepath.Join(baseDir, newName)
|
||||||
|
if _, err := os.Stat(finalPath); os.IsNotExist(err) {
|
||||||
|
return newName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,118 +0,0 @@
|
||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.elijahkuntz.com/Elijah/drive/config"
|
|
||||||
"git.elijahkuntz.com/Elijah/drive/database"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CreateVersion checks if a file exists and if so, moves it to the .versions directory
|
|
||||||
// and records it in the database before it gets overwritten.
|
|
||||||
// Call this BEFORE writing the new file to disk.
|
|
||||||
func CreateVersion(db *database.DB, cfg *config.Config, fullPath string, relPath string) error {
|
|
||||||
info, err := os.Stat(fullPath)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return nil // No existing file, nothing to version
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if info.IsDir() {
|
|
||||||
return nil // Don't version directories
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if versioning is enabled/has a max limit
|
|
||||||
val, err := db.GetSetting("max_versions")
|
|
||||||
maxVersions := 5
|
|
||||||
if err == nil {
|
|
||||||
if v, e := strconv.Atoi(val); e == nil {
|
|
||||||
maxVersions = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if maxVersions <= 0 {
|
|
||||||
return nil // Versioning disabled
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get current file details from DB (for checksum)
|
|
||||||
var checksum string
|
|
||||||
err = db.QueryRow("SELECT checksum FROM files WHERE path = ?", filepath.ToSlash(relPath)).Scan(&checksum)
|
|
||||||
if err != nil {
|
|
||||||
checksum = "" // fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate version path
|
|
||||||
timestamp := time.Now().Format("20060102150405")
|
|
||||||
versionFileName := fmt.Sprintf("%s_%s%s", strings.TrimSuffix(filepath.Base(relPath), filepath.Ext(relPath)), timestamp, filepath.Ext(relPath))
|
|
||||||
versionRelPath := filepath.Join(filepath.Dir(relPath), versionFileName)
|
|
||||||
versionFullPath := filepath.Join(cfg.VersionsDir, versionRelPath)
|
|
||||||
|
|
||||||
if err := os.MkdirAll(filepath.Dir(versionFullPath), 0755); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move current file to versions directory
|
|
||||||
if err := os.Rename(fullPath, versionFullPath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine version number
|
|
||||||
var currentMax int
|
|
||||||
err = db.QueryRow("SELECT COALESCE(MAX(version_number), 0) FROM file_versions WHERE file_path = ?", filepath.ToSlash(relPath)).Scan(¤tMax)
|
|
||||||
if err != nil {
|
|
||||||
currentMax = 0
|
|
||||||
}
|
|
||||||
versionNumber := currentMax + 1
|
|
||||||
|
|
||||||
// Insert version record
|
|
||||||
_, err = db.Exec(`
|
|
||||||
INSERT INTO file_versions (file_path, version_number, version_path, size, checksum)
|
|
||||||
VALUES (?, ?, ?, ?, ?)
|
|
||||||
`, filepath.ToSlash(relPath), versionNumber, filepath.ToSlash(versionRelPath), info.Size(), checksum)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prune old versions
|
|
||||||
pruneVersions(db, cfg, relPath, maxVersions)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func pruneVersions(db *database.DB, cfg *config.Config, relPath string, maxVersions int) {
|
|
||||||
rows, err := db.Query(`
|
|
||||||
SELECT id, version_path FROM file_versions
|
|
||||||
WHERE file_path = ?
|
|
||||||
ORDER BY version_number DESC
|
|
||||||
LIMIT -1 OFFSET ?
|
|
||||||
`, filepath.ToSlash(relPath), maxVersions)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var idsToDelete []int
|
|
||||||
for rows.Next() {
|
|
||||||
var id int
|
|
||||||
var vPath string
|
|
||||||
if err := rows.Scan(&id, &vPath); err == nil {
|
|
||||||
idsToDelete = append(idsToDelete, id)
|
|
||||||
os.Remove(filepath.Join(cfg.VersionsDir, filepath.FromSlash(vPath)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(idsToDelete) > 0 {
|
|
||||||
for _, id := range idsToDelete {
|
|
||||||
db.Exec("DELETE FROM file_versions WHERE id = ?", id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -84,13 +84,7 @@ func (h *WebDAVHandler) Handle(c *fiber.Ctx) error {
|
||||||
// Adapt Fiber's FastHTTP to standard net/http for the WebDAV handler
|
// Adapt Fiber's FastHTTP to standard net/http for the WebDAV handler
|
||||||
fasthttpadaptor.NewFastHTTPHandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
fasthttpadaptor.NewFastHTTPHandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
relPath := strings.TrimPrefix(r.URL.Path, "/webdav")
|
relPath := strings.TrimPrefix(r.URL.Path, "/webdav")
|
||||||
fullPath := filepath.Join(h.Config.StorageDir, filepath.FromSlash(relPath))
|
// Native WebDAV handles overwriting automatically
|
||||||
|
|
||||||
// If it's a PUT request, version the existing file before it gets overwritten
|
|
||||||
if r.Method == "PUT" {
|
|
||||||
CreateVersion(h.DB, h.Config, fullPath, filepath.ToSlash(relPath))
|
|
||||||
}
|
|
||||||
|
|
||||||
h.Handler.ServeHTTP(w, r)
|
h.Handler.ServeHTTP(w, r)
|
||||||
|
|
||||||
// If it was a PUT request, we should sync it with our DB (checksum, size, etc.)
|
// If it was a PUT request, we should sync it with our DB (checksum, size, etc.)
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,7 @@ func main() {
|
||||||
protected.Post("/files/rename", fsHandler.Rename)
|
protected.Post("/files/rename", fsHandler.Rename)
|
||||||
protected.Post("/files/move", fsHandler.Move)
|
protected.Post("/files/move", fsHandler.Move)
|
||||||
protected.Post("/files/copy", fsHandler.Copy)
|
protected.Post("/files/copy", fsHandler.Copy)
|
||||||
|
protected.Get("/files/download-folder", fsHandler.DownloadFolder)
|
||||||
protected.Post("/files/upload", fsHandler.Upload)
|
protected.Post("/files/upload", fsHandler.Upload)
|
||||||
|
|
||||||
// File listing and download (with path jail)
|
// File listing and download (with path jail)
|
||||||
|
|
|
||||||
16
frontend/node_modules/.bin/loose-envify
generated
vendored
Normal file
16
frontend/node_modules/.bin/loose-envify
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../loose-envify/cli.js" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../loose-envify/cli.js" "$@"
|
||||||
|
fi
|
||||||
17
frontend/node_modules/.bin/loose-envify.cmd
generated
vendored
Normal file
17
frontend/node_modules/.bin/loose-envify.cmd
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
@ECHO off
|
||||||
|
GOTO start
|
||||||
|
:find_dp0
|
||||||
|
SET dp0=%~dp0
|
||||||
|
EXIT /b
|
||||||
|
:start
|
||||||
|
SETLOCAL
|
||||||
|
CALL :find_dp0
|
||||||
|
|
||||||
|
IF EXIST "%dp0%\node.exe" (
|
||||||
|
SET "_prog=%dp0%\node.exe"
|
||||||
|
) ELSE (
|
||||||
|
SET "_prog=node"
|
||||||
|
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||||
|
)
|
||||||
|
|
||||||
|
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\loose-envify\cli.js" %*
|
||||||
28
frontend/node_modules/.bin/loose-envify.ps1
generated
vendored
Normal file
28
frontend/node_modules/.bin/loose-envify.ps1
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#!/usr/bin/env pwsh
|
||||||
|
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||||
|
|
||||||
|
$exe=""
|
||||||
|
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||||
|
# Fix case when both the Windows and Linux builds of Node
|
||||||
|
# are installed in the same directory
|
||||||
|
$exe=".exe"
|
||||||
|
}
|
||||||
|
$ret=0
|
||||||
|
if (Test-Path "$basedir/node$exe") {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "$basedir/node$exe" "$basedir/../loose-envify/cli.js" $args
|
||||||
|
} else {
|
||||||
|
& "$basedir/node$exe" "$basedir/../loose-envify/cli.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
} else {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "node$exe" "$basedir/../loose-envify/cli.js" $args
|
||||||
|
} else {
|
||||||
|
& "node$exe" "$basedir/../loose-envify/cli.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
}
|
||||||
|
exit $ret
|
||||||
193
frontend/node_modules/.package-lock.json
generated
vendored
193
frontend/node_modules/.package-lock.json
generated
vendored
|
|
@ -85,6 +85,56 @@
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@napi-rs/canvas": {
|
||||||
|
"version": "0.1.100",
|
||||||
|
"resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.100.tgz",
|
||||||
|
"integrity": "sha512-xglYA6q3XO5P3BNJYxVZ1IV7DLVjp1Py6nwag88YntrS+3vKHyYcMqXVS4ZztJmwz2uGvz1FWhI/4LgbR5uQDA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"workspaces": [
|
||||||
|
"e2e/*"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@napi-rs/canvas-android-arm64": "0.1.100",
|
||||||
|
"@napi-rs/canvas-darwin-arm64": "0.1.100",
|
||||||
|
"@napi-rs/canvas-darwin-x64": "0.1.100",
|
||||||
|
"@napi-rs/canvas-linux-arm-gnueabihf": "0.1.100",
|
||||||
|
"@napi-rs/canvas-linux-arm64-gnu": "0.1.100",
|
||||||
|
"@napi-rs/canvas-linux-arm64-musl": "0.1.100",
|
||||||
|
"@napi-rs/canvas-linux-riscv64-gnu": "0.1.100",
|
||||||
|
"@napi-rs/canvas-linux-x64-gnu": "0.1.100",
|
||||||
|
"@napi-rs/canvas-linux-x64-musl": "0.1.100",
|
||||||
|
"@napi-rs/canvas-win32-arm64-msvc": "0.1.100",
|
||||||
|
"@napi-rs/canvas-win32-x64-msvc": "0.1.100"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@napi-rs/canvas-win32-x64-msvc": {
|
||||||
|
"version": "0.1.100",
|
||||||
|
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.100.tgz",
|
||||||
|
"integrity": "sha512-MyT1j3mHC2+Lu4pBi9mKyMJhtP6U7k7EldY7sj/uS5gJA65gTXt8MefJQXLJo5d/vZbuWmfxzkEUNc/urV3pHA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@next/env": {
|
"node_modules/@next/env": {
|
||||||
"version": "15.5.18",
|
"version": "15.5.18",
|
||||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.18.tgz",
|
"resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.18.tgz",
|
||||||
|
|
@ -168,7 +218,7 @@
|
||||||
"version": "19.2.15",
|
"version": "19.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz",
|
||||||
"integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==",
|
"integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -404,6 +454,15 @@
|
||||||
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
|
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/clsx": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/combine-errors": {
|
"node_modules/combine-errors": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/combine-errors/-/combine-errors-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/combine-errors/-/combine-errors-3.0.3.tgz",
|
||||||
|
|
@ -440,7 +499,7 @@
|
||||||
"version": "3.2.3",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/custom-error-instance": {
|
"node_modules/custom-error-instance": {
|
||||||
|
|
@ -449,6 +508,15 @@
|
||||||
"integrity": "sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg==",
|
"integrity": "sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/dequal": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/detect-libc": {
|
"node_modules/detect-libc": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||||
|
|
@ -726,6 +794,12 @@
|
||||||
"integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==",
|
"integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==",
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/js-tokens": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/lilconfig": {
|
"node_modules/lilconfig": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
|
||||||
|
|
@ -808,6 +882,18 @@
|
||||||
"lodash._baseuniq": "~4.6.0"
|
"lodash._baseuniq": "~4.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/loose-envify": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"loose-envify": "cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lucide-react": {
|
"node_modules/lucide-react": {
|
||||||
"version": "0.468.0",
|
"version": "0.468.0",
|
||||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.468.0.tgz",
|
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.468.0.tgz",
|
||||||
|
|
@ -817,6 +903,41 @@
|
||||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
|
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/make-cancellable-promise": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/make-cancellable-promise/-/make-cancellable-promise-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-3SEQqTpV9oqVsIWqAcmDuaNeo7yBO3tqPtqGRcKkEo0lrzD3wqbKG9mkxO65KoOgXqj+zH2phJ2LiAsdzlogSw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/wojtekmaj/make-cancellable-promise?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/make-event-props": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/make-event-props/-/make-event-props-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-G/hncXrl4Qt7mauJEXSg3AcdYzmpkIITTNl5I+rH9sog5Yw0kK6vseJjCaPfOXqOqQuPUP89Rkhfz5kPS8ijtw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/wojtekmaj/make-event-props?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/merge-refs": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/merge-refs/-/merge-refs-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-3+B21mYK2IqUWnd2EivABLT7ueDhb0b8/dGK8LoFQPrU61YITeCMn14F7y7qZafWNZhUEKb24cJdiT5Wxs3prg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/wojtekmaj/merge-refs?sponsor=1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/merge2": {
|
"node_modules/merge2": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
|
|
@ -1013,6 +1134,18 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/pdfjs-dist": {
|
||||||
|
"version": "5.7.284",
|
||||||
|
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.7.284.tgz",
|
||||||
|
"integrity": "sha512-h4EdYQczmGhbOlqc3PPZwxevn7ApdWPbovAuWXOB/DjIyigSnwfy2oze7c6mRcSr9XgLp3eN3EeL4DyySTPMFw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22.13.0 || >=24"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@napi-rs/canvas": "^0.1.100"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
|
|
@ -1234,6 +1367,47 @@
|
||||||
"react": "^19.2.6"
|
"react": "^19.2.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-pdf": {
|
||||||
|
"version": "10.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-10.4.1.tgz",
|
||||||
|
"integrity": "sha512-kS/35staVCBqS29verTQJQZXw7RfsRCPO3fdJoW1KXylcv7A9dw6DZ3vJXC2w+bIBgLw5FN4pOFvKSQtkQhPfA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"clsx": "^2.0.0",
|
||||||
|
"dequal": "^2.0.3",
|
||||||
|
"make-cancellable-promise": "^2.0.0",
|
||||||
|
"make-event-props": "^2.0.0",
|
||||||
|
"merge-refs": "^2.0.0",
|
||||||
|
"pdfjs-dist": "5.4.296",
|
||||||
|
"tiny-invariant": "^1.0.0",
|
||||||
|
"warning": "^4.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/wojtekmaj/react-pdf?sponsor=1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-pdf/node_modules/pdfjs-dist": {
|
||||||
|
"version": "5.4.296",
|
||||||
|
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.296.tgz",
|
||||||
|
"integrity": "sha512-DlOzet0HO7OEnmUmB6wWGJrrdvbyJKftI1bhMitK7O2N8W2gc757yyYBbINy9IDafXAV9wmKr9t7xsTaNKRG5Q==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.16.0 || >=22.3.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@napi-rs/canvas": "^0.1.80"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/read-cache": {
|
"node_modules/read-cache": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||||
|
|
@ -1564,6 +1738,12 @@
|
||||||
"node": ">=0.8"
|
"node": ">=0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tiny-invariant": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/tinyglobby": {
|
"node_modules/tinyglobby": {
|
||||||
"version": "0.2.16",
|
"version": "0.2.16",
|
||||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
|
||||||
|
|
@ -1726,6 +1906,15 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/warning": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"loose-envify": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yaml": {
|
"node_modules/yaml": {
|
||||||
"version": "2.9.0",
|
"version": "2.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
|
||||||
|
|
|
||||||
3
frontend/node_modules/@napi-rs/canvas-win32-x64-msvc/README.md
generated
vendored
Normal file
3
frontend/node_modules/@napi-rs/canvas-win32-x64-msvc/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# `@napi-rs/canvas-win32-x64-msvc`
|
||||||
|
|
||||||
|
This is the **x86_64-pc-windows-msvc** binary for `@napi-rs/canvas`
|
||||||
BIN
frontend/node_modules/@napi-rs/canvas-win32-x64-msvc/icudtl.dat
generated
vendored
Normal file
BIN
frontend/node_modules/@napi-rs/canvas-win32-x64-msvc/icudtl.dat
generated
vendored
Normal file
Binary file not shown.
45
frontend/node_modules/@napi-rs/canvas-win32-x64-msvc/package.json
generated
vendored
Normal file
45
frontend/node_modules/@napi-rs/canvas-win32-x64-msvc/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
"name": "@napi-rs/canvas-win32-x64-msvc",
|
||||||
|
"version": "0.1.100",
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"main": "skia.win32-x64-msvc.node",
|
||||||
|
"files": [
|
||||||
|
"skia.win32-x64-msvc.node",
|
||||||
|
"icudtl.dat"
|
||||||
|
],
|
||||||
|
"description": "Canvas for Node.js with skia backend",
|
||||||
|
"keywords": [
|
||||||
|
"napi-rs",
|
||||||
|
"NAPI",
|
||||||
|
"N-API",
|
||||||
|
"Rust",
|
||||||
|
"node-addon",
|
||||||
|
"node-addon-api",
|
||||||
|
"canvas",
|
||||||
|
"image",
|
||||||
|
"pdf",
|
||||||
|
"svg",
|
||||||
|
"skia"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"registry": "https://registry.npmjs.org/",
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/Brooooooklyn/canvas.git"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
frontend/node_modules/@napi-rs/canvas-win32-x64-msvc/skia.win32-x64-msvc.node
generated
vendored
Normal file
BIN
frontend/node_modules/@napi-rs/canvas-win32-x64-msvc/skia.win32-x64-msvc.node
generated
vendored
Normal file
Binary file not shown.
21
frontend/node_modules/@napi-rs/canvas/LICENSE
generated
vendored
Normal file
21
frontend/node_modules/@napi-rs/canvas/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 lynweklm@gmail.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.
|
||||||
448
frontend/node_modules/@napi-rs/canvas/README.md
generated
vendored
Normal file
448
frontend/node_modules/@napi-rs/canvas/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,448 @@
|
||||||
|
# `skr canvas`
|
||||||
|
|
||||||
|
[](https://github.com/Brooooooklyn/canvas/actions/workflows/CI.yaml)
|
||||||
|

|
||||||
|
[](https://packagephobia.com/result?p=@napi-rs/canvas)
|
||||||
|
[](https://npmcharts.com/compare/@napi-rs/canvas?minimal=true)
|
||||||
|
|
||||||
|
> 🚀 Help me to become a full-time open-source developer by [sponsoring me on Github](https://github.com/sponsors/Brooooooklyn)
|
||||||
|
|
||||||
|
Google Skia binding to Node.js via [Node-API](https://napi.rs), **0 System dependencies!**
|
||||||
|
|
||||||
|
⚠️ This project is in pre-release stage. And there may be some bugs.<br/>
|
||||||
|
For details on planned features and future direction please refer to the [Roadmap](https://github.com/Brooooooklyn/canvas/issues/113).
|
||||||
|
|
||||||
|
[中文文档](./README-zh.md)
|
||||||
|
|
||||||
|
# Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn add @napi-rs/canvas
|
||||||
|
npm install @napi-rs/canvas
|
||||||
|
```
|
||||||
|
|
||||||
|
# Support matrix
|
||||||
|
|
||||||
|
## System requirement
|
||||||
|
|
||||||
|
### `arm64`
|
||||||
|
|
||||||
|
[**_cortex-a57_**](https://en.wikipedia.org/wiki/ARM_Cortex-A57) or newer CPU architecture on **Linux**.
|
||||||
|
|
||||||
|
All Apple M chips on **macOS**.
|
||||||
|
|
||||||
|
### `armv7`
|
||||||
|
|
||||||
|
[**_cortex-a7_**](https://en.wikipedia.org/wiki/ARM_Cortex-A7) or newer CPU architecture.
|
||||||
|
|
||||||
|
### glibc
|
||||||
|
|
||||||
|
Since Skia relies on the [glibc](https://www.gnu.org/software/libc/) 2.18 API, you need to have at least glibc version >= 2.18 on your system.
|
||||||
|
|
||||||
|
## AWS Lambda usage
|
||||||
|
|
||||||
|
To use this library on Lambda you will need to use a Lambda layer.
|
||||||
|
|
||||||
|
You can simply attach a lambda layer by getting an ARN from [Canvas-Lambda-Layer](https://github.com/ShivamJoker/Canvas-Lambda-Layer)
|
||||||
|
|
||||||
|
> Make sure to exclude `@napi-rs/canvas` while bundling your Lambda.
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { promises } = require('node:fs')
|
||||||
|
const { join } = require('node:path')
|
||||||
|
const { createCanvas, loadImage } = require('@napi-rs/canvas')
|
||||||
|
|
||||||
|
const canvas = createCanvas(300, 320)
|
||||||
|
const ctx = canvas.getContext('2d')
|
||||||
|
|
||||||
|
ctx.lineWidth = 10
|
||||||
|
ctx.strokeStyle = '#03a9f4'
|
||||||
|
ctx.fillStyle = '#03a9f4'
|
||||||
|
|
||||||
|
// Wall
|
||||||
|
ctx.strokeRect(75, 140, 150, 110)
|
||||||
|
|
||||||
|
// Door
|
||||||
|
ctx.fillRect(130, 190, 40, 60)
|
||||||
|
|
||||||
|
// Roof
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.moveTo(50, 140)
|
||||||
|
ctx.lineTo(150, 60)
|
||||||
|
ctx.lineTo(250, 140)
|
||||||
|
ctx.closePath()
|
||||||
|
ctx.stroke()
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
// load images from disk or from a URL
|
||||||
|
const catImage = await loadImage('path/to/cat.png')
|
||||||
|
const dogImage = await loadImage('https://example.com/path/to/dog.jpg')
|
||||||
|
|
||||||
|
ctx.drawImage(catImage, 0, 0, catImage.width, catImage.height)
|
||||||
|
|
||||||
|
ctx.drawImage(dogImage, canvas.width / 2, canvas.height / 2, dogImage.width, dogImage.height)
|
||||||
|
|
||||||
|
// export canvas as image
|
||||||
|
const pngData = await canvas.encode('png') // JPEG, AVIF and WebP are also supported
|
||||||
|
// encoding in libuv thread pool, non-blocking
|
||||||
|
await promises.writeFile(join(__dirname, 'simple.png'), pngData)
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Emoji text
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { writeFileSync } = require('fs')
|
||||||
|
const { join } = require('path')
|
||||||
|
|
||||||
|
const { createCanvas, GlobalFonts } = require('@napi-rs/canvas')
|
||||||
|
|
||||||
|
GlobalFonts.registerFromPath(join(__dirname, '..', 'fonts', 'AppleColorEmoji@2x.ttf'), 'Apple Emoji')
|
||||||
|
GlobalFonts.registerFromPath(join(__dirname, '..', '__test__', 'fonts', 'COLRv1.ttf'), 'COLRv1')
|
||||||
|
|
||||||
|
console.info(GlobalFonts.families)
|
||||||
|
|
||||||
|
const canvas = createCanvas(760, 360)
|
||||||
|
const ctx = canvas.getContext('2d')
|
||||||
|
|
||||||
|
ctx.font = '50px Apple Emoji'
|
||||||
|
ctx.strokeText('😀😃😄😁😆😅😂🤣☺️😊😊😇', 50, 150)
|
||||||
|
|
||||||
|
ctx.font = '100px COLRv1'
|
||||||
|
ctx.fillText('abc', 50, 300)
|
||||||
|
|
||||||
|
const b = canvas.toBuffer('image/png')
|
||||||
|
|
||||||
|
writeFileSync(join(__dirname, 'draw-emoji.png'), b)
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# Performance
|
||||||
|
|
||||||
|
See [benchmark](./benchmark) for benchmark code.
|
||||||
|
|
||||||
|
Hardware info:
|
||||||
|
|
||||||
|
```
|
||||||
|
,MMMM. Host - xxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
.MMMMMM Machine - Mac15,9
|
||||||
|
MMMMM, Kernel - 24.0.0
|
||||||
|
.;MMMMM:' MMMMMMMMMM;. OS - macOS 15.0.1 Sequoia
|
||||||
|
MMMMMMMMMMMMNWMMMMMMMMMMM: DE - Aqua
|
||||||
|
.MMMMMMMMMMMMMMMMMMMMMMMMWM. WM - Quartz Compositor
|
||||||
|
MMMMMMMMMMMMMMMMMMMMMMMMM. Packages - 194 (Homebrew), 32 (cargo)
|
||||||
|
;MMMMMMMMMMMMMMMMMMMMMMMM: Shell - zsh
|
||||||
|
:MMMMMMMMMMMMMMMMMMMMMMMM: Terminal - warpterminal (Version v0.2024.10.23.14.49.stable_00)
|
||||||
|
.MMMMMMMMMMMMMMMMMMMMMMMMM. Resolution - 5120x2880@160fps (as 2560x1440)
|
||||||
|
MMMMMMMMMMMMMMMMMMMMMMMMMMM. 2992x1934@120fps (as 1496x967)
|
||||||
|
.MMMMMMMMMMMMMMMMMMMMMMMMMM. 2232x1512@60fps (as 1116x756)
|
||||||
|
MMMMMMMMMMMMMMMMMMMMMMMM Uptime - 1d 2h 32m
|
||||||
|
;MMMMMMMMMMMMMMMMMMMM. CPU - Apple M3 Max (16)
|
||||||
|
.MMMM,. .MMMM,. CPU Load - 16%
|
||||||
|
Memory - 50.1 GB / 134.2 GB
|
||||||
|
Battery - 78% & Discharging
|
||||||
|
Disk Space - 624.0 GB / 994.7 GB
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
❯ yarn bench
|
||||||
|
Draw a House and export to PNG
|
||||||
|
┌─────────┬─────────────────┬───────────────────────┬──────────────────────────┬────────────────────────────┬───────────────────────────┬─────────┐
|
||||||
|
│ (index) │ Task name │ Latency average (ns) │ Latency median (ns) │ Throughput average (ops/s) │ Throughput median (ops/s) │ Samples │
|
||||||
|
├─────────┼─────────────────┼───────────────────────┼──────────────────────────┼────────────────────────────┼───────────────────────────┼─────────┤
|
||||||
|
│ 0 │ '@napi-rs/skia' │ '14676992.14 ± 0.68%' │ '14602333.00' │ '68 ± 0.59%' │ '68' │ 69 │
|
||||||
|
│ 1 │ 'skia-canvas' │ '21167809.17 ± 2.05%' │ '20960021.00 ± 13646.00' │ '47 ± 1.31%' │ '48' │ 64 │
|
||||||
|
│ 2 │ 'node-canvas' │ '16552027.42 ± 0.70%' │ '16451291.50 ± 2208.50' │ '60 ± 0.62%' │ '61' │ 64 │
|
||||||
|
└─────────┴─────────────────┴───────────────────────┴──────────────────────────┴────────────────────────────┴───────────────────────────┴─────────┘
|
||||||
|
Draw Gradient and export to PNG
|
||||||
|
┌─────────┬─────────────────┬───────────────────────┬─────────────────────────┬────────────────────────────┬───────────────────────────┬─────────┐
|
||||||
|
│ (index) │ Task name │ Latency average (ns) │ Latency median (ns) │ Throughput average (ops/s) │ Throughput median (ops/s) │ Samples │
|
||||||
|
├─────────┼─────────────────┼───────────────────────┼─────────────────────────┼────────────────────────────┼───────────────────────────┼─────────┤
|
||||||
|
│ 0 │ '@napi-rs/skia' │ '15228495.58 ± 0.53%' │ '15146312.50 ± 1187.50' │ '66 ± 0.48%' │ '66' │ 66 │
|
||||||
|
│ 1 │ 'skia-canvas' │ '21725564.41 ± 2.20%' │ '21412520.50 ± 2104.50' │ '46 ± 1.39%' │ '47' │ 64 │
|
||||||
|
│ 2 │ 'node-canvas' │ '17976022.14 ± 1.53%' │ '17563479.50 ± 5104.50' │ '56 ± 1.38%' │ '57' │ 64 │
|
||||||
|
└─────────┴─────────────────┴───────────────────────┴─────────────────────────┴────────────────────────────┴───────────────────────────┴─────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
# Features
|
||||||
|
|
||||||
|
## Path2D
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
new Path2D()
|
||||||
|
new Path2D(path: Path2D)
|
||||||
|
// new Path2D('M108.956,403.826c0,0,0.178,3.344-1.276,3.311 c-1.455-0.033-30.507-84.917-66.752-80.957C40.928,326.18,72.326,313.197,108.956,403.826z')
|
||||||
|
new Path2D(path: string)
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export interface DOMMatrix2DInit {
|
||||||
|
a: number
|
||||||
|
b: number
|
||||||
|
c: number
|
||||||
|
d: number
|
||||||
|
e: number
|
||||||
|
f: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Path2D {
|
||||||
|
constructor(path?: Path2D | string)
|
||||||
|
|
||||||
|
addPath(path: Path2D, transform?: DOMMatrix2DInit): void
|
||||||
|
arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: boolean): void
|
||||||
|
arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void
|
||||||
|
bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void
|
||||||
|
closePath(): void
|
||||||
|
ellipse(
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
radiusX: number,
|
||||||
|
radiusY: number,
|
||||||
|
rotation: number,
|
||||||
|
startAngle: number,
|
||||||
|
endAngle: number,
|
||||||
|
anticlockwise?: boolean,
|
||||||
|
): void
|
||||||
|
lineTo(x: number, y: number): void
|
||||||
|
moveTo(x: number, y: number): void
|
||||||
|
quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void
|
||||||
|
rect(x: number, y: number, w: number, h: number): void
|
||||||
|
|
||||||
|
// PathKit methods
|
||||||
|
op(path: Path2D, operation: PathOp): Path2D
|
||||||
|
toSVGString(): string
|
||||||
|
getFillType(): FillType
|
||||||
|
getFillTypeString(): string
|
||||||
|
setFillType(type: FillType): void
|
||||||
|
simplify(): Path2D
|
||||||
|
asWinding(): Path2D
|
||||||
|
stroke(stroke?: StrokeOptions): Path2D
|
||||||
|
transform(transform: DOMMatrix2DInit): Path2D
|
||||||
|
getBounds(): [left: number, top: number, right: number, bottom: number]
|
||||||
|
computeTightBounds(): [left: number, top: number, right: number, bottom: number]
|
||||||
|
trim(start: number, end: number, isComplement?: boolean): Path2D
|
||||||
|
round(radius: number): Path2D
|
||||||
|
equals(path: Path2D): boolean
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## PathKit
|
||||||
|
|
||||||
|
`PathKit` is a toolset for manipulating Path in `Skia`, supporting **_quadratic beziers_**, **_cubic beziers_** and **_conics_**.
|
||||||
|
The main features are.
|
||||||
|
|
||||||
|
### Path Operation
|
||||||
|
|
||||||
|
`.op(path, PathOp)`
|
||||||
|
|
||||||
|
```js
|
||||||
|
const pathOne = new Path2D(
|
||||||
|
'M8 50H92C96.4183 50 100 53.5817 100 58V142C100 146.418 96.4183 150 92 150H8C3.58172 150 0 146.418 0 142V58C0 53.5817 3.58172 50 8 50Z',
|
||||||
|
)
|
||||||
|
const pathTwo = new Path2D(
|
||||||
|
'"M58 0H142C146.418 0 150 3.58172 150 8V92C150 96.4183 146.418 100 142 100H58C53.5817 100 50 96.4183 50 92V8C50 3.58172 53.5817 0 58 0Z',
|
||||||
|
)
|
||||||
|
|
||||||
|
pathOne.op(pathTwo, PathOp.Intersect).toSVGString()
|
||||||
|
// => "M100 100L58 100C53.5817 100 50 96.4183 50 92L50 50L92 50C96.4183 50 100 53.5817 100 58L100 100Z"
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Union**, subtract the op path from the first path
|
||||||
|
- **Difference**, intersect the two paths
|
||||||
|
- **ReverseDifference**, union (inclusive-or) the two paths
|
||||||
|
- **Intersect**, exclusive-or the two paths
|
||||||
|
- **XOR**, subtract the first path from the op path
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Covert `FillType` in **_Path_**
|
||||||
|
|
||||||
|
`.asWinding()`
|
||||||
|
|
||||||
|
You can convert `fill-rule="evenodd"` to `fill-rule="nonzero"` in SVG.
|
||||||
|
This is useful for **OpenType** font-related tools, as `fill-rule="nonzero"` is only supported in **OpenType** fonts.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
```js
|
||||||
|
const pathCircle = new Path2D(
|
||||||
|
'M24.2979 13.6364H129.394V40.9091H24.2979L14.6278 27.2727L24.2979 13.6364ZM21.9592 0C19.0246 0 16.2716 1.42436 14.571 3.82251L1.67756 22.0043C-0.559186 25.1585 -0.559186 29.387 1.67756 32.5411L14.571 50.7227C16.2716 53.1209 19.0246 54.5455 21.9592 54.5455H70.4673V68.1818H16.073C11.0661 68.1818 7.00728 72.2518 7.00728 77.2727V113.636C7.00728 118.657 11.0661 122.727 16.073 122.727H70.4673V150H84.0658V122.727H128.041C130.975 122.727 133.729 121.303 135.429 118.905L148.323 100.723C150.559 97.5686 150.559 93.3405 148.323 90.1864L135.429 72.0045C133.729 69.6064 130.975 68.1818 128.041 68.1818H84.0658V54.5455H133.927C138.934 54.5455 142.993 50.4755 142.993 45.4545V9.09091C142.993 4.07014 138.934 0 133.927 0H21.9592ZM125.702 109.091H20.6058V81.8182H125.702L135.372 95.4545L125.702 109.091Z',
|
||||||
|
)
|
||||||
|
pathCircle.setFillType(FillType.EvenOdd)
|
||||||
|
pathCircle.asWinding().toSVGString()
|
||||||
|
// => "M24.2979 13.6364L129.394 13.6364L129.394 40.9091L24.2979 40.9091L14.6278 27.2727L24.2979 13.6364ZM21.9592 0C19.0246 0 16.2716 1.42436 14.571 3.82251L1.67756 22.0043C-0.559186 25.1585 -0.559186 29.387 1.67756 32.5411L14.571 50.7227C16.2716 53.1209 19.0246 54.5455 21.9592 54.5455L70.4673 54.5455L70.4673 68.1818L16.073 68.1818C11.0661 68.1818 7.00728 72.2518 7.00728 77.2727L7.00728 113.636C7.00728 118.657 11.0661 122.727 16.073 122.727L70.4673 122.727L70.4673 150L84.0658 150L84.0658 122.727L128.041 122.727C130.975 122.727 133.729 121.303 135.429 118.905L148.323 100.723C150.559 97.5686 150.559 93.3405 148.323 90.1864L135.429 72.0045C133.729 69.6064 130.975 68.1818 128.041 68.1818L84.0658 68.1818L84.0658 54.5455L133.927 54.5455C138.934 54.5455 142.993 50.4755 142.993 45.4545L142.993 9.09091C142.993 4.07014 138.934 0 133.927 0L21.9592 0ZM125.702 109.091L20.6058 109.091L20.6058 81.8182L125.702 81.8182L135.372 95.4545L125.702 109.091Z"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Simplify **_Path_**
|
||||||
|
|
||||||
|
`.simplify()`
|
||||||
|
|
||||||
|
Set the path to the same non-overlapping contour as the original path area, which means that it can also remove overlapping paths.
|
||||||
|
|
||||||
|
<img width="800" src="./docs/imgs/simplify.png" >
|
||||||
|
|
||||||
|
[SVG with overlapping paths](./docs/imgs/overlapping-path.svg) (Left)
|
||||||
|
|
||||||
|
```js
|
||||||
|
const path =
|
||||||
|
'M2.933,89.89 L89.005,3.818 Q90.412,2.411 92.249,1.65 Q94.087,0.889 96.076,0.889 Q98.065,0.889 99.903,1.65 Q101.741,2.411 103.147,3.818 L189.22,89.89 Q190.626,91.296 191.387,93.134 Q192.148,94.972 192.148,96.961 Q192.148,98.95 191.387,100.788 Q190.626,102.625 189.219,104.032 Q187.813,105.439 185.975,106.2 Q184.138,106.961 182.148,106.961 Q180.159,106.961 178.322,106.2 Q176.484,105.439 175.077,104.032 L89.005,17.96 L96.076,10.889 L103.147,17.96 L17.075,104.032 Q15.668,105.439 13.831,106.2 Q11.993,106.961 10.004,106.961 Q8.015,106.961 6.177,106.2 Q4.339,105.439 2.933,104.032 Q1.526,102.625 0.765,100.788 Q0.004,98.95 0.004,96.961 Q0.004,94.972 0.765,93.134 Q1.526,91.296 2.933,89.89 Z'
|
||||||
|
|
||||||
|
path.simplify().toSVGString()
|
||||||
|
// => "M89.005 3.818L2.933 89.89Q1.526 91.296 0.765 93.134Q0.004 94.972 0.004 96.961Q0.004 98.95 0.765 100.788Q1.526 102.625 2.933 104.032Q4.339 105.439 6.177 106.2Q8.015 106.961 10.004 106.961Q11.993 106.961 13.831 106.2Q15.668 105.439 17.075 104.032L96.076 25.031L175.077 104.032Q176.484 105.439 178.322 106.2Q180.159 106.961 182.148 106.961Q184.138 106.961 185.975 106.2Q187.813 105.439 189.219 104.032Q190.626 102.625 191.387 100.788Q192.148 98.95 192.148 96.961Q192.148 94.972 191.387 93.134Q190.626 91.296 189.22 89.89L103.147 3.818Q101.741 2.411 99.903 1.65Q98.065 0.889 96.076 0.889Q94.087 0.889 92.249 1.65Q90.412 2.411 89.005 3.818Z"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lottie Animation
|
||||||
|
|
||||||
|
Render [Lottie](https://airbnb.io/lottie/) animations using Skia's [Skottie](https://skia.org/docs/user/modules/skottie/) module.
|
||||||
|
|
||||||
|
### Load Animation
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { LottieAnimation } = require('@napi-rs/canvas')
|
||||||
|
|
||||||
|
// Load from file
|
||||||
|
const animation = LottieAnimation.loadFromFile('animation.json')
|
||||||
|
|
||||||
|
// Load from JSON string with resource path for external assets
|
||||||
|
const animation = LottieAnimation.loadFromData(jsonString, {
|
||||||
|
resourcePath: '/path/to/assets',
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Animation Properties
|
||||||
|
|
||||||
|
```js
|
||||||
|
animation.duration // Total duration in seconds
|
||||||
|
animation.fps // Frames per second
|
||||||
|
animation.frames // Total frame count
|
||||||
|
animation.width // Animation width
|
||||||
|
animation.height // Animation height
|
||||||
|
animation.version // Lottie format version
|
||||||
|
```
|
||||||
|
|
||||||
|
### Playback Control
|
||||||
|
|
||||||
|
```js
|
||||||
|
animation.seekFrame(30) // Seek to frame 30
|
||||||
|
animation.seek(1.5) // Seek to 1.5 seconds
|
||||||
|
```
|
||||||
|
|
||||||
|
### Render to Canvas
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { createCanvas, LottieAnimation } = require('@napi-rs/canvas')
|
||||||
|
|
||||||
|
const animation = LottieAnimation.loadFromFile('animation.json')
|
||||||
|
const canvas = createCanvas(animation.width, animation.height)
|
||||||
|
const ctx = canvas.getContext('2d')
|
||||||
|
|
||||||
|
// Render at original size
|
||||||
|
animation.render(ctx)
|
||||||
|
|
||||||
|
// Render with custom destination rect
|
||||||
|
animation.render(ctx, { x: 0, y: 0, width: 800, height: 600 })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Supported Features
|
||||||
|
|
||||||
|
- **Embedded images** - Base64-encoded images (`data:image/png;base64,...`)
|
||||||
|
- **Embedded fonts** - Vector glyph paths for text rendering without system fonts
|
||||||
|
- **External assets** - Load images from `resourcePath` directory
|
||||||
|
- **dotLottie format** - Extract `.lottie` ZIP files at runtime (see example)
|
||||||
|
|
||||||
|
### Example: Encode Lottie to Video
|
||||||
|
|
||||||
|
See [`example/lottie-to-video.ts`](./example/lottie-to-video.ts) for encoding Lottie animations to MP4 using [`@napi-rs/webcodecs`](https://github.com/Brooooooklyn/webcodecs-node).
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { createCanvas, LottieAnimation } from '@napi-rs/canvas'
|
||||||
|
import {
|
||||||
|
VideoEncoder,
|
||||||
|
VideoFrame,
|
||||||
|
Mp4Muxer,
|
||||||
|
type EncodedVideoChunk,
|
||||||
|
type EncodedVideoChunkMetadata,
|
||||||
|
} from '@napi-rs/webcodecs'
|
||||||
|
|
||||||
|
const animation = LottieAnimation.loadFromFile('animation.json')
|
||||||
|
const canvas = createCanvas(animation.width, animation.height)
|
||||||
|
const ctx = canvas.getContext('2d')
|
||||||
|
|
||||||
|
for (let frame = 0; frame < animation.frames; frame++) {
|
||||||
|
animation.seekFrame(frame)
|
||||||
|
ctx.fillStyle = '#ffffff'
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
||||||
|
animation.render(ctx)
|
||||||
|
// Encode frame to video...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# [Example](./example/tiger.js)
|
||||||
|
|
||||||
|
> The tiger.json was serialized from [gojs/samples/tiger](https://github.com/NorthwoodsSoftware/GoJS/blob/master/samples/tiger.html)
|
||||||
|
|
||||||
|
<img width="500" src="example/tiger.png">
|
||||||
|
|
||||||
|
```shell
|
||||||
|
node example/anime-girl.js
|
||||||
|
```
|
||||||
|
|
||||||
|
| SVG | PNG |
|
||||||
|
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| <img width="500" src="example/anime-girl.svg"><br/>[CC-BY-SA 3.0](https://creativecommons.org/licenses/by/3.0) by [Niabot](https://commons.wikimedia.org/wiki/User:Niabot) | <img width="500" src="example/anime-girl.png"><br/>[CC-BY-SA 3.0](https://creativecommons.org/licenses/by/3.0) by [Niabot](https://commons.wikimedia.org/wiki/User:Niabot) |
|
||||||
|
|
||||||
|
# Building
|
||||||
|
|
||||||
|
## Build skia from source
|
||||||
|
|
||||||
|
You can build this project from source, the system requirements are here: https://skia.org/docs/user/build
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Clone the code:
|
||||||
|
$ git clone --recurse-submodules https://github.com/Brooooooklyn/canvas.git
|
||||||
|
$ cd canvas
|
||||||
|
|
||||||
|
# Build Skia:
|
||||||
|
$ node scripts/build-skia.js
|
||||||
|
|
||||||
|
# Install NPM packages, build the Node.js addon:
|
||||||
|
$ npm install -g yarn
|
||||||
|
$ yarn install --mode=skip-build # Here are modules that are used for benchmarking and are hard to install, you can skip it by specifying `--mode=skip-build`
|
||||||
|
$ sudo dnf install clang # https://fedora.pkgs.org/34/fedora-x86_64/clang-12.0.0-0.3.rc1.fc34.x86_64.rpm.html
|
||||||
|
$ yarn build
|
||||||
|
|
||||||
|
# All done! Run test cases or examples now:
|
||||||
|
$ yarn test
|
||||||
|
$ node example/tiger.js
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pull pre-build skia binary from GitHub
|
||||||
|
|
||||||
|
You can pull skia pre-build binaries if you just care the `Rust` part:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Clone the code:
|
||||||
|
$ git clone --recurse-submodules https://github.com/Brooooooklyn/canvas.git
|
||||||
|
$ cd canvas
|
||||||
|
|
||||||
|
# Download Skia binaries:
|
||||||
|
# It will pull the binaries match the git hash in `./skia` submodule
|
||||||
|
$ node scripts/release-skia-binary.mjs --download
|
||||||
|
|
||||||
|
# Install NPM packages, build the Node.js addon:
|
||||||
|
$ npm install -g yarn
|
||||||
|
$ yarn install --mode=skip-build
|
||||||
|
$ sudo dnf install clang # https://fedora.pkgs.org/34/fedora-x86_64/clang-12.0.0-0.3.rc1.fc34.x86_64.rpm.html
|
||||||
|
$ yarn build
|
||||||
|
|
||||||
|
# All done! Run test cases or examples now:
|
||||||
|
$ yarn test
|
||||||
|
$ node example/tiger.js
|
||||||
|
```
|
||||||
873
frontend/node_modules/@napi-rs/canvas/geometry.js
generated
vendored
Normal file
873
frontend/node_modules/@napi-rs/canvas/geometry.js
generated
vendored
Normal file
|
|
@ -0,0 +1,873 @@
|
||||||
|
const { inspect } = require('util')
|
||||||
|
/*
|
||||||
|
* vendored in order to fix its dependence on the window global [cds 2020/08/04]
|
||||||
|
* otherwise unchanged from https://github.com/jarek-foksa/geometry-polyfill/tree/f36bbc8f4bc43539d980687904ce46c8e915543d
|
||||||
|
*/
|
||||||
|
|
||||||
|
// @info
|
||||||
|
// DOMPoint polyfill
|
||||||
|
// @src
|
||||||
|
// https://drafts.fxtf.org/geometry/#DOMPoint
|
||||||
|
// https://github.com/chromium/chromium/blob/master/third_party/blink/renderer/core/geometry/dom_point_read_only.cc
|
||||||
|
class DOMPoint {
|
||||||
|
constructor(x = 0, y = 0, z = 0, w = 1) {
|
||||||
|
this.x = x
|
||||||
|
this.y = y
|
||||||
|
this.z = z
|
||||||
|
this.w = w
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromPoint(otherPoint) {
|
||||||
|
return new DOMPoint(
|
||||||
|
otherPoint.x,
|
||||||
|
otherPoint.y,
|
||||||
|
otherPoint.z !== undefined ? otherPoint.z : 0,
|
||||||
|
otherPoint.w !== undefined ? otherPoint.w : 1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
matrixTransform(matrix) {
|
||||||
|
if ((matrix.is2D || matrix instanceof SVGMatrix) && this.z === 0 && this.w === 1) {
|
||||||
|
return new DOMPoint(
|
||||||
|
this.x * matrix.a + this.y * matrix.c + matrix.e,
|
||||||
|
this.x * matrix.b + this.y * matrix.d + matrix.f,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return new DOMPoint(
|
||||||
|
this.x * matrix.m11 + this.y * matrix.m21 + this.z * matrix.m31 + this.w * matrix.m41,
|
||||||
|
this.x * matrix.m12 + this.y * matrix.m22 + this.z * matrix.m32 + this.w * matrix.m42,
|
||||||
|
this.x * matrix.m13 + this.y * matrix.m23 + this.z * matrix.m33 + this.w * matrix.m43,
|
||||||
|
this.x * matrix.m14 + this.y * matrix.m24 + this.z * matrix.m34 + this.w * matrix.m44,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
x: this.x,
|
||||||
|
y: this.y,
|
||||||
|
z: this.z,
|
||||||
|
w: this.w,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @info
|
||||||
|
// DOMRect polyfill
|
||||||
|
// @src
|
||||||
|
// https://drafts.fxtf.org/geometry/#DOMRect
|
||||||
|
// https://github.com/chromium/chromium/blob/master/third_party/blink/renderer/core/geometry/dom_rect_read_only.cc
|
||||||
|
|
||||||
|
class DOMRect {
|
||||||
|
constructor(x = 0, y = 0, width = 0, height = 0) {
|
||||||
|
this.x = x
|
||||||
|
this.y = y
|
||||||
|
this.width = width
|
||||||
|
this.height = height
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromRect(otherRect) {
|
||||||
|
return new DOMRect(otherRect.x, otherRect.y, otherRect.width, otherRect.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
get top() {
|
||||||
|
return this.y
|
||||||
|
}
|
||||||
|
|
||||||
|
get left() {
|
||||||
|
return this.x
|
||||||
|
}
|
||||||
|
|
||||||
|
get right() {
|
||||||
|
return this.x + this.width
|
||||||
|
}
|
||||||
|
|
||||||
|
get bottom() {
|
||||||
|
return this.y + this.height
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
x: this.x,
|
||||||
|
y: this.y,
|
||||||
|
width: this.width,
|
||||||
|
height: this.height,
|
||||||
|
top: this.top,
|
||||||
|
left: this.left,
|
||||||
|
right: this.right,
|
||||||
|
bottom: this.bottom,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const propertyName of ['top', 'right', 'bottom', 'left']) {
|
||||||
|
const propertyDescriptor = Object.getOwnPropertyDescriptor(DOMRect.prototype, propertyName)
|
||||||
|
propertyDescriptor.enumerable = true
|
||||||
|
Object.defineProperty(DOMRect.prototype, propertyName, propertyDescriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @info
|
||||||
|
// DOMMatrix polyfill (SVG 2)
|
||||||
|
// @src
|
||||||
|
// https://github.com/chromium/chromium/blob/master/third_party/blink/renderer/core/geometry/dom_matrix_read_only.cc
|
||||||
|
// https://github.com/tocharomera/generativecanvas/blob/master/node-canvas/lib/DOMMatrix.js
|
||||||
|
|
||||||
|
const M11 = 0,
|
||||||
|
M12 = 1,
|
||||||
|
M13 = 2,
|
||||||
|
M14 = 3
|
||||||
|
const M21 = 4,
|
||||||
|
M22 = 5,
|
||||||
|
M23 = 6,
|
||||||
|
M24 = 7
|
||||||
|
const M31 = 8,
|
||||||
|
M32 = 9,
|
||||||
|
M33 = 10,
|
||||||
|
M34 = 11
|
||||||
|
const M41 = 12,
|
||||||
|
M42 = 13,
|
||||||
|
M43 = 14,
|
||||||
|
M44 = 15
|
||||||
|
|
||||||
|
const A = M11,
|
||||||
|
B = M12
|
||||||
|
const C = M21,
|
||||||
|
D = M22
|
||||||
|
const E = M41,
|
||||||
|
F = M42
|
||||||
|
|
||||||
|
const DEGREE_PER_RAD = 180 / Math.PI
|
||||||
|
const RAD_PER_DEGREE = Math.PI / 180
|
||||||
|
|
||||||
|
const VALUES = Symbol('values')
|
||||||
|
const IS_2D = Symbol('is2D')
|
||||||
|
|
||||||
|
function parseMatrix(init) {
|
||||||
|
let parsed = init.replace(/matrix\(/, '').split(/,/, 7)
|
||||||
|
|
||||||
|
if (parsed.length !== 6) {
|
||||||
|
throw new Error(`Failed to parse ${init}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed = parsed.map(parseFloat)
|
||||||
|
|
||||||
|
return [parsed[0], parsed[1], 0, 0, parsed[2], parsed[3], 0, 0, 0, 0, 1, 0, parsed[4], parsed[5], 0, 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseMatrix3d(init) {
|
||||||
|
const parsed = init.replace(/matrix3d\(/, '').split(/,/, 17)
|
||||||
|
|
||||||
|
if (parsed.length !== 16) {
|
||||||
|
throw new Error(`Failed to parse ${init}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsed.map(parseFloat)
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseTransform(tform) {
|
||||||
|
const type = tform.split(/\(/, 1)[0]
|
||||||
|
|
||||||
|
if (type === 'matrix') {
|
||||||
|
return parseMatrix(tform)
|
||||||
|
} else if (type === 'matrix3d') {
|
||||||
|
return parseMatrix3d(tform)
|
||||||
|
} else {
|
||||||
|
throw new Error(`${type} parsing not implemented`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setNumber2D = (receiver, index, value) => {
|
||||||
|
if (typeof value !== 'number') {
|
||||||
|
throw new TypeError('Expected number')
|
||||||
|
}
|
||||||
|
|
||||||
|
receiver[VALUES][index] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
const setNumber3D = (receiver, index, value) => {
|
||||||
|
if (typeof value !== 'number') {
|
||||||
|
throw new TypeError('Expected number')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index === M33 || index === M44) {
|
||||||
|
if (value !== 1) {
|
||||||
|
receiver[IS_2D] = false
|
||||||
|
}
|
||||||
|
} else if (value !== 0) {
|
||||||
|
receiver[IS_2D] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
receiver[VALUES][index] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
const newInstance = (values) => {
|
||||||
|
const instance = Object.create(DOMMatrix.prototype)
|
||||||
|
instance.constructor = DOMMatrix
|
||||||
|
instance[IS_2D] = true
|
||||||
|
instance[VALUES] = values
|
||||||
|
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
|
||||||
|
const multiply = (first, second) => {
|
||||||
|
const dest = new Float64Array(16)
|
||||||
|
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
for (let j = 0; j < 4; j++) {
|
||||||
|
let sum = 0
|
||||||
|
|
||||||
|
for (let k = 0; k < 4; k++) {
|
||||||
|
sum += first[i * 4 + k] * second[k * 4 + j]
|
||||||
|
}
|
||||||
|
|
||||||
|
dest[i * 4 + j] = sum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dest
|
||||||
|
}
|
||||||
|
|
||||||
|
class DOMMatrix {
|
||||||
|
get m11() {
|
||||||
|
return this[VALUES][M11]
|
||||||
|
}
|
||||||
|
set m11(value) {
|
||||||
|
setNumber2D(this, M11, value)
|
||||||
|
}
|
||||||
|
get m12() {
|
||||||
|
return this[VALUES][M12]
|
||||||
|
}
|
||||||
|
set m12(value) {
|
||||||
|
setNumber2D(this, M12, value)
|
||||||
|
}
|
||||||
|
get m13() {
|
||||||
|
return this[VALUES][M13]
|
||||||
|
}
|
||||||
|
set m13(value) {
|
||||||
|
setNumber3D(this, M13, value)
|
||||||
|
}
|
||||||
|
get m14() {
|
||||||
|
return this[VALUES][M14]
|
||||||
|
}
|
||||||
|
set m14(value) {
|
||||||
|
setNumber3D(this, M14, value)
|
||||||
|
}
|
||||||
|
get m21() {
|
||||||
|
return this[VALUES][M21]
|
||||||
|
}
|
||||||
|
set m21(value) {
|
||||||
|
setNumber2D(this, M21, value)
|
||||||
|
}
|
||||||
|
get m22() {
|
||||||
|
return this[VALUES][M22]
|
||||||
|
}
|
||||||
|
set m22(value) {
|
||||||
|
setNumber2D(this, M22, value)
|
||||||
|
}
|
||||||
|
get m23() {
|
||||||
|
return this[VALUES][M23]
|
||||||
|
}
|
||||||
|
set m23(value) {
|
||||||
|
setNumber3D(this, M23, value)
|
||||||
|
}
|
||||||
|
get m24() {
|
||||||
|
return this[VALUES][M24]
|
||||||
|
}
|
||||||
|
set m24(value) {
|
||||||
|
setNumber3D(this, M24, value)
|
||||||
|
}
|
||||||
|
get m31() {
|
||||||
|
return this[VALUES][M31]
|
||||||
|
}
|
||||||
|
set m31(value) {
|
||||||
|
setNumber3D(this, M31, value)
|
||||||
|
}
|
||||||
|
get m32() {
|
||||||
|
return this[VALUES][M32]
|
||||||
|
}
|
||||||
|
set m32(value) {
|
||||||
|
setNumber3D(this, M32, value)
|
||||||
|
}
|
||||||
|
get m33() {
|
||||||
|
return this[VALUES][M33]
|
||||||
|
}
|
||||||
|
set m33(value) {
|
||||||
|
setNumber3D(this, M33, value)
|
||||||
|
}
|
||||||
|
get m34() {
|
||||||
|
return this[VALUES][M34]
|
||||||
|
}
|
||||||
|
set m34(value) {
|
||||||
|
setNumber3D(this, M34, value)
|
||||||
|
}
|
||||||
|
get m41() {
|
||||||
|
return this[VALUES][M41]
|
||||||
|
}
|
||||||
|
set m41(value) {
|
||||||
|
setNumber2D(this, M41, value)
|
||||||
|
}
|
||||||
|
get m42() {
|
||||||
|
return this[VALUES][M42]
|
||||||
|
}
|
||||||
|
set m42(value) {
|
||||||
|
setNumber2D(this, M42, value)
|
||||||
|
}
|
||||||
|
get m43() {
|
||||||
|
return this[VALUES][M43]
|
||||||
|
}
|
||||||
|
set m43(value) {
|
||||||
|
setNumber3D(this, M43, value)
|
||||||
|
}
|
||||||
|
get m44() {
|
||||||
|
return this[VALUES][M44]
|
||||||
|
}
|
||||||
|
set m44(value) {
|
||||||
|
setNumber3D(this, M44, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
get a() {
|
||||||
|
return this[VALUES][A]
|
||||||
|
}
|
||||||
|
set a(value) {
|
||||||
|
setNumber2D(this, A, value)
|
||||||
|
}
|
||||||
|
get b() {
|
||||||
|
return this[VALUES][B]
|
||||||
|
}
|
||||||
|
set b(value) {
|
||||||
|
setNumber2D(this, B, value)
|
||||||
|
}
|
||||||
|
get c() {
|
||||||
|
return this[VALUES][C]
|
||||||
|
}
|
||||||
|
set c(value) {
|
||||||
|
setNumber2D(this, C, value)
|
||||||
|
}
|
||||||
|
get d() {
|
||||||
|
return this[VALUES][D]
|
||||||
|
}
|
||||||
|
set d(value) {
|
||||||
|
setNumber2D(this, D, value)
|
||||||
|
}
|
||||||
|
get e() {
|
||||||
|
return this[VALUES][E]
|
||||||
|
}
|
||||||
|
set e(value) {
|
||||||
|
setNumber2D(this, E, value)
|
||||||
|
}
|
||||||
|
get f() {
|
||||||
|
return this[VALUES][F]
|
||||||
|
}
|
||||||
|
set f(value) {
|
||||||
|
setNumber2D(this, F, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
get is2D() {
|
||||||
|
return this[IS_2D]
|
||||||
|
}
|
||||||
|
|
||||||
|
get isIdentity() {
|
||||||
|
const values = this[VALUES]
|
||||||
|
|
||||||
|
return (
|
||||||
|
values[M11] === 1 &&
|
||||||
|
values[M12] === 0 &&
|
||||||
|
values[M13] === 0 &&
|
||||||
|
values[M14] === 0 &&
|
||||||
|
values[M21] === 0 &&
|
||||||
|
values[M22] === 1 &&
|
||||||
|
values[M23] === 0 &&
|
||||||
|
values[M24] === 0 &&
|
||||||
|
values[M31] === 0 &&
|
||||||
|
values[M32] === 0 &&
|
||||||
|
values[M33] === 1 &&
|
||||||
|
values[M34] === 0 &&
|
||||||
|
values[M41] === 0 &&
|
||||||
|
values[M42] === 0 &&
|
||||||
|
values[M43] === 0 &&
|
||||||
|
values[M44] === 1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromMatrix(init) {
|
||||||
|
if (init instanceof DOMMatrix) {
|
||||||
|
return new DOMMatrix(init[VALUES])
|
||||||
|
} else if (init instanceof SVGMatrix) {
|
||||||
|
return new DOMMatrix([init.a, init.b, init.c, init.d, init.e, init.f])
|
||||||
|
} else {
|
||||||
|
throw new TypeError('Expected DOMMatrix')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromFloat32Array(init) {
|
||||||
|
if (!(init instanceof Float32Array)) throw new TypeError('Expected Float32Array')
|
||||||
|
return new DOMMatrix(init)
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromFloat64Array(init) {
|
||||||
|
if (!(init instanceof Float64Array)) throw new TypeError('Expected Float64Array')
|
||||||
|
return new DOMMatrix(init)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @type
|
||||||
|
// (Float64Array) => void
|
||||||
|
constructor(init) {
|
||||||
|
this[IS_2D] = true
|
||||||
|
|
||||||
|
this[VALUES] = new Float64Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])
|
||||||
|
|
||||||
|
// Parse CSS transformList
|
||||||
|
if (typeof init === 'string') {
|
||||||
|
if (init === '') {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
const tforms = init.split(/\)\s+/, 20).map(parseTransform)
|
||||||
|
|
||||||
|
if (tforms.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
init = tforms[0]
|
||||||
|
|
||||||
|
for (let i = 1; i < tforms.length; i++) {
|
||||||
|
init = multiply(tforms[i], init)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = 0
|
||||||
|
|
||||||
|
if (init && init.length === 6) {
|
||||||
|
setNumber2D(this, A, init[i++])
|
||||||
|
setNumber2D(this, B, init[i++])
|
||||||
|
setNumber2D(this, C, init[i++])
|
||||||
|
setNumber2D(this, D, init[i++])
|
||||||
|
setNumber2D(this, E, init[i++])
|
||||||
|
setNumber2D(this, F, init[i++])
|
||||||
|
} else if (init && init.length === 16) {
|
||||||
|
setNumber2D(this, M11, init[i++])
|
||||||
|
setNumber2D(this, M12, init[i++])
|
||||||
|
setNumber3D(this, M13, init[i++])
|
||||||
|
setNumber3D(this, M14, init[i++])
|
||||||
|
setNumber2D(this, M21, init[i++])
|
||||||
|
setNumber2D(this, M22, init[i++])
|
||||||
|
setNumber3D(this, M23, init[i++])
|
||||||
|
setNumber3D(this, M24, init[i++])
|
||||||
|
setNumber3D(this, M31, init[i++])
|
||||||
|
setNumber3D(this, M32, init[i++])
|
||||||
|
setNumber3D(this, M33, init[i++])
|
||||||
|
setNumber3D(this, M34, init[i++])
|
||||||
|
setNumber2D(this, M41, init[i++])
|
||||||
|
setNumber2D(this, M42, init[i++])
|
||||||
|
setNumber3D(this, M43, init[i++])
|
||||||
|
setNumber3D(this, M44, init[i])
|
||||||
|
} else if (init !== undefined) {
|
||||||
|
throw new TypeError('Expected string or array.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dump() {
|
||||||
|
const mat = this[VALUES]
|
||||||
|
console.info([mat.slice(0, 4), mat.slice(4, 8), mat.slice(8, 12), mat.slice(12, 16)])
|
||||||
|
}
|
||||||
|
|
||||||
|
[inspect.custom](depth) {
|
||||||
|
if (depth < 0) return '[DOMMatrix]'
|
||||||
|
|
||||||
|
const { a, b, c, d, e, f, is2D, isIdentity } = this
|
||||||
|
if (this.is2D) {
|
||||||
|
return `DOMMatrix ${inspect({ a, b, c, d, e, f, is2D, isIdentity }, { colors: true })}`
|
||||||
|
} else {
|
||||||
|
const { m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44, is2D, isIdentity } = this
|
||||||
|
return `DOMMatrix ${inspect(
|
||||||
|
{
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c,
|
||||||
|
d,
|
||||||
|
e,
|
||||||
|
f,
|
||||||
|
m11,
|
||||||
|
m12,
|
||||||
|
m13,
|
||||||
|
m14,
|
||||||
|
m21,
|
||||||
|
m22,
|
||||||
|
m23,
|
||||||
|
m24,
|
||||||
|
m31,
|
||||||
|
m32,
|
||||||
|
m33,
|
||||||
|
m34,
|
||||||
|
m41,
|
||||||
|
m42,
|
||||||
|
m43,
|
||||||
|
m44,
|
||||||
|
is2D,
|
||||||
|
isIdentity,
|
||||||
|
},
|
||||||
|
{ colors: true },
|
||||||
|
)}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
multiply(other) {
|
||||||
|
return newInstance(this[VALUES]).multiplySelf(other)
|
||||||
|
}
|
||||||
|
|
||||||
|
multiplySelf(other) {
|
||||||
|
this[VALUES] = multiply(other[VALUES], this[VALUES])
|
||||||
|
|
||||||
|
if (!other.is2D) {
|
||||||
|
this[IS_2D] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
preMultiplySelf(other) {
|
||||||
|
this[VALUES] = multiply(this[VALUES], other[VALUES])
|
||||||
|
|
||||||
|
if (!other.is2D) {
|
||||||
|
this[IS_2D] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
translate(tx, ty, tz) {
|
||||||
|
return newInstance(this[VALUES]).translateSelf(tx, ty, tz)
|
||||||
|
}
|
||||||
|
|
||||||
|
translateSelf(tx = 0, ty = 0, tz = 0) {
|
||||||
|
this[VALUES] = multiply([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, tx, ty, tz, 1], this[VALUES])
|
||||||
|
|
||||||
|
if (tz !== 0) {
|
||||||
|
this[IS_2D] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
scale(scaleX, scaleY, scaleZ, originX, originY, originZ) {
|
||||||
|
return newInstance(this[VALUES]).scaleSelf(scaleX, scaleY, scaleZ, originX, originY, originZ)
|
||||||
|
}
|
||||||
|
|
||||||
|
scale3d(scale, originX, originY, originZ) {
|
||||||
|
return newInstance(this[VALUES]).scale3dSelf(scale, originX, originY, originZ)
|
||||||
|
}
|
||||||
|
|
||||||
|
scale3dSelf(scale, originX, originY, originZ) {
|
||||||
|
return this.scaleSelf(scale, scale, scale, originX, originY, originZ)
|
||||||
|
}
|
||||||
|
|
||||||
|
scaleSelf(scaleX, scaleY, scaleZ, originX, originY, originZ) {
|
||||||
|
// Not redundant with translate's checks because we need to negate the values later.
|
||||||
|
if (typeof originX !== 'number') originX = 0
|
||||||
|
if (typeof originY !== 'number') originY = 0
|
||||||
|
if (typeof originZ !== 'number') originZ = 0
|
||||||
|
|
||||||
|
this.translateSelf(originX, originY, originZ)
|
||||||
|
|
||||||
|
if (typeof scaleX !== 'number') scaleX = 1
|
||||||
|
if (typeof scaleY !== 'number') scaleY = scaleX
|
||||||
|
if (typeof scaleZ !== 'number') scaleZ = 1
|
||||||
|
|
||||||
|
this[VALUES] = multiply([scaleX, 0, 0, 0, 0, scaleY, 0, 0, 0, 0, scaleZ, 0, 0, 0, 0, 1], this[VALUES])
|
||||||
|
|
||||||
|
this.translateSelf(-originX, -originY, -originZ)
|
||||||
|
|
||||||
|
if (scaleZ !== 1 || originZ !== 0) {
|
||||||
|
this[IS_2D] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
rotateFromVector(x, y) {
|
||||||
|
return newInstance(this[VALUES]).rotateFromVectorSelf(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
rotateFromVectorSelf(x = 0, y = 0) {
|
||||||
|
const theta = x === 0 && y === 0 ? 0 : Math.atan2(y, x) * DEGREE_PER_RAD
|
||||||
|
return this.rotateSelf(theta)
|
||||||
|
}
|
||||||
|
|
||||||
|
rotate(rotX, rotY, rotZ) {
|
||||||
|
return newInstance(this[VALUES]).rotateSelf(rotX, rotY, rotZ)
|
||||||
|
}
|
||||||
|
|
||||||
|
rotateSelf(rotX, rotY, rotZ) {
|
||||||
|
if (rotY === undefined && rotZ === undefined) {
|
||||||
|
rotZ = rotX
|
||||||
|
rotX = rotY = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof rotY !== 'number') rotY = 0
|
||||||
|
if (typeof rotZ !== 'number') rotZ = 0
|
||||||
|
|
||||||
|
if (rotX !== 0 || rotY !== 0) {
|
||||||
|
this[IS_2D] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
rotX *= RAD_PER_DEGREE
|
||||||
|
rotY *= RAD_PER_DEGREE
|
||||||
|
rotZ *= RAD_PER_DEGREE
|
||||||
|
|
||||||
|
let c = Math.cos(rotZ)
|
||||||
|
let s = Math.sin(rotZ)
|
||||||
|
|
||||||
|
this[VALUES] = multiply([c, s, 0, 0, -s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], this[VALUES])
|
||||||
|
|
||||||
|
c = Math.cos(rotY)
|
||||||
|
s = Math.sin(rotY)
|
||||||
|
|
||||||
|
this[VALUES] = multiply([c, 0, -s, 0, 0, 1, 0, 0, s, 0, c, 0, 0, 0, 0, 1], this[VALUES])
|
||||||
|
|
||||||
|
c = Math.cos(rotX)
|
||||||
|
s = Math.sin(rotX)
|
||||||
|
|
||||||
|
this[VALUES] = multiply([1, 0, 0, 0, 0, c, s, 0, 0, -s, c, 0, 0, 0, 0, 1], this[VALUES])
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
rotateAxisAngle(x, y, z, angle) {
|
||||||
|
return newInstance(this[VALUES]).rotateAxisAngleSelf(x, y, z, angle)
|
||||||
|
}
|
||||||
|
|
||||||
|
rotateAxisAngleSelf(x = 0, y = 0, z = 0, angle = 0) {
|
||||||
|
const length = Math.sqrt(x * x + y * y + z * z)
|
||||||
|
|
||||||
|
if (length === 0) {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length !== 1) {
|
||||||
|
x /= length
|
||||||
|
y /= length
|
||||||
|
z /= length
|
||||||
|
}
|
||||||
|
|
||||||
|
angle *= RAD_PER_DEGREE
|
||||||
|
|
||||||
|
const c = Math.cos(angle)
|
||||||
|
const s = Math.sin(angle)
|
||||||
|
const t = 1 - c
|
||||||
|
const tx = t * x
|
||||||
|
const ty = t * y
|
||||||
|
|
||||||
|
this[VALUES] = multiply(
|
||||||
|
[
|
||||||
|
tx * x + c,
|
||||||
|
tx * y + s * z,
|
||||||
|
tx * z - s * y,
|
||||||
|
0,
|
||||||
|
tx * y - s * z,
|
||||||
|
ty * y + c,
|
||||||
|
ty * z + s * x,
|
||||||
|
0,
|
||||||
|
tx * z + s * y,
|
||||||
|
ty * z - s * x,
|
||||||
|
t * z * z + c,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
this[VALUES],
|
||||||
|
)
|
||||||
|
|
||||||
|
if (x !== 0 || y !== 0) {
|
||||||
|
this[IS_2D] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
skewX(sx) {
|
||||||
|
return newInstance(this[VALUES]).skewXSelf(sx)
|
||||||
|
}
|
||||||
|
|
||||||
|
skewXSelf(sx) {
|
||||||
|
if (typeof sx !== 'number') {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
const t = Math.tan(sx * RAD_PER_DEGREE)
|
||||||
|
|
||||||
|
this[VALUES] = multiply([1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], this[VALUES])
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
skewY(sy) {
|
||||||
|
return newInstance(this[VALUES]).skewYSelf(sy)
|
||||||
|
}
|
||||||
|
|
||||||
|
skewYSelf(sy) {
|
||||||
|
if (typeof sy !== 'number') {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
const t = Math.tan(sy * RAD_PER_DEGREE)
|
||||||
|
|
||||||
|
this[VALUES] = multiply([1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], this[VALUES])
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
flipX() {
|
||||||
|
return newInstance(multiply([-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], this[VALUES]))
|
||||||
|
}
|
||||||
|
|
||||||
|
flipY() {
|
||||||
|
return newInstance(multiply([1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], this[VALUES]))
|
||||||
|
}
|
||||||
|
|
||||||
|
inverse() {
|
||||||
|
return newInstance(this[VALUES].slice()).invertSelf()
|
||||||
|
}
|
||||||
|
|
||||||
|
invertSelf() {
|
||||||
|
if (this[IS_2D]) {
|
||||||
|
const det = this[VALUES][A] * this[VALUES][D] - this[VALUES][B] * this[VALUES][C]
|
||||||
|
|
||||||
|
// Invertable
|
||||||
|
if (det !== 0) {
|
||||||
|
const newA = this[VALUES][D] / det
|
||||||
|
const newB = -this[VALUES][B] / det
|
||||||
|
const newC = -this[VALUES][C] / det
|
||||||
|
const newD = this[VALUES][A] / det
|
||||||
|
const newE = (this[VALUES][C] * this[VALUES][F] - this[VALUES][D] * this[VALUES][E]) / det
|
||||||
|
const newF = (this[VALUES][B] * this[VALUES][E] - this[VALUES][A] * this[VALUES][F]) / det
|
||||||
|
|
||||||
|
this.a = newA
|
||||||
|
this.b = newB
|
||||||
|
this.c = newC
|
||||||
|
this.d = newD
|
||||||
|
this.e = newE
|
||||||
|
this.f = newF
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not invertable
|
||||||
|
else {
|
||||||
|
this[IS_2D] = false
|
||||||
|
|
||||||
|
this[VALUES] = [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN]
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('3D matrix inversion is not implemented.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setMatrixValue(transformList) {
|
||||||
|
const temp = new DOMMatrix(transformList)
|
||||||
|
|
||||||
|
this[VALUES] = temp[VALUES]
|
||||||
|
this[IS_2D] = temp[IS_2D]
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
transformPoint(point) {
|
||||||
|
const x = point.x || 0
|
||||||
|
const y = point.y || 0
|
||||||
|
const z = point.z || 0
|
||||||
|
const w = point.w || 1
|
||||||
|
|
||||||
|
const values = this[VALUES]
|
||||||
|
|
||||||
|
const nx = values[M11] * x + values[M21] * y + values[M31] * z + values[M41] * w
|
||||||
|
const ny = values[M12] * x + values[M22] * y + values[M32] * z + values[M42] * w
|
||||||
|
const nz = values[M13] * x + values[M23] * y + values[M33] * z + values[M43] * w
|
||||||
|
const nw = values[M14] * x + values[M24] * y + values[M34] * z + values[M44] * w
|
||||||
|
|
||||||
|
return new DOMPoint(nx, ny, nz, nw)
|
||||||
|
}
|
||||||
|
|
||||||
|
toFloat32Array() {
|
||||||
|
return Float32Array.from(this[VALUES])
|
||||||
|
}
|
||||||
|
|
||||||
|
toFloat64Array() {
|
||||||
|
return this[VALUES].slice(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
a: this.a,
|
||||||
|
b: this.b,
|
||||||
|
c: this.c,
|
||||||
|
d: this.d,
|
||||||
|
e: this.e,
|
||||||
|
f: this.f,
|
||||||
|
m11: this.m11,
|
||||||
|
m12: this.m12,
|
||||||
|
m13: this.m13,
|
||||||
|
m14: this.m14,
|
||||||
|
m21: this.m21,
|
||||||
|
m22: this.m22,
|
||||||
|
m23: this.m23,
|
||||||
|
m24: this.m24,
|
||||||
|
m31: this.m31,
|
||||||
|
m32: this.m32,
|
||||||
|
m33: this.m33,
|
||||||
|
m34: this.m34,
|
||||||
|
m41: this.m41,
|
||||||
|
m42: this.m42,
|
||||||
|
m43: this.m43,
|
||||||
|
m44: this.m44,
|
||||||
|
is2D: this.is2D,
|
||||||
|
isIdentity: this.isIdentity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
if (this.is2D) {
|
||||||
|
return `matrix(${this.a}, ${this.b}, ${this.c}, ${this.d}, ${this.e}, ${this.f})`
|
||||||
|
} else {
|
||||||
|
return `matrix3d(${this[VALUES].join(', ')})`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const propertyName of [
|
||||||
|
'a',
|
||||||
|
'b',
|
||||||
|
'c',
|
||||||
|
'd',
|
||||||
|
'e',
|
||||||
|
'f',
|
||||||
|
'm11',
|
||||||
|
'm12',
|
||||||
|
'm13',
|
||||||
|
'm14',
|
||||||
|
'm21',
|
||||||
|
'm22',
|
||||||
|
'm23',
|
||||||
|
'm24',
|
||||||
|
'm31',
|
||||||
|
'm32',
|
||||||
|
'm33',
|
||||||
|
'm34',
|
||||||
|
'm41',
|
||||||
|
'm42',
|
||||||
|
'm43',
|
||||||
|
'm44',
|
||||||
|
'is2D',
|
||||||
|
'isIdentity',
|
||||||
|
]) {
|
||||||
|
const propertyDescriptor = Object.getOwnPropertyDescriptor(DOMMatrix.prototype, propertyName)
|
||||||
|
propertyDescriptor.enumerable = true
|
||||||
|
Object.defineProperty(DOMMatrix.prototype, propertyName, propertyDescriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { DOMPoint, DOMMatrix, DOMRect }
|
||||||
1097
frontend/node_modules/@napi-rs/canvas/index.d.ts
generated
vendored
Normal file
1097
frontend/node_modules/@napi-rs/canvas/index.d.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
196
frontend/node_modules/@napi-rs/canvas/index.js
generated
vendored
Normal file
196
frontend/node_modules/@napi-rs/canvas/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,196 @@
|
||||||
|
const { platform, homedir } = require('os')
|
||||||
|
const { join } = require('path')
|
||||||
|
|
||||||
|
const {
|
||||||
|
clearAllCache,
|
||||||
|
CanvasRenderingContext2D,
|
||||||
|
CanvasElement,
|
||||||
|
SVGCanvas,
|
||||||
|
Path: Path2D,
|
||||||
|
ImageData,
|
||||||
|
Image,
|
||||||
|
FontKey,
|
||||||
|
GlobalFonts,
|
||||||
|
PathOp,
|
||||||
|
FillType,
|
||||||
|
StrokeJoin,
|
||||||
|
StrokeCap,
|
||||||
|
convertSVGTextToPath,
|
||||||
|
PdfDocument,
|
||||||
|
GifEncoder,
|
||||||
|
GifDisposal,
|
||||||
|
LottieAnimation,
|
||||||
|
} = require('./js-binding')
|
||||||
|
|
||||||
|
const { DOMPoint, DOMMatrix, DOMRect } = require('./geometry')
|
||||||
|
|
||||||
|
const loadImage = require('./load-image')
|
||||||
|
|
||||||
|
// Add Symbol.dispose support for GifEncoder (ECMAScript 2024 Explicit Resource Management)
|
||||||
|
if (GifEncoder && typeof Symbol.dispose !== 'undefined') {
|
||||||
|
GifEncoder.prototype[Symbol.dispose] = function () {
|
||||||
|
this.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SvgExportFlag = {
|
||||||
|
ConvertTextToPaths: 0x01,
|
||||||
|
NoPrettyXML: 0x02,
|
||||||
|
RelativePathEncoding: 0x04,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!('families' in GlobalFonts)) {
|
||||||
|
Object.defineProperty(GlobalFonts, 'families', {
|
||||||
|
get: function () {
|
||||||
|
return JSON.parse(GlobalFonts.getFamilies().toString())
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!('has' in GlobalFonts)) {
|
||||||
|
Object.defineProperty(GlobalFonts, 'has', {
|
||||||
|
value: function has(name) {
|
||||||
|
return !!JSON.parse(GlobalFonts.getFamilies().toString()).find(({ family }) => family === name)
|
||||||
|
},
|
||||||
|
configurable: false,
|
||||||
|
enumerable: false,
|
||||||
|
writable: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const _toBlob = CanvasElement.prototype.toBlob
|
||||||
|
const _convertToBlob = CanvasElement.prototype.convertToBlob
|
||||||
|
if ('Blob' in globalThis) {
|
||||||
|
CanvasElement.prototype.toBlob = function toBlob(callback, mimeType, quality) {
|
||||||
|
_toBlob.call(
|
||||||
|
this,
|
||||||
|
function (/** @type {Uint8Array} */ imageBuffer) {
|
||||||
|
const blob = new Blob([imageBuffer.buffer], { type: mimeType })
|
||||||
|
callback(blob)
|
||||||
|
},
|
||||||
|
mimeType,
|
||||||
|
quality,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
CanvasElement.prototype.convertToBlob = function convertToBlob(options) {
|
||||||
|
return _convertToBlob.call(this, options).then((/** @type {Uint8Array} */ imageBuffer) => {
|
||||||
|
const blob = new Blob([imageBuffer.buffer], { type: options?.mime || 'image/png' })
|
||||||
|
return blob
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// oxlint-disable-next-line no-unused-vars
|
||||||
|
CanvasElement.prototype.toBlob = function toBlob(callback, mimeType, quality) {
|
||||||
|
callback(null)
|
||||||
|
}
|
||||||
|
// oxlint-disable-next-line no-unused-vars
|
||||||
|
CanvasElement.prototype.convertToBlob = function convertToBlob(options) {
|
||||||
|
return Promise.reject(new Error('Blob is not supported in this environment'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const _getTransform = CanvasRenderingContext2D.prototype.getTransform
|
||||||
|
|
||||||
|
CanvasRenderingContext2D.prototype.getTransform = function getTransform() {
|
||||||
|
const transform = _getTransform.apply(this, arguments)
|
||||||
|
// monkey patched, skip
|
||||||
|
if (transform instanceof DOMMatrix) {
|
||||||
|
return transform
|
||||||
|
}
|
||||||
|
const { a, b, c, d, e, f } = transform
|
||||||
|
return new DOMMatrix([a, b, c, d, e, f])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workaround for webpack bundling issue with drawImage
|
||||||
|
// Store the original drawImage method
|
||||||
|
const _drawImage = CanvasRenderingContext2D.prototype.drawImage
|
||||||
|
|
||||||
|
// Override drawImage to ensure proper type recognition in bundled environments
|
||||||
|
CanvasRenderingContext2D.prototype.drawImage = function drawImage(image, ...args) {
|
||||||
|
// If the image is a Canvas-like object but not recognized due to bundling,
|
||||||
|
// we need to ensure it's properly identified
|
||||||
|
if (image && typeof image === 'object') {
|
||||||
|
// First check if it's a wrapped canvas object
|
||||||
|
if (image.canvas instanceof CanvasElement || image.canvas instanceof SVGCanvas) {
|
||||||
|
image = image.canvas
|
||||||
|
} else if (image._canvas instanceof CanvasElement || image._canvas instanceof SVGCanvas) {
|
||||||
|
image = image._canvas
|
||||||
|
}
|
||||||
|
// Then check if it's a Canvas-like object by checking for getContext method
|
||||||
|
else if (typeof image.getContext === 'function' && image.width && image.height) {
|
||||||
|
// If it has canvas properties but isn't recognized as CanvasElement or SVGCanvas,
|
||||||
|
// try to correct the prototype chain
|
||||||
|
if (!(image instanceof CanvasElement) && !(image instanceof SVGCanvas)) {
|
||||||
|
// Try to create a proper CanvasElement from the canvas-like object
|
||||||
|
// This helps when webpack has transformed the prototype chain
|
||||||
|
Object.setPrototypeOf(image, CanvasElement.prototype)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the original drawImage with the potentially corrected image
|
||||||
|
return _drawImage.apply(this, [image, ...args])
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCanvas(width, height, flag) {
|
||||||
|
const isSvgBackend = typeof flag !== 'undefined'
|
||||||
|
return isSvgBackend ? new SVGCanvas(width, height, flag) : new CanvasElement(width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Canvas {
|
||||||
|
constructor(width, height, flag) {
|
||||||
|
return createCanvas(width, height, flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
static [Symbol.hasInstance](instance) {
|
||||||
|
return instance instanceof CanvasElement || instance instanceof SVGCanvas
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process.env.DISABLE_SYSTEM_FONTS_LOAD) {
|
||||||
|
GlobalFonts.loadSystemFonts()
|
||||||
|
const platformName = platform()
|
||||||
|
const homedirPath = homedir()
|
||||||
|
switch (platformName) {
|
||||||
|
case 'win32':
|
||||||
|
GlobalFonts.loadFontsFromDir(join(homedirPath, 'AppData', 'Local', 'Microsoft', 'Windows', 'Fonts'))
|
||||||
|
break
|
||||||
|
case 'darwin':
|
||||||
|
GlobalFonts.loadFontsFromDir(join(homedirPath, 'Library', 'Fonts'))
|
||||||
|
break
|
||||||
|
case 'linux':
|
||||||
|
GlobalFonts.loadFontsFromDir(join('usr', 'local', 'share', 'fonts'))
|
||||||
|
GlobalFonts.loadFontsFromDir(join(homedirPath, '.fonts'))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
clearAllCache,
|
||||||
|
Canvas,
|
||||||
|
createCanvas,
|
||||||
|
Path2D,
|
||||||
|
ImageData,
|
||||||
|
Image,
|
||||||
|
PathOp,
|
||||||
|
FillType,
|
||||||
|
StrokeCap,
|
||||||
|
StrokeJoin,
|
||||||
|
SvgExportFlag,
|
||||||
|
GlobalFonts: GlobalFonts,
|
||||||
|
convertSVGTextToPath,
|
||||||
|
DOMPoint,
|
||||||
|
DOMMatrix,
|
||||||
|
DOMRect,
|
||||||
|
loadImage,
|
||||||
|
FontKey,
|
||||||
|
// Export these for better webpack compatibility
|
||||||
|
CanvasElement,
|
||||||
|
SVGCanvas,
|
||||||
|
PDFDocument: PdfDocument,
|
||||||
|
// GIF encoding
|
||||||
|
GifEncoder,
|
||||||
|
GifDisposal,
|
||||||
|
// Lottie animation
|
||||||
|
LottieAnimation,
|
||||||
|
}
|
||||||
464
frontend/node_modules/@napi-rs/canvas/js-binding.js
generated
vendored
Normal file
464
frontend/node_modules/@napi-rs/canvas/js-binding.js
generated
vendored
Normal file
|
|
@ -0,0 +1,464 @@
|
||||||
|
// prettier-ignore
|
||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
/* auto-generated by NAPI-RS */
|
||||||
|
|
||||||
|
const { readFileSync } = require('fs')
|
||||||
|
let nativeBinding = null
|
||||||
|
const loadErrors = []
|
||||||
|
|
||||||
|
const isMusl = () => {
|
||||||
|
let musl = false
|
||||||
|
if (process.platform === 'linux') {
|
||||||
|
musl = isMuslFromFilesystem()
|
||||||
|
if (musl === null) {
|
||||||
|
musl = isMuslFromReport()
|
||||||
|
}
|
||||||
|
if (musl === null) {
|
||||||
|
musl = isMuslFromChildProcess()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return musl
|
||||||
|
}
|
||||||
|
|
||||||
|
const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-')
|
||||||
|
|
||||||
|
const isMuslFromFilesystem = () => {
|
||||||
|
try {
|
||||||
|
return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl')
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isMuslFromReport = () => {
|
||||||
|
let report = null
|
||||||
|
if (typeof process.report?.getReport === 'function') {
|
||||||
|
process.report.excludeNetwork = true
|
||||||
|
report = process.report.getReport()
|
||||||
|
}
|
||||||
|
if (!report) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (report.header && report.header.glibcVersionRuntime) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (Array.isArray(report.sharedObjects)) {
|
||||||
|
if (report.sharedObjects.some(isFileMusl)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const isMuslFromChildProcess = () => {
|
||||||
|
try {
|
||||||
|
return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl')
|
||||||
|
} catch (e) {
|
||||||
|
// If we reach this case, we don't know if the system is musl or not, so is better to just fallback to false
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function requireNative() {
|
||||||
|
if (process.env.NAPI_RS_NATIVE_LIBRARY_PATH) {
|
||||||
|
try {
|
||||||
|
return require(process.env.NAPI_RS_NATIVE_LIBRARY_PATH)
|
||||||
|
} catch (err) {
|
||||||
|
loadErrors.push(err)
|
||||||
|
}
|
||||||
|
} else if (process.platform === 'android') {
|
||||||
|
if (process.arch === 'arm64') {
|
||||||
|
try {
|
||||||
|
return require('./skia.android-arm64.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-android-arm64')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'arm') {
|
||||||
|
try {
|
||||||
|
return require('./skia.android-arm-eabi.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-android-arm-eabi')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadErrors.push(new Error(`Unsupported architecture on Android ${process.arch}`))
|
||||||
|
}
|
||||||
|
} else if (process.platform === 'win32') {
|
||||||
|
if (process.arch === 'x64') {
|
||||||
|
if (
|
||||||
|
process.config?.variables?.shlib_suffix === 'dll.a' ||
|
||||||
|
process.config?.variables?.node_target_type === 'shared_library'
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return require('./skia.win32-x64-gnu.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-win32-x64-gnu')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return require('./skia.win32-x64-msvc.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-win32-x64-msvc')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'ia32') {
|
||||||
|
try {
|
||||||
|
return require('./skia.win32-ia32-msvc.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-win32-ia32-msvc')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'arm64') {
|
||||||
|
try {
|
||||||
|
return require('./skia.win32-arm64-msvc.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-win32-arm64-msvc')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadErrors.push(new Error(`Unsupported architecture on Windows: ${process.arch}`))
|
||||||
|
}
|
||||||
|
} else if (process.platform === 'darwin') {
|
||||||
|
try {
|
||||||
|
return require('./skia.darwin-universal.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-darwin-universal')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
if (process.arch === 'x64') {
|
||||||
|
try {
|
||||||
|
return require('./skia.darwin-x64.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-darwin-x64')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'arm64') {
|
||||||
|
try {
|
||||||
|
return require('./skia.darwin-arm64.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-darwin-arm64')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadErrors.push(new Error(`Unsupported architecture on macOS: ${process.arch}`))
|
||||||
|
}
|
||||||
|
} else if (process.platform === 'freebsd') {
|
||||||
|
if (process.arch === 'x64') {
|
||||||
|
try {
|
||||||
|
return require('./skia.freebsd-x64.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-freebsd-x64')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'arm64') {
|
||||||
|
try {
|
||||||
|
return require('./skia.freebsd-arm64.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-freebsd-arm64')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadErrors.push(new Error(`Unsupported architecture on FreeBSD: ${process.arch}`))
|
||||||
|
}
|
||||||
|
} else if (process.platform === 'linux') {
|
||||||
|
if (process.arch === 'x64') {
|
||||||
|
if (isMusl()) {
|
||||||
|
try {
|
||||||
|
return require('./skia.linux-x64-musl.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-linux-x64-musl')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return require('./skia.linux-x64-gnu.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-linux-x64-gnu')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'arm64') {
|
||||||
|
if (isMusl()) {
|
||||||
|
try {
|
||||||
|
return require('./skia.linux-arm64-musl.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-linux-arm64-musl')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return require('./skia.linux-arm64-gnu.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-linux-arm64-gnu')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'arm') {
|
||||||
|
if (isMusl()) {
|
||||||
|
try {
|
||||||
|
return require('./skia.linux-arm-musleabihf.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-linux-arm-musleabihf')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return require('./skia.linux-arm-gnueabihf.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-linux-arm-gnueabihf')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'loong64') {
|
||||||
|
if (isMusl()) {
|
||||||
|
try {
|
||||||
|
return require('./skia.linux-loong64-musl.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-linux-loong64-musl')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return require('./skia.linux-loong64-gnu.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-linux-loong64-gnu')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'riscv64') {
|
||||||
|
if (isMusl()) {
|
||||||
|
try {
|
||||||
|
return require('./skia.linux-riscv64-musl.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-linux-riscv64-musl')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return require('./skia.linux-riscv64-gnu.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-linux-riscv64-gnu')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'ppc64') {
|
||||||
|
try {
|
||||||
|
return require('./skia.linux-ppc64-gnu.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-linux-ppc64-gnu')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else if (process.arch === 's390x') {
|
||||||
|
try {
|
||||||
|
return require('./skia.linux-s390x-gnu.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-linux-s390x-gnu')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadErrors.push(new Error(`Unsupported architecture on Linux: ${process.arch}`))
|
||||||
|
}
|
||||||
|
} else if (process.platform === 'openharmony') {
|
||||||
|
if (process.arch === 'arm64') {
|
||||||
|
try {
|
||||||
|
return require('./skia.openharmony-arm64.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-openharmony-arm64')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'x64') {
|
||||||
|
try {
|
||||||
|
return require('./skia.openharmony-x64.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require('@napi-rs/canvas-openharmony-x64')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'arm') {
|
||||||
|
try {
|
||||||
|
return require('./skia.openharmony-arm.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@napi-rs/canvas-openharmony-arm')
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadErrors.push(new Error(`Unsupported architecture on OpenHarmony: ${process.arch}`))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadErrors.push(new Error(`Unsupported OS: ${process.platform}, architecture: ${process.arch}`))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nativeBinding = requireNative()
|
||||||
|
|
||||||
|
if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) {
|
||||||
|
let wasiBinding = null
|
||||||
|
let wasiBindingError = null
|
||||||
|
try {
|
||||||
|
wasiBinding = require('./skia.wasi.cjs')
|
||||||
|
nativeBinding = wasiBinding
|
||||||
|
} catch (err) {
|
||||||
|
if (process.env.NAPI_RS_FORCE_WASI) {
|
||||||
|
wasiBindingError = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!nativeBinding) {
|
||||||
|
try {
|
||||||
|
wasiBinding = require('@napi-rs/canvas-wasm32-wasi')
|
||||||
|
nativeBinding = wasiBinding
|
||||||
|
} catch (err) {
|
||||||
|
if (process.env.NAPI_RS_FORCE_WASI) {
|
||||||
|
wasiBindingError.cause = err
|
||||||
|
loadErrors.push(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nativeBinding) {
|
||||||
|
if (loadErrors.length > 0) {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot find native binding. ` +
|
||||||
|
`npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). ` +
|
||||||
|
'Please try `npm i` again after removing both package-lock.json and node_modules directory.',
|
||||||
|
{
|
||||||
|
cause: loadErrors.reduce((err, cur) => {
|
||||||
|
cur.cause = err
|
||||||
|
return cur
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
throw new Error(`Failed to load native binding`)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = nativeBinding
|
||||||
|
module.exports.GlobalFonts = nativeBinding.GlobalFonts
|
||||||
|
module.exports.CanvasElement = nativeBinding.CanvasElement
|
||||||
|
module.exports.CanvasGradient = nativeBinding.CanvasGradient
|
||||||
|
module.exports.CanvasPattern = nativeBinding.CanvasPattern
|
||||||
|
module.exports.CanvasRenderingContext2D = nativeBinding.CanvasRenderingContext2D
|
||||||
|
module.exports.FontKey = nativeBinding.FontKey
|
||||||
|
module.exports.Image = nativeBinding.Image
|
||||||
|
module.exports.ImageData = nativeBinding.ImageData
|
||||||
|
module.exports.Path = nativeBinding.Path
|
||||||
|
module.exports.PdfDocument = nativeBinding.PdfDocument
|
||||||
|
module.exports.SVGCanvas = nativeBinding.SVGCanvas
|
||||||
|
module.exports.ChromaSubsampling = nativeBinding.ChromaSubsampling
|
||||||
|
module.exports.clearAllCache = nativeBinding.clearAllCache
|
||||||
|
module.exports.convertSVGTextToPath = nativeBinding.convertSVGTextToPath
|
||||||
|
module.exports.FillType = nativeBinding.FillType
|
||||||
|
module.exports.PathOp = nativeBinding.PathOp
|
||||||
|
module.exports.StrokeCap = nativeBinding.StrokeCap
|
||||||
|
module.exports.StrokeJoin = nativeBinding.StrokeJoin
|
||||||
|
module.exports.SvgExportFlag = nativeBinding.SvgExportFlag
|
||||||
|
module.exports.GifEncoder = nativeBinding.GifEncoder
|
||||||
|
module.exports.GifDisposal = nativeBinding.GifDisposal
|
||||||
156
frontend/node_modules/@napi-rs/canvas/load-image.js
generated
vendored
Normal file
156
frontend/node_modules/@napi-rs/canvas/load-image.js
generated
vendored
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
const fs = require('fs')
|
||||||
|
const { Readable } = require('stream')
|
||||||
|
const { URL } = require('url')
|
||||||
|
|
||||||
|
const { Image } = require('./js-binding')
|
||||||
|
|
||||||
|
let http, https
|
||||||
|
|
||||||
|
const MAX_REDIRECTS = 20
|
||||||
|
const REDIRECT_STATUSES = new Set([301, 302])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the given source into canvas Image
|
||||||
|
* @param {string|URL|Image|Buffer} source The image source to be loaded
|
||||||
|
* @param {object} options Options passed to the loader
|
||||||
|
*/
|
||||||
|
module.exports = async function loadImage(source, options = {}) {
|
||||||
|
// use the same buffer without copying if the source is a buffer
|
||||||
|
if (Buffer.isBuffer(source) || source instanceof Uint8Array) return createImage(source, options.alt)
|
||||||
|
// load readable stream as image
|
||||||
|
if (source instanceof Readable) return createImage(await consumeStream(source), options.alt)
|
||||||
|
// construct a Uint8Array if the source is ArrayBuffer or SharedArrayBuffer
|
||||||
|
if (source instanceof ArrayBuffer || source instanceof SharedArrayBuffer)
|
||||||
|
return createImage(new Uint8Array(source), options.alt)
|
||||||
|
// construct a buffer if the source is buffer-like
|
||||||
|
if (isBufferLike(source)) return createImage(Buffer.from(source), options.alt)
|
||||||
|
// if the source is Image instance, copy the image src to new image
|
||||||
|
if (source instanceof Image) return createImage(source.src, options.alt)
|
||||||
|
// if source is string and in data uri format, construct image using data uri
|
||||||
|
if (typeof source === 'string' && source.trimStart().startsWith('data:')) {
|
||||||
|
const commaIdx = source.indexOf(',')
|
||||||
|
const encoding = source.lastIndexOf('base64', commaIdx) < 0 ? 'utf-8' : 'base64'
|
||||||
|
const data = Buffer.from(source.slice(commaIdx + 1), encoding)
|
||||||
|
// Empty payload would silently complete on Image (HTML spec) and the loadImage
|
||||||
|
// promise would never settle; reject upfront instead.
|
||||||
|
// See https://github.com/Brooooooklyn/canvas/issues/1255
|
||||||
|
if (data.length === 0) throw new Error(`Invalid data URI: empty payload in ${source.slice(0, 64)}`)
|
||||||
|
return createImage(data, options.alt)
|
||||||
|
}
|
||||||
|
// if source is a string or URL instance
|
||||||
|
if (typeof source === 'string') {
|
||||||
|
// if the source exists as a file, construct image from that file
|
||||||
|
if (!source.startsWith('http') && !source.startsWith('https') && (await exists(source))) {
|
||||||
|
return createImage(source, options.alt)
|
||||||
|
} else {
|
||||||
|
// the source is a remote url here
|
||||||
|
source = new URL(source)
|
||||||
|
// attempt to download the remote source and construct image
|
||||||
|
const data = await new Promise((resolve, reject) =>
|
||||||
|
makeRequest(
|
||||||
|
source,
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
typeof options.maxRedirects === 'number' && options.maxRedirects >= 0 ? options.maxRedirects : MAX_REDIRECTS,
|
||||||
|
options.requestOptions,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return createImage(data, options.alt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source instanceof URL) {
|
||||||
|
if (source.protocol === 'file:') {
|
||||||
|
// remove the leading slash on windows
|
||||||
|
return createImage(process.platform === 'win32' ? source.pathname.substring(1) : source.pathname, options.alt)
|
||||||
|
} else {
|
||||||
|
const data = await new Promise((resolve, reject) =>
|
||||||
|
makeRequest(
|
||||||
|
source,
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
typeof options.maxRedirects === 'number' && options.maxRedirects >= 0 ? options.maxRedirects : MAX_REDIRECTS,
|
||||||
|
options.requestOptions,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return createImage(data, options.alt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// throw error as don't support that source
|
||||||
|
throw new TypeError('unsupported image source')
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeRequest(url, resolve, reject, redirectCount, requestOptions) {
|
||||||
|
const isHttps = url.protocol === 'https:'
|
||||||
|
// lazy load the lib
|
||||||
|
const lib = isHttps ? (!https ? (https = require('https')) : https) : !http ? (http = require('http')) : http
|
||||||
|
|
||||||
|
lib
|
||||||
|
.get(url.toString(), requestOptions || {}, (res) => {
|
||||||
|
try {
|
||||||
|
const shouldRedirect = REDIRECT_STATUSES.has(res.statusCode) && typeof res.headers.location === 'string'
|
||||||
|
if (shouldRedirect && redirectCount > 0)
|
||||||
|
return makeRequest(
|
||||||
|
new URL(res.headers.location, url.origin),
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
redirectCount - 1,
|
||||||
|
requestOptions,
|
||||||
|
)
|
||||||
|
if (typeof res.statusCode === 'number' && (res.statusCode < 200 || res.statusCode >= 300)) {
|
||||||
|
return reject(new Error(`remote source rejected with status code ${res.statusCode}`))
|
||||||
|
}
|
||||||
|
|
||||||
|
consumeStream(res).then(resolve, reject)
|
||||||
|
} catch (err) {
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on('error', reject)
|
||||||
|
}
|
||||||
|
|
||||||
|
// use stream/consumers in the future?
|
||||||
|
function consumeStream(res) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const chunks = []
|
||||||
|
|
||||||
|
res.on('data', (chunk) => chunks.push(chunk))
|
||||||
|
res.on('end', () => resolve(Buffer.concat(chunks)))
|
||||||
|
res.on('error', reject)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createImage(src, alt) {
|
||||||
|
// Empty Buffer / Uint8Array: Image follows the HTML spec (silent completion,
|
||||||
|
// no events) so the promise below would hang. Reject upfront.
|
||||||
|
// See https://github.com/Brooooooklyn/canvas/issues/1255
|
||||||
|
if ((Buffer.isBuffer(src) || src instanceof Uint8Array) && src.length === 0) {
|
||||||
|
throw new Error('loadImage: empty image data')
|
||||||
|
}
|
||||||
|
|
||||||
|
const image = new Image()
|
||||||
|
if (typeof alt === 'string') image.alt = alt
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
image.onload = () => {
|
||||||
|
// Wait for bitmap decode before resolving
|
||||||
|
image.decode().then(() => resolve(image), reject)
|
||||||
|
}
|
||||||
|
image.onerror = (e) => reject(e)
|
||||||
|
image.src = src
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function isBufferLike(src) {
|
||||||
|
return (src && src.type === 'Buffer') || Array.isArray(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function exists(path) {
|
||||||
|
try {
|
||||||
|
await fs.promises.access(path, fs.constants.F_OK)
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
216
frontend/node_modules/@napi-rs/canvas/node-canvas.d.ts
generated
vendored
Normal file
216
frontend/node_modules/@napi-rs/canvas/node-canvas.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,216 @@
|
||||||
|
/**
|
||||||
|
* node-canvas compatibility layer for @napi-rs/canvas.
|
||||||
|
*
|
||||||
|
* Provides a drop-in replacement API matching the `canvas` (node-canvas) npm package.
|
||||||
|
* Quality values for JPEG/WebP use the node-canvas 0-1 scale and are automatically
|
||||||
|
* converted to @napi-rs/canvas's 0-100 scale internally.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* // Drop-in replacement: change this import
|
||||||
|
* // import { createCanvas, registerFont, loadImage } from 'canvas'
|
||||||
|
* // to this:
|
||||||
|
* import { createCanvas, registerFont, loadImage } from '@napi-rs/canvas/node-canvas'
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Readable } from 'node:stream'
|
||||||
|
|
||||||
|
// Re-export types from @napi-rs/canvas that are API-compatible
|
||||||
|
import {
|
||||||
|
Image as NapiImage,
|
||||||
|
ImageData as NapiImageData,
|
||||||
|
Path2D as NapiPath2D,
|
||||||
|
DOMPoint as NapiDOMPoint,
|
||||||
|
DOMMatrix as NapiDOMMatrix,
|
||||||
|
DOMRect as NapiDOMRect,
|
||||||
|
SKRSContext2D,
|
||||||
|
Canvas as NapiCanvas,
|
||||||
|
SvgCanvas,
|
||||||
|
SvgExportFlag,
|
||||||
|
IGlobalFonts,
|
||||||
|
PDFDocument,
|
||||||
|
} from './index'
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Config interfaces (node-canvas conventions)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export interface PngConfig {
|
||||||
|
/** ZLIB compression level (0-9). Accepted for compatibility; Skia uses its own encoder. */
|
||||||
|
compressionLevel?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
|
||||||
|
/** PNG filter flags. Accepted for compatibility; Skia uses its own encoder. */
|
||||||
|
filters?: number
|
||||||
|
/** Palette for indexed PNGs. Not supported by Skia backend. */
|
||||||
|
palette?: Uint8ClampedArray
|
||||||
|
/** Background color index for indexed PNGs. Not supported by Skia backend. */
|
||||||
|
backgroundIndex?: number
|
||||||
|
/** Pixels per inch. Accepted for compatibility. */
|
||||||
|
resolution?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JpegConfig {
|
||||||
|
/** Quality between 0 and 1. Defaults to 0.75. Converted to 0-100 for Skia. */
|
||||||
|
quality?: number
|
||||||
|
/** Progressive encoding. Accepted for compatibility; not supported by Skia backend. */
|
||||||
|
progressive?: boolean
|
||||||
|
/** 2x2 chroma subsampling. Accepted for compatibility; not supported by Skia backend. */
|
||||||
|
chromaSubsampling?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Canvas class (with node-canvas compatible methods)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export interface Canvas extends Omit<NapiCanvas, 'toBuffer' | 'toDataURL'> {
|
||||||
|
/**
|
||||||
|
* Encode the canvas as a PNG and return a Node.js Readable stream.
|
||||||
|
* @param config PNG encoding options (accepted for compatibility)
|
||||||
|
*/
|
||||||
|
createPNGStream(config?: PngConfig): PNGStream
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode the canvas as a JPEG and return a Node.js Readable stream.
|
||||||
|
* @param config JPEG encoding options. Quality is 0-1 (default 0.75).
|
||||||
|
*/
|
||||||
|
createJPEGStream(config?: JpegConfig): JPEGStream
|
||||||
|
|
||||||
|
// --- toBuffer overloads (node-canvas conventions) ---
|
||||||
|
|
||||||
|
/** Encode as PNG (default). */
|
||||||
|
toBuffer(): Buffer
|
||||||
|
toBuffer(mimeType: 'image/png', config?: PngConfig): Buffer
|
||||||
|
/** Encode as JPEG. Quality in config is 0-1 (default 0.75). */
|
||||||
|
toBuffer(mimeType: 'image/jpeg', config?: JpegConfig): Buffer
|
||||||
|
/** Get raw unencoded pixel data. */
|
||||||
|
toBuffer(mimeType: 'raw'): Buffer
|
||||||
|
|
||||||
|
/** Async: encode as PNG (default) via callback. */
|
||||||
|
toBuffer(cb: (err: Error | null, result: Buffer) => void): void
|
||||||
|
toBuffer(cb: (err: Error | null, result: Buffer) => void, mimeType: 'image/png', config?: PngConfig): void
|
||||||
|
/** Async: encode as JPEG via callback. Quality in config is 0-1 (default 0.75). */
|
||||||
|
toBuffer(cb: (err: Error | null, result: Buffer) => void, mimeType: 'image/jpeg', config?: JpegConfig): void
|
||||||
|
|
||||||
|
// --- toDataURL overloads (node-canvas conventions) ---
|
||||||
|
|
||||||
|
/** Encode as PNG data URL (default). */
|
||||||
|
toDataURL(): string
|
||||||
|
toDataURL(mimeType: 'image/png'): string
|
||||||
|
/** Encode as JPEG data URL. Quality is 0-1. */
|
||||||
|
toDataURL(mimeType: 'image/jpeg', quality?: number): string
|
||||||
|
/** Async: encode as data URL via callback. */
|
||||||
|
toDataURL(cb: (err: Error | null, result: string) => void): void
|
||||||
|
toDataURL(mimeType: 'image/png', cb: (err: Error | null, result: string) => void): void
|
||||||
|
toDataURL(mimeType: 'image/jpeg', cb: (err: Error | null, result: string) => void): void
|
||||||
|
toDataURL(mimeType: 'image/jpeg', quality: number, cb: (err: Error | null, result: string) => void): void
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Stream classes
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Readable stream that emits PNG-encoded data. */
|
||||||
|
export class PNGStream extends Readable {}
|
||||||
|
|
||||||
|
/** Readable stream that emits JPEG-encoded data. */
|
||||||
|
export class JPEGStream extends Readable {}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Factory functions
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new canvas with node-canvas compatible API.
|
||||||
|
* Canvas instances created through this function have `createPNGStream()`,
|
||||||
|
* `createJPEGStream()`, and node-canvas compatible `toBuffer()` overloads.
|
||||||
|
*
|
||||||
|
* When `type` is `'svg'`, returns a `SvgCanvas` which produces vector output
|
||||||
|
* via `getContent()` instead of raster encoding methods.
|
||||||
|
*
|
||||||
|
* @param width Canvas width in pixels
|
||||||
|
* @param height Canvas height in pixels
|
||||||
|
* @param type Canvas type: 'image' (default) or 'svg'
|
||||||
|
*/
|
||||||
|
export function createCanvas(width: number, height: number, type: 'svg'): SvgCanvas
|
||||||
|
export function createCanvas(width: number, height: number, type?: 'image'): Canvas
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an ImageData instance.
|
||||||
|
* @param data Pixel data array
|
||||||
|
* @param width Width in pixels
|
||||||
|
* @param height Height in pixels (calculated from data length if omitted)
|
||||||
|
*/
|
||||||
|
export function createImageData(data: Uint8ClampedArray, width: number, height?: number): NapiImageData
|
||||||
|
export function createImageData(width: number, height: number): NapiImageData
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load an image from a file path, URL, Buffer, or other source.
|
||||||
|
* Returns a Promise that resolves to an Image instance.
|
||||||
|
*/
|
||||||
|
export function loadImage(
|
||||||
|
source: string | URL | Buffer | ArrayBufferLike | Uint8Array | NapiImage | import('stream').Readable,
|
||||||
|
options?: { alt?: string; maxRedirects?: number; requestOptions?: import('http').RequestOptions },
|
||||||
|
): Promise<NapiImage>
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Font registration (node-canvas convention)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a font file for use in canvas text rendering.
|
||||||
|
* Compatible with node-canvas's `registerFont()`.
|
||||||
|
*
|
||||||
|
* Note: @napi-rs/canvas auto-detects weight and style from font file metadata
|
||||||
|
* (matching browser behavior). The `weight` and `style` properties in fontFace
|
||||||
|
* are accepted for API compatibility.
|
||||||
|
*
|
||||||
|
* @param path Absolute path to the font file (.ttf, .otf, etc.)
|
||||||
|
* @param fontFace Font face properties matching CSS @font-face descriptors
|
||||||
|
*/
|
||||||
|
export function registerFont(path: string, fontFace: { family: string; weight?: string; style?: string }): void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deregister all previously registered fonts.
|
||||||
|
* Compatible with node-canvas's `deregisterAllFonts()`.
|
||||||
|
*/
|
||||||
|
export function deregisterAllFonts(): void
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Re-exported classes & values
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Canvas is exported both as a type (the compat interface above) and as a
|
||||||
|
// value (constructor that returns compat Canvas instances at runtime).
|
||||||
|
export declare const Canvas: {
|
||||||
|
new (width: number, height: number): Canvas
|
||||||
|
new (width: number, height: number, flag: SvgExportFlag): SvgCanvas
|
||||||
|
prototype: NapiCanvas
|
||||||
|
}
|
||||||
|
|
||||||
|
export { NapiImage as Image }
|
||||||
|
export { NapiImageData as ImageData }
|
||||||
|
export { NapiPath2D as Path2D }
|
||||||
|
export { NapiDOMPoint as DOMPoint }
|
||||||
|
export { NapiDOMMatrix as DOMMatrix }
|
||||||
|
export { NapiDOMRect as DOMRect }
|
||||||
|
|
||||||
|
// SKRSContext2D is an interface in index.d.ts but a class value at runtime.
|
||||||
|
// Declare as const to export the value for instanceof checks and re-export the type.
|
||||||
|
export type CanvasRenderingContext2D = SKRSContext2D
|
||||||
|
export declare const CanvasRenderingContext2D: { prototype: SKRSContext2D }
|
||||||
|
/** Legacy alias for CanvasRenderingContext2D. */
|
||||||
|
export type Context2d = SKRSContext2D
|
||||||
|
export declare const Context2d: { prototype: SKRSContext2D }
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// @napi-rs/canvas extras (not part of node-canvas, but useful)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export { IGlobalFonts }
|
||||||
|
export declare const GlobalFonts: IGlobalFonts & {
|
||||||
|
/** Reload system fonts. Available at runtime but not declared in IGlobalFonts. */
|
||||||
|
loadSystemFonts(): number
|
||||||
|
}
|
||||||
|
export { PDFDocument }
|
||||||
|
export { NapiCanvas as CanvasElement }
|
||||||
|
export { SvgCanvas as SVGCanvas }
|
||||||
379
frontend/node_modules/@napi-rs/canvas/node-canvas.js
generated
vendored
Normal file
379
frontend/node_modules/@napi-rs/canvas/node-canvas.js
generated
vendored
Normal file
|
|
@ -0,0 +1,379 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const { Readable } = require('node:stream')
|
||||||
|
|
||||||
|
const {
|
||||||
|
createCanvas: _createCanvas,
|
||||||
|
Canvas,
|
||||||
|
CanvasElement,
|
||||||
|
SVGCanvas,
|
||||||
|
GlobalFonts,
|
||||||
|
Image,
|
||||||
|
ImageData,
|
||||||
|
Path2D,
|
||||||
|
DOMPoint,
|
||||||
|
DOMMatrix,
|
||||||
|
DOMRect,
|
||||||
|
loadImage,
|
||||||
|
PDFDocument,
|
||||||
|
SvgExportFlag,
|
||||||
|
} = require('./index.js')
|
||||||
|
|
||||||
|
// CanvasRenderingContext2D is not re-exported by index.js, grab from native bindings
|
||||||
|
const { CanvasRenderingContext2D } = require('./js-binding')
|
||||||
|
|
||||||
|
// node-canvas defaults JPEG quality to 0.75 across all paths.
|
||||||
|
// @napi-rs/canvas native default is 0.92 (matching Blink/Chrome).
|
||||||
|
const NODE_CANVAS_DEFAULT_QUALITY = 0.75
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Stream classes (node-canvas returns Node.js Readable streams)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class PNGStream extends Readable {
|
||||||
|
constructor(canvas, options) {
|
||||||
|
super()
|
||||||
|
this._canvas = canvas
|
||||||
|
this._options = options || {}
|
||||||
|
this._done = false
|
||||||
|
}
|
||||||
|
|
||||||
|
_read() {
|
||||||
|
if (this._done) return
|
||||||
|
this._done = true
|
||||||
|
this._canvas
|
||||||
|
.encode('png')
|
||||||
|
.then((buf) => {
|
||||||
|
this.push(buf)
|
||||||
|
this.push(null)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.destroy(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class JPEGStream extends Readable {
|
||||||
|
constructor(canvas, options) {
|
||||||
|
super()
|
||||||
|
this._canvas = canvas
|
||||||
|
const opts = options || {}
|
||||||
|
// node-canvas quality: 0-1 (default 0.75), encode() expects 0-100
|
||||||
|
this._quality = Math.round((opts.quality != null ? opts.quality : NODE_CANVAS_DEFAULT_QUALITY) * 100)
|
||||||
|
this._done = false
|
||||||
|
}
|
||||||
|
|
||||||
|
_read() {
|
||||||
|
if (this._done) return
|
||||||
|
this._done = true
|
||||||
|
this._canvas
|
||||||
|
.encode('jpeg', this._quality)
|
||||||
|
.then((buf) => {
|
||||||
|
this.push(buf)
|
||||||
|
this.push(null)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.destroy(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Quality normalization helpers
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const MIME_FORMAT_MAP = {
|
||||||
|
'image/png': 'png',
|
||||||
|
'image/jpeg': 'jpeg',
|
||||||
|
'image/webp': 'webp',
|
||||||
|
'image/avif': 'avif',
|
||||||
|
'image/gif': 'gif',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract quality from a node-canvas config object or number.
|
||||||
|
* Returns the raw 0-1 quality value, defaulting to NODE_CANVAS_DEFAULT_QUALITY
|
||||||
|
* for JPEG/WebP when not specified.
|
||||||
|
*
|
||||||
|
* @param {string} mime
|
||||||
|
* @param {number|object|undefined} configOrQuality
|
||||||
|
* @returns {number|undefined} 0-1 scale for JPEG/WebP, undefined for other mimes
|
||||||
|
*/
|
||||||
|
function _extractQuality(mime, configOrQuality) {
|
||||||
|
if (mime !== 'image/jpeg' && mime !== 'image/webp') return undefined
|
||||||
|
if (configOrQuality == null) return NODE_CANVAS_DEFAULT_QUALITY
|
||||||
|
if (typeof configOrQuality === 'number') return configOrQuality
|
||||||
|
if (typeof configOrQuality === 'object' && configOrQuality.quality != null) {
|
||||||
|
return configOrQuality.quality
|
||||||
|
}
|
||||||
|
return NODE_CANVAS_DEFAULT_QUALITY
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Compat methods added to each canvas instance created via this module
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Keep references to the original prototype methods
|
||||||
|
const _origToBuffer = CanvasElement.prototype.toBuffer
|
||||||
|
const _origToDataURL = CanvasElement.prototype.toDataURL
|
||||||
|
|
||||||
|
function _compatCreatePNGStream(options) {
|
||||||
|
return new PNGStream(this, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
function _compatCreateJPEGStream(options) {
|
||||||
|
return new JPEGStream(this, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* node-canvas compatible toBuffer:
|
||||||
|
* - toBuffer() → PNG (sync)
|
||||||
|
* - toBuffer('image/png', config?) → PNG (sync)
|
||||||
|
* - toBuffer('image/jpeg', config?) → JPEG (sync, quality 0-1)
|
||||||
|
* - toBuffer('raw') → raw pixel data (sync)
|
||||||
|
* - toBuffer(callback) → PNG (async)
|
||||||
|
* - toBuffer(callback, mime, config?) → specified format (async)
|
||||||
|
*/
|
||||||
|
function _compatToBuffer(mimeOrCallback, configOrQuality) {
|
||||||
|
// Callback form: toBuffer(callback) or toBuffer(callback, mime, config)
|
||||||
|
if (typeof mimeOrCallback === 'function') {
|
||||||
|
const callback = mimeOrCallback
|
||||||
|
const mime = typeof configOrQuality === 'string' ? configOrQuality : 'image/png'
|
||||||
|
const config = arguments[2]
|
||||||
|
|
||||||
|
if (mime === 'raw') {
|
||||||
|
let buf
|
||||||
|
try {
|
||||||
|
buf = this.data()
|
||||||
|
} catch (err) {
|
||||||
|
callback(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
callback(null, buf)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const format = MIME_FORMAT_MAP[mime]
|
||||||
|
if (!format) {
|
||||||
|
callback(new TypeError(`Unsupported MIME type "${mime}". Supported: ${Object.keys(MIME_FORMAT_MAP).join(', ')}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const q = _extractQuality(mime, config)
|
||||||
|
this.encode(format, q != null ? Math.round(q * 100) : undefined).then(
|
||||||
|
(buf) => callback(null, buf),
|
||||||
|
(err) => callback(err),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync: no args → PNG
|
||||||
|
if (mimeOrCallback === undefined) {
|
||||||
|
return _origToBuffer.call(this, 'image/png')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync: raw pixel data
|
||||||
|
if (mimeOrCallback === 'raw') {
|
||||||
|
return this.data()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync: extract quality (0-1) and scale to 0-100 for native toBuffer
|
||||||
|
const q = _extractQuality(mimeOrCallback, configOrQuality)
|
||||||
|
return _origToBuffer.call(this, mimeOrCallback, q != null ? Math.round(q * 100) : undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* node-canvas compatible toDataURL:
|
||||||
|
* - toDataURL() → PNG data URL (sync)
|
||||||
|
* - toDataURL('image/jpeg', quality) → JPEG data URL, quality 0-1 (sync)
|
||||||
|
* - toDataURL(callback) → PNG data URL (async)
|
||||||
|
* - toDataURL(mime, callback) → data URL (async)
|
||||||
|
* - toDataURL(mime, quality, callback) → data URL with quality (async)
|
||||||
|
*/
|
||||||
|
function _compatToDataURL(mimeOrCallback, qualityOrCallback) {
|
||||||
|
// toDataURL(callback)
|
||||||
|
if (typeof mimeOrCallback === 'function') {
|
||||||
|
this.toDataURLAsync('image/png').then(
|
||||||
|
(url) => mimeOrCallback(null, url),
|
||||||
|
(err) => mimeOrCallback(err),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// toDataURL(mime, callback)
|
||||||
|
if (typeof qualityOrCallback === 'function') {
|
||||||
|
this.toDataURLAsync(mimeOrCallback, _extractQuality(mimeOrCallback, undefined)).then(
|
||||||
|
(url) => qualityOrCallback(null, url),
|
||||||
|
(err) => qualityOrCallback(err),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// toDataURL(mime, quality, callback)
|
||||||
|
const cb = arguments[2]
|
||||||
|
if (typeof cb === 'function') {
|
||||||
|
// Native toDataURLAsync expects 0-1 (f64) — pass quality through as-is
|
||||||
|
const quality = _extractQuality(mimeOrCallback, qualityOrCallback)
|
||||||
|
this.toDataURLAsync(mimeOrCallback, quality).then(
|
||||||
|
(url) => cb(null, url),
|
||||||
|
(err) => cb(err),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync form: native toDataURL expects 0-1 (f64) — pass quality through as-is
|
||||||
|
const quality = _extractQuality(mimeOrCallback, qualityOrCallback)
|
||||||
|
return _origToDataURL.call(this, mimeOrCallback, quality)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach node-canvas compatible methods to a CanvasElement instance.
|
||||||
|
*/
|
||||||
|
function _addCompatMethods(canvas) {
|
||||||
|
canvas.createPNGStream = _compatCreatePNGStream
|
||||||
|
canvas.createJPEGStream = _compatCreateJPEGStream
|
||||||
|
canvas.toBuffer = _compatToBuffer
|
||||||
|
canvas.toDataURL = _compatToDataURL
|
||||||
|
return canvas
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Public API: registerFont / deregisterAllFonts
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Track FontKeys from registerFont() so deregisterAllFonts() can remove
|
||||||
|
// only user-registered fonts (matching node-canvas behavior which leaves
|
||||||
|
// system fonts untouched).
|
||||||
|
const _registeredFontKeys = []
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a font file with the specified font face properties.
|
||||||
|
* Compatible with node-canvas's registerFont(path, { family, weight?, style? }).
|
||||||
|
*
|
||||||
|
* Note: @napi-rs/canvas auto-detects weight and style from font file metadata
|
||||||
|
* (matching browser behavior). The weight and style properties in fontFace are
|
||||||
|
* accepted for API compatibility but the actual values are read from the font.
|
||||||
|
*
|
||||||
|
* @param {string} path Absolute path to the font file (.ttf, .otf, etc.)
|
||||||
|
* @param {{ family: string, weight?: string, style?: string }} fontFace
|
||||||
|
*/
|
||||||
|
function registerFont(path, fontFace) {
|
||||||
|
if (!fontFace || typeof fontFace.family !== 'string') {
|
||||||
|
throw new TypeError('registerFont requires a fontFace with a "family" property')
|
||||||
|
}
|
||||||
|
const key = GlobalFonts.registerFromPath(path, fontFace.family)
|
||||||
|
if (!key) {
|
||||||
|
throw new Error(`Failed to register font from "${path}" with family "${fontFace.family}"`)
|
||||||
|
}
|
||||||
|
_registeredFontKeys.push(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deregister all fonts previously registered via registerFont().
|
||||||
|
* Compatible with node-canvas's deregisterAllFonts() which only removes
|
||||||
|
* user-registered fonts and leaves system fonts untouched.
|
||||||
|
*/
|
||||||
|
function deregisterAllFonts() {
|
||||||
|
if (_registeredFontKeys.length > 0) {
|
||||||
|
GlobalFonts.removeBatch(_registeredFontKeys)
|
||||||
|
_registeredFontKeys.length = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Public API: createCanvas / createImageData
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new canvas instance with node-canvas compatible API.
|
||||||
|
*
|
||||||
|
* @param {number} width
|
||||||
|
* @param {number} height
|
||||||
|
* @param {'image'|'svg'} [type='image']
|
||||||
|
* @returns {Canvas}
|
||||||
|
*/
|
||||||
|
function createCanvas(width, height, type) {
|
||||||
|
if (type === 'svg') {
|
||||||
|
// SvgExportFlag enum requires a valid variant; NoPrettyXML (0x02) is the
|
||||||
|
// least impactful on rendering behavior (only affects XML whitespace).
|
||||||
|
return _createCanvas(width, height, SvgExportFlag.NoPrettyXML)
|
||||||
|
}
|
||||||
|
if (type === 'pdf') {
|
||||||
|
throw new Error(
|
||||||
|
'createCanvas with type "pdf" is not supported. Use the PDFDocument class from @napi-rs/canvas directly.',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (type != null && type !== 'image') {
|
||||||
|
throw new TypeError(`createCanvas: unknown type "${type}". Supported types: "image", "svg".`)
|
||||||
|
}
|
||||||
|
return _addCompatMethods(_createCanvas(width, height))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an ImageData instance.
|
||||||
|
* Compatible with node-canvas's createImageData().
|
||||||
|
*
|
||||||
|
* @param {Uint8ClampedArray|number} dataOrWidth
|
||||||
|
* @param {number} widthOrHeight
|
||||||
|
* @param {number} [height]
|
||||||
|
* @returns {ImageData}
|
||||||
|
*/
|
||||||
|
function createImageData(dataOrWidth, widthOrHeight, height) {
|
||||||
|
if (typeof dataOrWidth === 'number') {
|
||||||
|
return new ImageData(dataOrWidth, widthOrHeight)
|
||||||
|
}
|
||||||
|
if (height != null) {
|
||||||
|
return new ImageData(dataOrWidth, widthOrHeight, height)
|
||||||
|
}
|
||||||
|
return new ImageData(dataOrWidth, widthOrHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Canvas constructor wrapper
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// In node-canvas, `new Canvas(w, h)` and `createCanvas(w, h)` produce
|
||||||
|
// equivalent canvases. Wrap the native Canvas constructor so that
|
||||||
|
// `new Canvas(w, h)` also gets compat methods, without polluting the
|
||||||
|
// prototype (which would affect @napi-rs/canvas users who don't use
|
||||||
|
// this compat layer).
|
||||||
|
const CompatCanvas = new Proxy(Canvas, {
|
||||||
|
construct(target, args) {
|
||||||
|
const canvas = new target(...args)
|
||||||
|
if (canvas instanceof CanvasElement) {
|
||||||
|
return _addCompatMethods(canvas)
|
||||||
|
}
|
||||||
|
return canvas
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Exports (matches node-canvas export shape)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
// Factory functions
|
||||||
|
Canvas: CompatCanvas,
|
||||||
|
createCanvas,
|
||||||
|
createImageData,
|
||||||
|
loadImage,
|
||||||
|
registerFont,
|
||||||
|
deregisterAllFonts,
|
||||||
|
|
||||||
|
// Classes
|
||||||
|
Image,
|
||||||
|
ImageData,
|
||||||
|
CanvasRenderingContext2D,
|
||||||
|
Context2d: CanvasRenderingContext2D,
|
||||||
|
PNGStream,
|
||||||
|
JPEGStream,
|
||||||
|
Path2D,
|
||||||
|
|
||||||
|
// Geometry
|
||||||
|
DOMPoint,
|
||||||
|
DOMMatrix,
|
||||||
|
DOMRect,
|
||||||
|
|
||||||
|
// @napi-rs/canvas extras (available but not part of node-canvas)
|
||||||
|
GlobalFonts,
|
||||||
|
PDFDocument,
|
||||||
|
CanvasElement,
|
||||||
|
SVGCanvas,
|
||||||
|
}
|
||||||
176
frontend/node_modules/@napi-rs/canvas/package.json
generated
vendored
Normal file
176
frontend/node_modules/@napi-rs/canvas/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
{
|
||||||
|
"name": "@napi-rs/canvas",
|
||||||
|
"version": "0.1.100",
|
||||||
|
"description": "Canvas for Node.js with skia backend",
|
||||||
|
"main": "index.js",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/Brooooooklyn/canvas.git"
|
||||||
|
},
|
||||||
|
"workspaces": [
|
||||||
|
"e2e/*"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"keywords": [
|
||||||
|
"napi-rs",
|
||||||
|
"NAPI",
|
||||||
|
"N-API",
|
||||||
|
"Rust",
|
||||||
|
"node-addon",
|
||||||
|
"node-addon-api",
|
||||||
|
"canvas",
|
||||||
|
"image",
|
||||||
|
"pdf",
|
||||||
|
"svg",
|
||||||
|
"skia"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"index.d.ts",
|
||||||
|
"index.js",
|
||||||
|
"geometry.js",
|
||||||
|
"js-binding.js",
|
||||||
|
"load-image.js",
|
||||||
|
"node-canvas.js",
|
||||||
|
"node-canvas.d.ts"
|
||||||
|
],
|
||||||
|
"napi": {
|
||||||
|
"binaryName": "skia",
|
||||||
|
"targets": [
|
||||||
|
"x86_64-unknown-linux-gnu",
|
||||||
|
"x86_64-apple-darwin",
|
||||||
|
"x86_64-pc-windows-msvc",
|
||||||
|
"aarch64-pc-windows-msvc",
|
||||||
|
"armv7-unknown-linux-gnueabihf",
|
||||||
|
"x86_64-unknown-linux-musl",
|
||||||
|
"aarch64-unknown-linux-gnu",
|
||||||
|
"aarch64-unknown-linux-musl",
|
||||||
|
"aarch64-apple-darwin",
|
||||||
|
"aarch64-linux-android",
|
||||||
|
"riscv64-unknown-linux-gnu"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"registry": "https://registry.npmjs.org/",
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"artifacts": "napi artifacts",
|
||||||
|
"bench": "node --import @oxc-node/core/register benchmark/bench.ts",
|
||||||
|
"build": "napi build --platform --release --js js-binding.js",
|
||||||
|
"build:debug": "napi build --platform --js js-binding.js",
|
||||||
|
"example-lottie": "yarn oxnode ./example/lottie-to-video.ts",
|
||||||
|
"format": "run-p format:source format:rs format:toml",
|
||||||
|
"format:rs": "cargo fmt",
|
||||||
|
"format:source": "prettier . -w",
|
||||||
|
"format:toml": "taplo format",
|
||||||
|
"lint": "oxlint",
|
||||||
|
"prepublishOnly": "pinst --disable && napi prepublish -t npm",
|
||||||
|
"postpublish": "pinst --enable",
|
||||||
|
"test:ci": "ava -c 1",
|
||||||
|
"test": "ava",
|
||||||
|
"e2e": "yarn workspace @napi-rs/canvas-e2e-webpack test",
|
||||||
|
"version": "napi version && conventional-changelog -p angular -i CHANGELOG.md -s && git add ."
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@jimp/core": "^1.6.0",
|
||||||
|
"@jimp/custom": "^0.22.12",
|
||||||
|
"@jimp/jpeg": "^0.22.12",
|
||||||
|
"@jimp/png": "^0.22.12",
|
||||||
|
"@napi-rs/cli": "^3.5.0",
|
||||||
|
"@napi-rs/webcodecs": "^1.1.1",
|
||||||
|
"@octokit/rest": "^22.0.1",
|
||||||
|
"@oxc-node/cli": "^0.1.0",
|
||||||
|
"@oxc-node/core": "^0.1.0",
|
||||||
|
"@taplo/cli": "^0.7.0",
|
||||||
|
"@types/lodash": "^4.17.21",
|
||||||
|
"@types/node": "^25.0.3",
|
||||||
|
"@types/semver": "^7",
|
||||||
|
"ava": "^7.0.0",
|
||||||
|
"canvas": "^3.2.0",
|
||||||
|
"canvaskit-wasm": "^0.41.0",
|
||||||
|
"colorette": "^2.0.20",
|
||||||
|
"conventional-changelog-cli": "^5.0.0",
|
||||||
|
"core-js": "^3.47.0",
|
||||||
|
"echarts": "^6.0.0",
|
||||||
|
"electron": "^41.0.0",
|
||||||
|
"husky": "^9.1.7",
|
||||||
|
"lint-staged": "^16.2.7",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"npm-run-all2": "^8.0.4",
|
||||||
|
"oxlint": "^1.34.0",
|
||||||
|
"oxlint-tsgolint": "^0.22.0",
|
||||||
|
"pinst": "^3.0.0",
|
||||||
|
"png.js": "^0.2.1",
|
||||||
|
"prettier": "^3.7.4",
|
||||||
|
"pretty-bytes": "^7.1.0",
|
||||||
|
"semver": "^7.7.3",
|
||||||
|
"skia-canvas": "^3.0.8",
|
||||||
|
"table": "^6.9.0",
|
||||||
|
"tinybench": "^6.0.0",
|
||||||
|
"typescript": "^6.0.0"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.@(js|ts|tsx|yml|yaml|md|json|html)": [
|
||||||
|
"prettier --write"
|
||||||
|
],
|
||||||
|
"*.@(js|ts|tsx)": [
|
||||||
|
"oxlint --fix"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ava": {
|
||||||
|
"nodeArguments": [
|
||||||
|
"--import=@oxc-node/core/register",
|
||||||
|
"--import=core-js/proposals/promise-with-resolvers.js"
|
||||||
|
],
|
||||||
|
"extensions": {
|
||||||
|
"ts": "module"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"__test__/**/*.spec.ts",
|
||||||
|
"scripts/__test__/**/*.spec.ts"
|
||||||
|
],
|
||||||
|
"workerThreads": false,
|
||||||
|
"cache": false,
|
||||||
|
"timeout": "3m",
|
||||||
|
"environmentVariables": {
|
||||||
|
"SWC_NODE_PROJECT": "./tsconfig.json",
|
||||||
|
"NODE_ENV": "ava"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prettier": {
|
||||||
|
"printWidth": 120,
|
||||||
|
"semi": false,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"singleQuote": true,
|
||||||
|
"arrowParens": "always"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||||
|
},
|
||||||
|
"packageManager": "yarn@4.14.1",
|
||||||
|
"dependenciesMeta": {
|
||||||
|
"canvas": {
|
||||||
|
"built": true
|
||||||
|
},
|
||||||
|
"skia-canvas": {
|
||||||
|
"built": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@napi-rs/canvas-linux-x64-gnu": "0.1.100",
|
||||||
|
"@napi-rs/canvas-darwin-x64": "0.1.100",
|
||||||
|
"@napi-rs/canvas-win32-x64-msvc": "0.1.100",
|
||||||
|
"@napi-rs/canvas-win32-arm64-msvc": "0.1.100",
|
||||||
|
"@napi-rs/canvas-linux-arm-gnueabihf": "0.1.100",
|
||||||
|
"@napi-rs/canvas-linux-x64-musl": "0.1.100",
|
||||||
|
"@napi-rs/canvas-linux-arm64-gnu": "0.1.100",
|
||||||
|
"@napi-rs/canvas-linux-arm64-musl": "0.1.100",
|
||||||
|
"@napi-rs/canvas-darwin-arm64": "0.1.100",
|
||||||
|
"@napi-rs/canvas-android-arm64": "0.1.100",
|
||||||
|
"@napi-rs/canvas-linux-riscv64-gnu": "0.1.100"
|
||||||
|
}
|
||||||
|
}
|
||||||
6
frontend/node_modules/clsx/clsx.d.mts
generated
vendored
Normal file
6
frontend/node_modules/clsx/clsx.d.mts
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
export type ClassValue = ClassArray | ClassDictionary | string | number | bigint | null | boolean | undefined;
|
||||||
|
export type ClassDictionary = Record<string, any>;
|
||||||
|
export type ClassArray = ClassValue[];
|
||||||
|
|
||||||
|
export function clsx(...inputs: ClassValue[]): string;
|
||||||
|
export default clsx;
|
||||||
10
frontend/node_modules/clsx/clsx.d.ts
generated
vendored
Normal file
10
frontend/node_modules/clsx/clsx.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
declare namespace clsx {
|
||||||
|
type ClassValue = ClassArray | ClassDictionary | string | number | bigint | null | boolean | undefined;
|
||||||
|
type ClassDictionary = Record<string, any>;
|
||||||
|
type ClassArray = ClassValue[];
|
||||||
|
function clsx(...inputs: ClassValue[]): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare function clsx(...inputs: clsx.ClassValue[]): string;
|
||||||
|
|
||||||
|
export = clsx;
|
||||||
9
frontend/node_modules/clsx/license
generated
vendored
Normal file
9
frontend/node_modules/clsx/license
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) Luke Edwards <luke.edwards05@gmail.com> (lukeed.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.
|
||||||
60
frontend/node_modules/clsx/package.json
generated
vendored
Normal file
60
frontend/node_modules/clsx/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
{
|
||||||
|
"name": "clsx",
|
||||||
|
"version": "2.1.1",
|
||||||
|
"repository": "lukeed/clsx",
|
||||||
|
"description": "A tiny (239B) utility for constructing className strings conditionally.",
|
||||||
|
"module": "dist/clsx.mjs",
|
||||||
|
"unpkg": "dist/clsx.min.js",
|
||||||
|
"main": "dist/clsx.js",
|
||||||
|
"types": "clsx.d.ts",
|
||||||
|
"license": "MIT",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": {
|
||||||
|
"types": "./clsx.d.mts",
|
||||||
|
"default": "./dist/clsx.mjs"
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"types": "./clsx.d.ts",
|
||||||
|
"default": "./dist/clsx.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"./lite": {
|
||||||
|
"import": {
|
||||||
|
"types": "./clsx.d.mts",
|
||||||
|
"default": "./dist/lite.mjs"
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"types": "./clsx.d.ts",
|
||||||
|
"default": "./dist/lite.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"name": "Luke Edwards",
|
||||||
|
"email": "luke.edwards05@gmail.com",
|
||||||
|
"url": "https://lukeed.com"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "node bin",
|
||||||
|
"test": "uvu -r esm test"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"*.d.mts",
|
||||||
|
"*.d.ts",
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"keywords": [
|
||||||
|
"classes",
|
||||||
|
"classname",
|
||||||
|
"classnames"
|
||||||
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"esm": "3.2.25",
|
||||||
|
"terser": "4.8.0",
|
||||||
|
"uvu": "0.5.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
154
frontend/node_modules/clsx/readme.md
generated
vendored
Normal file
154
frontend/node_modules/clsx/readme.md
generated
vendored
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
# clsx [](https://github.com/lukeed/clsx/actions?query=workflow%3ACI) [](https://codecov.io/gh/lukeed/clsx) [](https://licenses.dev/npm/clsx)
|
||||||
|
|
||||||
|
> A tiny (239B) utility for constructing `className` strings conditionally.<Br>Also serves as a [faster](bench) & smaller drop-in replacement for the `classnames` module.
|
||||||
|
|
||||||
|
This module is available in three formats:
|
||||||
|
|
||||||
|
* **ES Module**: `dist/clsx.mjs`
|
||||||
|
* **CommonJS**: `dist/clsx.js`
|
||||||
|
* **UMD**: `dist/clsx.min.js`
|
||||||
|
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```
|
||||||
|
$ npm install --save clsx
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```js
|
||||||
|
import clsx from 'clsx';
|
||||||
|
// or
|
||||||
|
import { clsx } from 'clsx';
|
||||||
|
|
||||||
|
// Strings (variadic)
|
||||||
|
clsx('foo', true && 'bar', 'baz');
|
||||||
|
//=> 'foo bar baz'
|
||||||
|
|
||||||
|
// Objects
|
||||||
|
clsx({ foo:true, bar:false, baz:isTrue() });
|
||||||
|
//=> 'foo baz'
|
||||||
|
|
||||||
|
// Objects (variadic)
|
||||||
|
clsx({ foo:true }, { bar:false }, null, { '--foobar':'hello' });
|
||||||
|
//=> 'foo --foobar'
|
||||||
|
|
||||||
|
// Arrays
|
||||||
|
clsx(['foo', 0, false, 'bar']);
|
||||||
|
//=> 'foo bar'
|
||||||
|
|
||||||
|
// Arrays (variadic)
|
||||||
|
clsx(['foo'], ['', 0, false, 'bar'], [['baz', [['hello'], 'there']]]);
|
||||||
|
//=> 'foo bar baz hello there'
|
||||||
|
|
||||||
|
// Kitchen sink (with nesting)
|
||||||
|
clsx('foo', [1 && 'bar', { baz:false, bat:null }, ['hello', ['world']]], 'cya');
|
||||||
|
//=> 'foo bar hello world cya'
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### clsx(...input)
|
||||||
|
Returns: `String`
|
||||||
|
|
||||||
|
#### input
|
||||||
|
Type: `Mixed`
|
||||||
|
|
||||||
|
The `clsx` function can take ***any*** number of arguments, each of which can be an Object, Array, Boolean, or String.
|
||||||
|
|
||||||
|
> **Important:** _Any_ falsey values are discarded!<br>Standalone Boolean values are discarded as well.
|
||||||
|
|
||||||
|
```js
|
||||||
|
clsx(true, false, '', null, undefined, 0, NaN);
|
||||||
|
//=> ''
|
||||||
|
```
|
||||||
|
|
||||||
|
## Modes
|
||||||
|
|
||||||
|
There are multiple "versions" of `clsx` available, which allows you to bring only the functionality you need!
|
||||||
|
|
||||||
|
#### `clsx`
|
||||||
|
> **Size (gzip):** 239 bytes<br>
|
||||||
|
> **Availability:** CommonJS, ES Module, UMD
|
||||||
|
|
||||||
|
The default `clsx` module; see [API](#API) for info.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { clsx } from 'clsx';
|
||||||
|
// or
|
||||||
|
import clsx from 'clsx';
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `clsx/lite`
|
||||||
|
> **Size (gzip):** 140 bytes<br>
|
||||||
|
> **Availability:** CommonJS, ES Module<br>
|
||||||
|
> **CAUTION:** Accepts **ONLY** string arguments!
|
||||||
|
|
||||||
|
Ideal for applications that ***only*** use the string-builder pattern.
|
||||||
|
|
||||||
|
Any non-string arguments are ignored!
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { clsx } from 'clsx/lite';
|
||||||
|
// or
|
||||||
|
import clsx from 'clsx/lite';
|
||||||
|
|
||||||
|
// string
|
||||||
|
clsx('hello', true && 'foo', false && 'bar');
|
||||||
|
// => "hello foo"
|
||||||
|
|
||||||
|
// NOTE: Any non-string input(s) ignored
|
||||||
|
clsx({ foo: true });
|
||||||
|
//=> ""
|
||||||
|
```
|
||||||
|
|
||||||
|
## Benchmarks
|
||||||
|
|
||||||
|
For snapshots of cross-browser results, check out the [`bench`](bench) directory~!
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
All versions of Node.js are supported.
|
||||||
|
|
||||||
|
All browsers that support [`Array.isArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray#Browser_compatibility) are supported (IE9+).
|
||||||
|
|
||||||
|
>**Note:** For IE8 support and older, please install `clsx@1.0.x` and beware of [#17](https://github.com/lukeed/clsx/issues/17).
|
||||||
|
|
||||||
|
## Tailwind Support
|
||||||
|
|
||||||
|
Here some additional (optional) steps to enable classes autocompletion using `clsx` with Tailwind CSS.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>
|
||||||
|
Visual Studio Code
|
||||||
|
</summary>
|
||||||
|
|
||||||
|
1. [Install the "Tailwind CSS IntelliSense" Visual Studio Code extension](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss)
|
||||||
|
|
||||||
|
2. Add the following to your [`settings.json`](https://code.visualstudio.com/docs/getstarted/settings):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tailwindCSS.experimental.classRegex": [
|
||||||
|
["clsx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
You may find the [`clsx/lite`](#clsxlite) module useful within Tailwind contexts. This is especially true if/when your application **only** composes classes in this pattern:
|
||||||
|
|
||||||
|
```js
|
||||||
|
clsx('text-base', props.active && 'text-primary', props.className);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [obj-str](https://github.com/lukeed/obj-str) - A smaller (96B) and similiar utility that only works with Objects.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT © [Luke Edwards](https://lukeed.com)
|
||||||
1
frontend/node_modules/dequal/index.d.ts
generated
vendored
Normal file
1
frontend/node_modules/dequal/index.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export function dequal(foo: any, bar: any): boolean;
|
||||||
21
frontend/node_modules/dequal/license
generated
vendored
Normal file
21
frontend/node_modules/dequal/license
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) Luke Edwards <luke.edwards05@gmail.com> (lukeed.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.
|
||||||
1
frontend/node_modules/dequal/lite/index.d.ts
generated
vendored
Normal file
1
frontend/node_modules/dequal/lite/index.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export function dequal(foo: any, bar: any): boolean;
|
||||||
31
frontend/node_modules/dequal/lite/index.js
generated
vendored
Normal file
31
frontend/node_modules/dequal/lite/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
var has = Object.prototype.hasOwnProperty;
|
||||||
|
|
||||||
|
function dequal(foo, bar) {
|
||||||
|
var ctor, len;
|
||||||
|
if (foo === bar) return true;
|
||||||
|
|
||||||
|
if (foo && bar && (ctor=foo.constructor) === bar.constructor) {
|
||||||
|
if (ctor === Date) return foo.getTime() === bar.getTime();
|
||||||
|
if (ctor === RegExp) return foo.toString() === bar.toString();
|
||||||
|
|
||||||
|
if (ctor === Array) {
|
||||||
|
if ((len=foo.length) === bar.length) {
|
||||||
|
while (len-- && dequal(foo[len], bar[len]));
|
||||||
|
}
|
||||||
|
return len === -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctor || typeof foo === 'object') {
|
||||||
|
len = 0;
|
||||||
|
for (ctor in foo) {
|
||||||
|
if (has.call(foo, ctor) && ++len && !has.call(bar, ctor)) return false;
|
||||||
|
if (!(ctor in bar) || !dequal(foo[ctor], bar[ctor])) return false;
|
||||||
|
}
|
||||||
|
return Object.keys(bar).length === len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return foo !== foo && bar !== bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.dequal = dequal;
|
||||||
1
frontend/node_modules/dequal/lite/index.min.js
generated
vendored
Normal file
1
frontend/node_modules/dequal/lite/index.min.js
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(e.dequal={})}(this,(function(e){var t=Object.prototype.hasOwnProperty;e.dequal=function e(r,n){var o,i;if(r===n)return!0;if(r&&n&&(o=r.constructor)===n.constructor){if(o===Date)return r.getTime()===n.getTime();if(o===RegExp)return r.toString()===n.toString();if(o===Array){if((i=r.length)===n.length)for(;i--&&e(r[i],n[i]););return-1===i}if(!o||"object"==typeof r){for(o in i=0,r){if(t.call(r,o)&&++i&&!t.call(n,o))return!1;if(!(o in n)||!e(r[o],n[o]))return!1}return Object.keys(n).length===i}}return r!=r&&n!=n}}));
|
||||||
29
frontend/node_modules/dequal/lite/index.mjs
generated
vendored
Normal file
29
frontend/node_modules/dequal/lite/index.mjs
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
var has = Object.prototype.hasOwnProperty;
|
||||||
|
|
||||||
|
export function dequal(foo, bar) {
|
||||||
|
var ctor, len;
|
||||||
|
if (foo === bar) return true;
|
||||||
|
|
||||||
|
if (foo && bar && (ctor=foo.constructor) === bar.constructor) {
|
||||||
|
if (ctor === Date) return foo.getTime() === bar.getTime();
|
||||||
|
if (ctor === RegExp) return foo.toString() === bar.toString();
|
||||||
|
|
||||||
|
if (ctor === Array) {
|
||||||
|
if ((len=foo.length) === bar.length) {
|
||||||
|
while (len-- && dequal(foo[len], bar[len]));
|
||||||
|
}
|
||||||
|
return len === -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctor || typeof foo === 'object') {
|
||||||
|
len = 0;
|
||||||
|
for (ctor in foo) {
|
||||||
|
if (has.call(foo, ctor) && ++len && !has.call(bar, ctor)) return false;
|
||||||
|
if (!(ctor in bar) || !dequal(foo[ctor], bar[ctor])) return false;
|
||||||
|
}
|
||||||
|
return Object.keys(bar).length === len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return foo !== foo && bar !== bar;
|
||||||
|
}
|
||||||
57
frontend/node_modules/dequal/package.json
generated
vendored
Normal file
57
frontend/node_modules/dequal/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
{
|
||||||
|
"name": "dequal",
|
||||||
|
"version": "2.0.3",
|
||||||
|
"repository": "lukeed/dequal",
|
||||||
|
"description": "A tiny (304B to 489B) utility for check for deep equality",
|
||||||
|
"unpkg": "dist/index.min.js",
|
||||||
|
"module": "dist/index.mjs",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"types": "index.d.ts",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": {
|
||||||
|
"name": "Luke Edwards",
|
||||||
|
"email": "luke.edwards05@gmail.com",
|
||||||
|
"url": "https://lukeed.com"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "bundt",
|
||||||
|
"pretest": "npm run build",
|
||||||
|
"postbuild": "echo \"lite\" | xargs -n1 cp -v index.d.ts",
|
||||||
|
"test": "uvu -r esm test"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
|
"dist",
|
||||||
|
"lite"
|
||||||
|
],
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./index.d.ts",
|
||||||
|
"import": "./dist/index.mjs",
|
||||||
|
"require": "./dist/index.js"
|
||||||
|
},
|
||||||
|
"./lite": {
|
||||||
|
"types": "./index.d.ts",
|
||||||
|
"import": "./lite/index.mjs",
|
||||||
|
"require": "./lite/index.js"
|
||||||
|
},
|
||||||
|
"./package.json": "./package.json"
|
||||||
|
},
|
||||||
|
"modes": {
|
||||||
|
"lite": "src/lite.js",
|
||||||
|
"default": "src/index.js"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"deep",
|
||||||
|
"deep-equal",
|
||||||
|
"equality"
|
||||||
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"bundt": "1.0.2",
|
||||||
|
"esm": "3.2.25",
|
||||||
|
"uvu": "0.3.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
112
frontend/node_modules/dequal/readme.md
generated
vendored
Normal file
112
frontend/node_modules/dequal/readme.md
generated
vendored
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
# dequal [](https://github.com/lukeed/dequal/actions)
|
||||||
|
|
||||||
|
> A tiny (304B to 489B) utility to check for deep equality
|
||||||
|
|
||||||
|
This module supports comparison of all types, including `Function`, `RegExp`, `Date`, `Set`, `Map`, `TypedArray`s, `DataView`, `null`, `undefined`, and `NaN` values. Complex values (eg, Objects, Arrays, Sets, Maps, etc) are traversed recursively.
|
||||||
|
|
||||||
|
> **Important:**
|
||||||
|
> * key order **within Objects** does not matter
|
||||||
|
> * value order **within Arrays** _does_ matter
|
||||||
|
> * values **within Sets and Maps** use value equality
|
||||||
|
> * keys **within Maps** use value equality
|
||||||
|
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```
|
||||||
|
$ npm install --save dequal
|
||||||
|
```
|
||||||
|
|
||||||
|
## Modes
|
||||||
|
|
||||||
|
There are two "versions" of `dequal` available:
|
||||||
|
|
||||||
|
#### `dequal`
|
||||||
|
> **Size (gzip):** 489 bytes<br>
|
||||||
|
> **Availability:** [CommonJS](https://unpkg.com/dequal/dist/index.js), [ES Module](https://unpkg.com/dequal/dist/index.mjs), [UMD](https://unpkg.com/dequal/dist/index.min.js)
|
||||||
|
|
||||||
|
#### `dequal/lite`
|
||||||
|
> **Size (gzip):** 304 bytes<br>
|
||||||
|
> **Availability:** [CommonJS](https://unpkg.com/dequal/lite/index.js), [ES Module](https://unpkg.com/dequal/lite/index.mjs)
|
||||||
|
|
||||||
|
| | IE9+ | Number | String | Date | RegExp | Object | Array | Class | Set | Map | ArrayBuffer | [TypedArray](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray#TypedArray_objects) | [DataView](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView) |
|
||||||
|
|-|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|
||||||
|
| `dequal` | :x: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||||
|
| `dequal/lite` | :+1: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | :x: | :x: |
|
||||||
|
|
||||||
|
> <sup>**Note:** Table scrolls horizontally!</sup>
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { dequal } from 'dequal';
|
||||||
|
|
||||||
|
dequal(1, 1); //=> true
|
||||||
|
dequal({}, {}); //=> true
|
||||||
|
dequal('foo', 'foo'); //=> true
|
||||||
|
dequal([1, 2, 3], [1, 2, 3]); //=> true
|
||||||
|
dequal(dequal, dequal); //=> true
|
||||||
|
dequal(/foo/, /foo/); //=> true
|
||||||
|
dequal(null, null); //=> true
|
||||||
|
dequal(NaN, NaN); //=> true
|
||||||
|
dequal([], []); //=> true
|
||||||
|
dequal(
|
||||||
|
[{ a:1 }, [{ b:{ c:[1] } }]],
|
||||||
|
[{ a:1 }, [{ b:{ c:[1] } }]]
|
||||||
|
); //=> true
|
||||||
|
|
||||||
|
dequal(1, '1'); //=> false
|
||||||
|
dequal(null, undefined); //=> false
|
||||||
|
dequal({ a:1, b:[2,3] }, { a:1, b:[2,5] }); //=> false
|
||||||
|
dequal(/foo/i, /bar/g); //=> false
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### dequal(foo, bar)
|
||||||
|
Returns: `Boolean`
|
||||||
|
|
||||||
|
Both `foo` and `bar` can be of any type.<br>
|
||||||
|
A `Boolean` is returned indicating if the two were deeply equal.
|
||||||
|
|
||||||
|
|
||||||
|
## Benchmarks
|
||||||
|
|
||||||
|
> Running Node v10.13.0
|
||||||
|
|
||||||
|
The benchmarks can be found in the [`/bench`](/bench) directory. They are separated into two categories:
|
||||||
|
|
||||||
|
* `basic` – compares an object comprised of `String`, `Number`, `Date`, `Array`, and `Object` values.
|
||||||
|
* `complex` – like `basic`, but adds `RegExp`, `Map`, `Set`, and `Uint8Array` values.
|
||||||
|
|
||||||
|
> **Note:** Only candidates that pass validation step(s) are listed. <br>For example, `fast-deep-equal/es6` handles `Set` and `Map` values, but uses _referential equality_ while those listed use _value equality_.
|
||||||
|
|
||||||
|
```
|
||||||
|
Load times:
|
||||||
|
assert 0.109ms
|
||||||
|
util 0.006ms
|
||||||
|
fast-deep-equal 0.479ms
|
||||||
|
lodash/isequal 22.826ms
|
||||||
|
nano-equal 0.417ms
|
||||||
|
dequal 0.396ms
|
||||||
|
dequal/lite 0.264ms
|
||||||
|
|
||||||
|
Benchmark :: basic
|
||||||
|
assert.deepStrictEqual x 325,262 ops/sec ±0.57% (94 runs sampled)
|
||||||
|
util.isDeepStrictEqual x 318,812 ops/sec ±0.87% (94 runs sampled)
|
||||||
|
fast-deep-equal x 1,332,393 ops/sec ±0.36% (93 runs sampled)
|
||||||
|
lodash.isEqual x 269,129 ops/sec ±0.59% (95 runs sampled)
|
||||||
|
nano-equal x 1,122,053 ops/sec ±0.36% (96 runs sampled)
|
||||||
|
dequal/lite x 1,700,972 ops/sec ±0.31% (94 runs sampled)
|
||||||
|
dequal x 1,698,972 ops/sec ±0.63% (97 runs sampled)
|
||||||
|
|
||||||
|
Benchmark :: complex
|
||||||
|
assert.deepStrictEqual x 124,518 ops/sec ±0.64% (96 runs sampled)
|
||||||
|
util.isDeepStrictEqual x 125,113 ops/sec ±0.24% (96 runs sampled)
|
||||||
|
lodash.isEqual x 58,677 ops/sec ±0.49% (96 runs sampled)
|
||||||
|
dequal x 345,386 ops/sec ±0.27% (96 runs sampled)
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT © [Luke Edwards](https://lukeed.com)
|
||||||
151
frontend/node_modules/js-tokens/CHANGELOG.md
generated
vendored
Normal file
151
frontend/node_modules/js-tokens/CHANGELOG.md
generated
vendored
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
### Version 4.0.0 (2018-01-28) ###
|
||||||
|
|
||||||
|
- Added: Support for ES2018. The only change needed was recognizing the `s`
|
||||||
|
regex flag.
|
||||||
|
- Changed: _All_ tokens returned by the `matchToToken` function now have a
|
||||||
|
`closed` property. It is set to `undefined` for the tokens where “closed”
|
||||||
|
doesn’t make sense. This means that all tokens objects have the same shape,
|
||||||
|
which might improve performance.
|
||||||
|
|
||||||
|
These are the breaking changes:
|
||||||
|
|
||||||
|
- `'/a/s'.match(jsTokens)` no longer returns `['/', 'a', '/', 's']`, but
|
||||||
|
`['/a/s']`. (There are of course other variations of this.)
|
||||||
|
- Code that rely on some token objects not having the `closed` property could
|
||||||
|
now behave differently.
|
||||||
|
|
||||||
|
|
||||||
|
### Version 3.0.2 (2017-06-28) ###
|
||||||
|
|
||||||
|
- No code changes. Just updates to the readme.
|
||||||
|
|
||||||
|
|
||||||
|
### Version 3.0.1 (2017-01-30) ###
|
||||||
|
|
||||||
|
- Fixed: ES2015 unicode escapes with more than 6 hex digits are now matched
|
||||||
|
correctly.
|
||||||
|
|
||||||
|
|
||||||
|
### Version 3.0.0 (2017-01-11) ###
|
||||||
|
|
||||||
|
This release contains one breaking change, that should [improve performance in
|
||||||
|
V8][v8-perf]:
|
||||||
|
|
||||||
|
> So how can you, as a JavaScript developer, ensure that your RegExps are fast?
|
||||||
|
> If you are not interested in hooking into RegExp internals, make sure that
|
||||||
|
> neither the RegExp instance, nor its prototype is modified in order to get the
|
||||||
|
> best performance:
|
||||||
|
>
|
||||||
|
> ```js
|
||||||
|
> var re = /./g;
|
||||||
|
> re.exec(''); // Fast path.
|
||||||
|
> re.new_property = 'slow';
|
||||||
|
> ```
|
||||||
|
|
||||||
|
This module used to export a single regex, with `.matchToToken` bolted
|
||||||
|
on, just like in the above example. This release changes the exports of
|
||||||
|
the module to avoid this issue.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import jsTokens from "js-tokens"
|
||||||
|
// or:
|
||||||
|
var jsTokens = require("js-tokens")
|
||||||
|
var matchToToken = jsTokens.matchToToken
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import jsTokens, {matchToToken} from "js-tokens"
|
||||||
|
// or:
|
||||||
|
var jsTokens = require("js-tokens").default
|
||||||
|
var matchToToken = require("js-tokens").matchToToken
|
||||||
|
```
|
||||||
|
|
||||||
|
[v8-perf]: http://v8project.blogspot.se/2017/01/speeding-up-v8-regular-expressions.html
|
||||||
|
|
||||||
|
|
||||||
|
### Version 2.0.0 (2016-06-19) ###
|
||||||
|
|
||||||
|
- Added: Support for ES2016. In other words, support for the `**` exponentiation
|
||||||
|
operator.
|
||||||
|
|
||||||
|
These are the breaking changes:
|
||||||
|
|
||||||
|
- `'**'.match(jsTokens)` no longer returns `['*', '*']`, but `['**']`.
|
||||||
|
- `'**='.match(jsTokens)` no longer returns `['*', '*=']`, but `['**=']`.
|
||||||
|
|
||||||
|
|
||||||
|
### Version 1.0.3 (2016-03-27) ###
|
||||||
|
|
||||||
|
- Improved: Made the regex ever so slightly smaller.
|
||||||
|
- Updated: The readme.
|
||||||
|
|
||||||
|
|
||||||
|
### Version 1.0.2 (2015-10-18) ###
|
||||||
|
|
||||||
|
- Improved: Limited npm package contents for a smaller download. Thanks to
|
||||||
|
@zertosh!
|
||||||
|
|
||||||
|
|
||||||
|
### Version 1.0.1 (2015-06-20) ###
|
||||||
|
|
||||||
|
- Fixed: Declared an undeclared variable.
|
||||||
|
|
||||||
|
|
||||||
|
### Version 1.0.0 (2015-02-26) ###
|
||||||
|
|
||||||
|
- Changed: Merged the 'operator' and 'punctuation' types into 'punctuator'. That
|
||||||
|
type is now equivalent to the Punctuator token in the ECMAScript
|
||||||
|
specification. (Backwards-incompatible change.)
|
||||||
|
- Fixed: A `-` followed by a number is now correctly matched as a punctuator
|
||||||
|
followed by a number. It used to be matched as just a number, but there is no
|
||||||
|
such thing as negative number literals. (Possibly backwards-incompatible
|
||||||
|
change.)
|
||||||
|
|
||||||
|
|
||||||
|
### Version 0.4.1 (2015-02-21) ###
|
||||||
|
|
||||||
|
- Added: Support for the regex `u` flag.
|
||||||
|
|
||||||
|
|
||||||
|
### Version 0.4.0 (2015-02-21) ###
|
||||||
|
|
||||||
|
- Improved: `jsTokens.matchToToken` performance.
|
||||||
|
- Added: Support for octal and binary number literals.
|
||||||
|
- Added: Support for template strings.
|
||||||
|
|
||||||
|
|
||||||
|
### Version 0.3.1 (2015-01-06) ###
|
||||||
|
|
||||||
|
- Fixed: Support for unicode spaces. They used to be allowed in names (which is
|
||||||
|
very confusing), and some unicode newlines were wrongly allowed in strings and
|
||||||
|
regexes.
|
||||||
|
|
||||||
|
|
||||||
|
### Version 0.3.0 (2014-12-19) ###
|
||||||
|
|
||||||
|
- Changed: The `jsTokens.names` array has been replaced with the
|
||||||
|
`jsTokens.matchToToken` function. The capturing groups of `jsTokens` are no
|
||||||
|
longer part of the public API; instead use said function. See this [gist] for
|
||||||
|
an example. (Backwards-incompatible change.)
|
||||||
|
- Changed: The empty string is now considered an “invalid” token, instead an
|
||||||
|
“empty” token (its own group). (Backwards-incompatible change.)
|
||||||
|
- Removed: component support. (Backwards-incompatible change.)
|
||||||
|
|
||||||
|
[gist]: https://gist.github.com/lydell/be49dbf80c382c473004
|
||||||
|
|
||||||
|
|
||||||
|
### Version 0.2.0 (2014-06-19) ###
|
||||||
|
|
||||||
|
- Changed: Match ES6 function arrows (`=>`) as an operator, instead of its own
|
||||||
|
category (“functionArrow”), for simplicity. (Backwards-incompatible change.)
|
||||||
|
- Added: ES6 splats (`...`) are now matched as an operator (instead of three
|
||||||
|
punctuations). (Backwards-incompatible change.)
|
||||||
|
|
||||||
|
|
||||||
|
### Version 0.1.0 (2014-03-08) ###
|
||||||
|
|
||||||
|
- Initial release.
|
||||||
21
frontend/node_modules/js-tokens/LICENSE
generated
vendored
Normal file
21
frontend/node_modules/js-tokens/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014, 2015, 2016, 2017, 2018 Simon Lydell
|
||||||
|
|
||||||
|
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.
|
||||||
240
frontend/node_modules/js-tokens/README.md
generated
vendored
Normal file
240
frontend/node_modules/js-tokens/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,240 @@
|
||||||
|
Overview [](https://travis-ci.org/lydell/js-tokens)
|
||||||
|
========
|
||||||
|
|
||||||
|
A regex that tokenizes JavaScript.
|
||||||
|
|
||||||
|
```js
|
||||||
|
var jsTokens = require("js-tokens").default
|
||||||
|
|
||||||
|
var jsString = "var foo=opts.foo;\n..."
|
||||||
|
|
||||||
|
jsString.match(jsTokens)
|
||||||
|
// ["var", " ", "foo", "=", "opts", ".", "foo", ";", "\n", ...]
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
`npm install js-tokens`
|
||||||
|
|
||||||
|
```js
|
||||||
|
import jsTokens from "js-tokens"
|
||||||
|
// or:
|
||||||
|
var jsTokens = require("js-tokens").default
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
### `jsTokens` ###
|
||||||
|
|
||||||
|
A regex with the `g` flag that matches JavaScript tokens.
|
||||||
|
|
||||||
|
The regex _always_ matches, even invalid JavaScript and the empty string.
|
||||||
|
|
||||||
|
The next match is always directly after the previous.
|
||||||
|
|
||||||
|
### `var token = matchToToken(match)` ###
|
||||||
|
|
||||||
|
```js
|
||||||
|
import {matchToToken} from "js-tokens"
|
||||||
|
// or:
|
||||||
|
var matchToToken = require("js-tokens").matchToToken
|
||||||
|
```
|
||||||
|
|
||||||
|
Takes a `match` returned by `jsTokens.exec(string)`, and returns a `{type:
|
||||||
|
String, value: String}` object. The following types are available:
|
||||||
|
|
||||||
|
- string
|
||||||
|
- comment
|
||||||
|
- regex
|
||||||
|
- number
|
||||||
|
- name
|
||||||
|
- punctuator
|
||||||
|
- whitespace
|
||||||
|
- invalid
|
||||||
|
|
||||||
|
Multi-line comments and strings also have a `closed` property indicating if the
|
||||||
|
token was closed or not (see below).
|
||||||
|
|
||||||
|
Comments and strings both come in several flavors. To distinguish them, check if
|
||||||
|
the token starts with `//`, `/*`, `'`, `"` or `` ` ``.
|
||||||
|
|
||||||
|
Names are ECMAScript IdentifierNames, that is, including both identifiers and
|
||||||
|
keywords. You may use [is-keyword-js] to tell them apart.
|
||||||
|
|
||||||
|
Whitespace includes both line terminators and other whitespace.
|
||||||
|
|
||||||
|
[is-keyword-js]: https://github.com/crissdev/is-keyword-js
|
||||||
|
|
||||||
|
|
||||||
|
ECMAScript support
|
||||||
|
==================
|
||||||
|
|
||||||
|
The intention is to always support the latest ECMAScript version whose feature
|
||||||
|
set has been finalized.
|
||||||
|
|
||||||
|
If adding support for a newer version requires changes, a new version with a
|
||||||
|
major verion bump will be released.
|
||||||
|
|
||||||
|
Currently, ECMAScript 2018 is supported.
|
||||||
|
|
||||||
|
|
||||||
|
Invalid code handling
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Unterminated strings are still matched as strings. JavaScript strings cannot
|
||||||
|
contain (unescaped) newlines, so unterminated strings simply end at the end of
|
||||||
|
the line. Unterminated template strings can contain unescaped newlines, though,
|
||||||
|
so they go on to the end of input.
|
||||||
|
|
||||||
|
Unterminated multi-line comments are also still matched as comments. They
|
||||||
|
simply go on to the end of the input.
|
||||||
|
|
||||||
|
Unterminated regex literals are likely matched as division and whatever is
|
||||||
|
inside the regex.
|
||||||
|
|
||||||
|
Invalid ASCII characters have their own capturing group.
|
||||||
|
|
||||||
|
Invalid non-ASCII characters are treated as names, to simplify the matching of
|
||||||
|
names (except unicode spaces which are treated as whitespace). Note: See also
|
||||||
|
the [ES2018](#es2018) section.
|
||||||
|
|
||||||
|
Regex literals may contain invalid regex syntax. They are still matched as
|
||||||
|
regex literals. They may also contain repeated regex flags, to keep the regex
|
||||||
|
simple.
|
||||||
|
|
||||||
|
Strings may contain invalid escape sequences.
|
||||||
|
|
||||||
|
|
||||||
|
Limitations
|
||||||
|
===========
|
||||||
|
|
||||||
|
Tokenizing JavaScript using regexes—in fact, _one single regex_—won’t be
|
||||||
|
perfect. But that’s not the point either.
|
||||||
|
|
||||||
|
You may compare jsTokens with [esprima] by using `esprima-compare.js`.
|
||||||
|
See `npm run esprima-compare`!
|
||||||
|
|
||||||
|
[esprima]: http://esprima.org/
|
||||||
|
|
||||||
|
### Template string interpolation ###
|
||||||
|
|
||||||
|
Template strings are matched as single tokens, from the starting `` ` `` to the
|
||||||
|
ending `` ` ``, including interpolations (whose tokens are not matched
|
||||||
|
individually).
|
||||||
|
|
||||||
|
Matching template string interpolations requires recursive balancing of `{` and
|
||||||
|
`}`—something that JavaScript regexes cannot do. Only one level of nesting is
|
||||||
|
supported.
|
||||||
|
|
||||||
|
### Division and regex literals collision ###
|
||||||
|
|
||||||
|
Consider this example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var g = 9.82
|
||||||
|
var number = bar / 2/g
|
||||||
|
|
||||||
|
var regex = / 2/g
|
||||||
|
```
|
||||||
|
|
||||||
|
A human can easily understand that in the `number` line we’re dealing with
|
||||||
|
division, and in the `regex` line we’re dealing with a regex literal. How come?
|
||||||
|
Because humans can look at the whole code to put the `/` characters in context.
|
||||||
|
A JavaScript regex cannot. It only sees forwards. (Well, ES2018 regexes can also
|
||||||
|
look backwards. See the [ES2018](#es2018) section).
|
||||||
|
|
||||||
|
When the `jsTokens` regex scans throught the above, it will see the following
|
||||||
|
at the end of both the `number` and `regex` rows:
|
||||||
|
|
||||||
|
```js
|
||||||
|
/ 2/g
|
||||||
|
```
|
||||||
|
|
||||||
|
It is then impossible to know if that is a regex literal, or part of an
|
||||||
|
expression dealing with division.
|
||||||
|
|
||||||
|
Here is a similar case:
|
||||||
|
|
||||||
|
```js
|
||||||
|
foo /= 2/g
|
||||||
|
foo(/= 2/g)
|
||||||
|
```
|
||||||
|
|
||||||
|
The first line divides the `foo` variable with `2/g`. The second line calls the
|
||||||
|
`foo` function with the regex literal `/= 2/g`. Again, since `jsTokens` only
|
||||||
|
sees forwards, it cannot tell the two cases apart.
|
||||||
|
|
||||||
|
There are some cases where we _can_ tell division and regex literals apart,
|
||||||
|
though.
|
||||||
|
|
||||||
|
First off, we have the simple cases where there’s only one slash in the line:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var foo = 2/g
|
||||||
|
foo /= 2
|
||||||
|
```
|
||||||
|
|
||||||
|
Regex literals cannot contain newlines, so the above cases are correctly
|
||||||
|
identified as division. Things are only problematic when there are more than
|
||||||
|
one non-comment slash in a single line.
|
||||||
|
|
||||||
|
Secondly, not every character is a valid regex flag.
|
||||||
|
|
||||||
|
```js
|
||||||
|
var number = bar / 2/e
|
||||||
|
```
|
||||||
|
|
||||||
|
The above example is also correctly identified as division, because `e` is not a
|
||||||
|
valid regex flag. I initially wanted to future-proof by allowing `[a-zA-Z]*`
|
||||||
|
(any letter) as flags, but it is not worth it since it increases the amount of
|
||||||
|
ambigous cases. So only the standard `g`, `m`, `i`, `y` and `u` flags are
|
||||||
|
allowed. This means that the above example will be identified as division as
|
||||||
|
long as you don’t rename the `e` variable to some permutation of `gmiyus` 1 to 6
|
||||||
|
characters long.
|
||||||
|
|
||||||
|
Lastly, we can look _forward_ for information.
|
||||||
|
|
||||||
|
- If the token following what looks like a regex literal is not valid after a
|
||||||
|
regex literal, but is valid in a division expression, then the regex literal
|
||||||
|
is treated as division instead. For example, a flagless regex cannot be
|
||||||
|
followed by a string, number or name, but all of those three can be the
|
||||||
|
denominator of a division.
|
||||||
|
- Generally, if what looks like a regex literal is followed by an operator, the
|
||||||
|
regex literal is treated as division instead. This is because regexes are
|
||||||
|
seldomly used with operators (such as `+`, `*`, `&&` and `==`), but division
|
||||||
|
could likely be part of such an expression.
|
||||||
|
|
||||||
|
Please consult the regex source and the test cases for precise information on
|
||||||
|
when regex or division is matched (should you need to know). In short, you
|
||||||
|
could sum it up as:
|
||||||
|
|
||||||
|
If the end of a statement looks like a regex literal (even if it isn’t), it
|
||||||
|
will be treated as one. Otherwise it should work as expected (if you write sane
|
||||||
|
code).
|
||||||
|
|
||||||
|
### ES2018 ###
|
||||||
|
|
||||||
|
ES2018 added some nice regex improvements to the language.
|
||||||
|
|
||||||
|
- [Unicode property escapes] should allow telling names and invalid non-ASCII
|
||||||
|
characters apart without blowing up the regex size.
|
||||||
|
- [Lookbehind assertions] should allow matching telling division and regex
|
||||||
|
literals apart in more cases.
|
||||||
|
- [Named capture groups] might simplify some things.
|
||||||
|
|
||||||
|
These things would be nice to do, but are not critical. They probably have to
|
||||||
|
wait until the oldest maintained Node.js LTS release supports those features.
|
||||||
|
|
||||||
|
[Unicode property escapes]: http://2ality.com/2017/07/regexp-unicode-property-escapes.html
|
||||||
|
[Lookbehind assertions]: http://2ality.com/2017/05/regexp-lookbehind-assertions.html
|
||||||
|
[Named capture groups]: http://2ality.com/2017/05/regexp-named-capture-groups.html
|
||||||
|
|
||||||
|
|
||||||
|
License
|
||||||
|
=======
|
||||||
|
|
||||||
|
[MIT](LICENSE).
|
||||||
23
frontend/node_modules/js-tokens/index.js
generated
vendored
Normal file
23
frontend/node_modules/js-tokens/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
// Copyright 2014, 2015, 2016, 2017, 2018 Simon Lydell
|
||||||
|
// License: MIT. (See LICENSE.)
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// This regex comes from regex.coffee, and is inserted here by generate-index.js
|
||||||
|
// (run `npm run build`).
|
||||||
|
exports.default = /((['"])(?:(?!\2|\\).|\\(?:\r\n|[\s\S]))*(\2)?|`(?:[^`\\$]|\\[\s\S]|\$(?!\{)|\$\{(?:[^{}]|\{[^}]*\}?)*\}?)*(`)?)|(\/\/.*)|(\/\*(?:[^*]|\*(?!\/))*(\*\/)?)|(\/(?!\*)(?:\[(?:(?![\]\\]).|\\.)*\]|(?![\/\]\\]).|\\.)+\/(?:(?!\s*(?:\b|[\u0080-\uFFFF$\\'"~({]|[+\-!](?!=)|\.?\d))|[gmiyus]{1,6}\b(?![\u0080-\uFFFF$\\]|\s*(?:[+\-*%&|^<>!=?({]|\/(?![\/*])))))|(0[xX][\da-fA-F]+|0[oO][0-7]+|0[bB][01]+|(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?)|((?!\d)(?:(?!\s)[$\w\u0080-\uFFFF]|\\u[\da-fA-F]{4}|\\u\{[\da-fA-F]+\})+)|(--|\+\+|&&|\|\||=>|\.{3}|(?:[+\-\/%&|^]|\*{1,2}|<{1,2}|>{1,3}|!=?|={1,2})=?|[?~.,:;[\](){}])|(\s+)|(^$|[\s\S])/g
|
||||||
|
|
||||||
|
exports.matchToToken = function(match) {
|
||||||
|
var token = {type: "invalid", value: match[0], closed: undefined}
|
||||||
|
if (match[ 1]) token.type = "string" , token.closed = !!(match[3] || match[4])
|
||||||
|
else if (match[ 5]) token.type = "comment"
|
||||||
|
else if (match[ 6]) token.type = "comment", token.closed = !!match[7]
|
||||||
|
else if (match[ 8]) token.type = "regex"
|
||||||
|
else if (match[ 9]) token.type = "number"
|
||||||
|
else if (match[10]) token.type = "name"
|
||||||
|
else if (match[11]) token.type = "punctuator"
|
||||||
|
else if (match[12]) token.type = "whitespace"
|
||||||
|
return token
|
||||||
|
}
|
||||||
30
frontend/node_modules/js-tokens/package.json
generated
vendored
Normal file
30
frontend/node_modules/js-tokens/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"name": "js-tokens",
|
||||||
|
"version": "4.0.0",
|
||||||
|
"author": "Simon Lydell",
|
||||||
|
"license": "MIT",
|
||||||
|
"description": "A regex that tokenizes JavaScript.",
|
||||||
|
"keywords": [
|
||||||
|
"JavaScript",
|
||||||
|
"js",
|
||||||
|
"token",
|
||||||
|
"tokenize",
|
||||||
|
"regex"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"index.js"
|
||||||
|
],
|
||||||
|
"repository": "lydell/js-tokens",
|
||||||
|
"scripts": {
|
||||||
|
"test": "mocha --ui tdd",
|
||||||
|
"esprima-compare": "node esprima-compare ./index.js everything.js/es5.js",
|
||||||
|
"build": "node generate-index.js",
|
||||||
|
"dev": "npm run build && npm test"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"coffeescript": "2.1.1",
|
||||||
|
"esprima": "4.0.0",
|
||||||
|
"everything.js": "1.0.3",
|
||||||
|
"mocha": "5.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
21
frontend/node_modules/loose-envify/LICENSE
generated
vendored
Normal file
21
frontend/node_modules/loose-envify/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Andres Suarez <zertosh@gmail.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.
|
||||||
45
frontend/node_modules/loose-envify/README.md
generated
vendored
Normal file
45
frontend/node_modules/loose-envify/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
# loose-envify
|
||||||
|
|
||||||
|
[](https://travis-ci.org/zertosh/loose-envify)
|
||||||
|
|
||||||
|
Fast (and loose) selective `process.env` replacer using [js-tokens](https://github.com/lydell/js-tokens) instead of an AST. Works just like [envify](https://github.com/hughsk/envify) but much faster.
|
||||||
|
|
||||||
|
## Gotchas
|
||||||
|
|
||||||
|
* Doesn't handle broken syntax.
|
||||||
|
* Doesn't look inside embedded expressions in template strings.
|
||||||
|
- **this won't work:**
|
||||||
|
```js
|
||||||
|
console.log(`the current env is ${process.env.NODE_ENV}`);
|
||||||
|
```
|
||||||
|
* Doesn't replace oddly-spaced or oddly-commented expressions.
|
||||||
|
- **this won't work:**
|
||||||
|
```js
|
||||||
|
console.log(process./*won't*/env./*work*/NODE_ENV);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage/Options
|
||||||
|
|
||||||
|
loose-envify has the exact same interface as [envify](https://github.com/hughsk/envify), including the CLI.
|
||||||
|
|
||||||
|
## Benchmark
|
||||||
|
|
||||||
|
```
|
||||||
|
envify:
|
||||||
|
|
||||||
|
$ for i in {1..5}; do node bench/bench.js 'envify'; done
|
||||||
|
708ms
|
||||||
|
727ms
|
||||||
|
791ms
|
||||||
|
719ms
|
||||||
|
720ms
|
||||||
|
|
||||||
|
loose-envify:
|
||||||
|
|
||||||
|
$ for i in {1..5}; do node bench/bench.js '../'; done
|
||||||
|
51ms
|
||||||
|
52ms
|
||||||
|
52ms
|
||||||
|
52ms
|
||||||
|
52ms
|
||||||
|
```
|
||||||
16
frontend/node_modules/loose-envify/cli.js
generated
vendored
Normal file
16
frontend/node_modules/loose-envify/cli.js
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var looseEnvify = require('./');
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
|
if (process.argv[2]) {
|
||||||
|
fs.createReadStream(process.argv[2], {encoding: 'utf8'})
|
||||||
|
.pipe(looseEnvify(process.argv[2]))
|
||||||
|
.pipe(process.stdout);
|
||||||
|
} else {
|
||||||
|
process.stdin.resume()
|
||||||
|
process.stdin
|
||||||
|
.pipe(looseEnvify(__filename))
|
||||||
|
.pipe(process.stdout);
|
||||||
|
}
|
||||||
4
frontend/node_modules/loose-envify/custom.js
generated
vendored
Normal file
4
frontend/node_modules/loose-envify/custom.js
generated
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
// envify compatibility
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = require('./loose-envify');
|
||||||
3
frontend/node_modules/loose-envify/index.js
generated
vendored
Normal file
3
frontend/node_modules/loose-envify/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = require('./loose-envify')(process.env);
|
||||||
36
frontend/node_modules/loose-envify/loose-envify.js
generated
vendored
Normal file
36
frontend/node_modules/loose-envify/loose-envify.js
generated
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var stream = require('stream');
|
||||||
|
var util = require('util');
|
||||||
|
var replace = require('./replace');
|
||||||
|
|
||||||
|
var jsonExtRe = /\.json$/;
|
||||||
|
|
||||||
|
module.exports = function(rootEnv) {
|
||||||
|
rootEnv = rootEnv || process.env;
|
||||||
|
return function (file, trOpts) {
|
||||||
|
if (jsonExtRe.test(file)) {
|
||||||
|
return stream.PassThrough();
|
||||||
|
}
|
||||||
|
var envs = trOpts ? [rootEnv, trOpts] : [rootEnv];
|
||||||
|
return new LooseEnvify(envs);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
function LooseEnvify(envs) {
|
||||||
|
stream.Transform.call(this);
|
||||||
|
this._data = '';
|
||||||
|
this._envs = envs;
|
||||||
|
}
|
||||||
|
util.inherits(LooseEnvify, stream.Transform);
|
||||||
|
|
||||||
|
LooseEnvify.prototype._transform = function(buf, enc, cb) {
|
||||||
|
this._data += buf;
|
||||||
|
cb();
|
||||||
|
};
|
||||||
|
|
||||||
|
LooseEnvify.prototype._flush = function(cb) {
|
||||||
|
var replaced = replace(this._data, this._envs);
|
||||||
|
this.push(replaced);
|
||||||
|
cb();
|
||||||
|
};
|
||||||
36
frontend/node_modules/loose-envify/package.json
generated
vendored
Normal file
36
frontend/node_modules/loose-envify/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"name": "loose-envify",
|
||||||
|
"version": "1.4.0",
|
||||||
|
"description": "Fast (and loose) selective `process.env` replacer using js-tokens instead of an AST",
|
||||||
|
"keywords": [
|
||||||
|
"environment",
|
||||||
|
"variables",
|
||||||
|
"browserify",
|
||||||
|
"browserify-transform",
|
||||||
|
"transform",
|
||||||
|
"source",
|
||||||
|
"configuration"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/zertosh/loose-envify",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "Andres Suarez <zertosh@gmail.com>",
|
||||||
|
"main": "index.js",
|
||||||
|
"bin": {
|
||||||
|
"loose-envify": "cli.js"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/zertosh/loose-envify.git"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "tap test/*.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"browserify": "^13.1.1",
|
||||||
|
"envify": "^3.4.0",
|
||||||
|
"tap": "^8.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
65
frontend/node_modules/loose-envify/replace.js
generated
vendored
Normal file
65
frontend/node_modules/loose-envify/replace.js
generated
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var jsTokens = require('js-tokens').default;
|
||||||
|
|
||||||
|
var processEnvRe = /\bprocess\.env\.[_$a-zA-Z][$\w]+\b/;
|
||||||
|
var spaceOrCommentRe = /^(?:\s|\/[/*])/;
|
||||||
|
|
||||||
|
function replace(src, envs) {
|
||||||
|
if (!processEnvRe.test(src)) {
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
var out = [];
|
||||||
|
var purge = envs.some(function(env) {
|
||||||
|
return env._ && env._.indexOf('purge') !== -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
jsTokens.lastIndex = 0
|
||||||
|
var parts = src.match(jsTokens);
|
||||||
|
|
||||||
|
for (var i = 0; i < parts.length; i++) {
|
||||||
|
if (parts[i ] === 'process' &&
|
||||||
|
parts[i + 1] === '.' &&
|
||||||
|
parts[i + 2] === 'env' &&
|
||||||
|
parts[i + 3] === '.') {
|
||||||
|
var prevCodeToken = getAdjacentCodeToken(-1, parts, i);
|
||||||
|
var nextCodeToken = getAdjacentCodeToken(1, parts, i + 4);
|
||||||
|
var replacement = getReplacementString(envs, parts[i + 4], purge);
|
||||||
|
if (prevCodeToken !== '.' &&
|
||||||
|
nextCodeToken !== '.' &&
|
||||||
|
nextCodeToken !== '=' &&
|
||||||
|
typeof replacement === 'string') {
|
||||||
|
out.push(replacement);
|
||||||
|
i += 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.push(parts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAdjacentCodeToken(dir, parts, i) {
|
||||||
|
while (true) {
|
||||||
|
var part = parts[i += dir];
|
||||||
|
if (!spaceOrCommentRe.test(part)) {
|
||||||
|
return part;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getReplacementString(envs, name, purge) {
|
||||||
|
for (var j = 0; j < envs.length; j++) {
|
||||||
|
var env = envs[j];
|
||||||
|
if (typeof env[name] !== 'undefined') {
|
||||||
|
return JSON.stringify(env[name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (purge) {
|
||||||
|
return 'undefined';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = replace;
|
||||||
21
frontend/node_modules/make-cancellable-promise/LICENSE
generated
vendored
Normal file
21
frontend/node_modules/make-cancellable-promise/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019–2024 Wojciech Maj
|
||||||
|
|
||||||
|
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.
|
||||||
80
frontend/node_modules/make-cancellable-promise/README.md
generated
vendored
Normal file
80
frontend/node_modules/make-cancellable-promise/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
[](https://www.npmjs.com/package/make-cancellable-promise)  [](https://github.com/wojtekmaj/make-cancellable-promise/actions)
|
||||||
|
|
||||||
|
# Make-Cancellable-Promise
|
||||||
|
|
||||||
|
Make any Promise cancellable.
|
||||||
|
|
||||||
|
## tl;dr
|
||||||
|
|
||||||
|
- Install by executing `npm install make-cancellable-promise` or `yarn add make-cancellable-promise`.
|
||||||
|
- Import by adding `import makeCancellablePromise from 'make-cancellable-promise`.
|
||||||
|
- Do stuff with it!
|
||||||
|
```ts
|
||||||
|
const { promise, cancel } = makeCancellablePromise(myPromise);
|
||||||
|
```
|
||||||
|
|
||||||
|
## User guide
|
||||||
|
|
||||||
|
### makeCancellablePromise(myPromise)
|
||||||
|
|
||||||
|
A function that returns an object with two properties:
|
||||||
|
|
||||||
|
`promise` and `cancel`. `promise` is a wrapped around your promise. `cancel` is a function which stops `.then()` and `.catch()` from working on `promise`, even if promise passed to `makeCancellablePromise` resolves or rejects.
|
||||||
|
|
||||||
|
#### Usage
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const { promise, cancel } = makeCancellablePromise(myPromise);
|
||||||
|
```
|
||||||
|
|
||||||
|
Typically, you'd want to use `makeCancellablePromise` in React components. If you call `setState` on an unmounted component, React will throw an error.
|
||||||
|
|
||||||
|
Here's how you can use `makeCancellablePromise` with React:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
function MyComponent() {
|
||||||
|
const [status, setStatus] = useState('initial');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const { promise, cancel } = makeCancellable(fetchData());
|
||||||
|
|
||||||
|
promise.then(() => setStatus('success')).catch(() => setStatus('error'));
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancel();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const text = (() => {
|
||||||
|
switch (status) {
|
||||||
|
case 'pending':
|
||||||
|
return 'Fetching…';
|
||||||
|
case 'success':
|
||||||
|
return 'Success';
|
||||||
|
case 'error':
|
||||||
|
return 'Error!';
|
||||||
|
default:
|
||||||
|
return 'Click to fetch';
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
return <p>{text}</p>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The MIT License.
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td >
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/5426427?v=4&s=128" width="64" height="64" alt="Wojciech Maj">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="https://github.com/wojtekmaj">Wojciech Maj</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
54
frontend/node_modules/make-cancellable-promise/package.json
generated
vendored
Normal file
54
frontend/node_modules/make-cancellable-promise/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
{
|
||||||
|
"name": "make-cancellable-promise",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"description": "Make any Promise cancellable.",
|
||||||
|
"type": "module",
|
||||||
|
"sideEffects": false,
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"source": "./src/index.ts",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"exports": {
|
||||||
|
".": "./dist/index.js",
|
||||||
|
"./*": "./*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc --project tsconfig.build.json",
|
||||||
|
"clean": "rimraf dist",
|
||||||
|
"format": "biome format",
|
||||||
|
"lint": "biome lint",
|
||||||
|
"prepack": "yarn clean && yarn build",
|
||||||
|
"test": "yarn lint && yarn tsc && yarn format && yarn unit",
|
||||||
|
"tsc": "tsc",
|
||||||
|
"unit": "vitest"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"promise",
|
||||||
|
"promise-cancelling"
|
||||||
|
],
|
||||||
|
"author": {
|
||||||
|
"name": "Wojciech Maj",
|
||||||
|
"email": "kontakt@wojtekmaj.pl"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@biomejs/biome": "1.9.0",
|
||||||
|
"husky": "^9.0.0",
|
||||||
|
"rimraf": "^6.0.0",
|
||||||
|
"typescript": "^5.5.2",
|
||||||
|
"vitest": "^3.0.5"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public",
|
||||||
|
"provenance": true
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"src"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/wojtekmaj/make-cancellable-promise.git"
|
||||||
|
},
|
||||||
|
"funding": "https://github.com/wojtekmaj/make-cancellable-promise?sponsor=1",
|
||||||
|
"packageManager": "yarn@4.3.1"
|
||||||
|
}
|
||||||
79
frontend/node_modules/make-cancellable-promise/src/index.spec.ts
generated
vendored
Normal file
79
frontend/node_modules/make-cancellable-promise/src/index.spec.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
import { describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
import makeCancellablePromise from './index.js';
|
||||||
|
|
||||||
|
vi.useFakeTimers();
|
||||||
|
|
||||||
|
describe('makeCancellablePromise()', () => {
|
||||||
|
function resolveInFiveSeconds(): Promise<string> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve('Success');
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function rejectInFiveSeconds() {
|
||||||
|
return new Promise((_resolve, reject) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
reject(new Error('Error'));
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
it('resolves promise if not cancelled', async () => {
|
||||||
|
const resolve = vi.fn();
|
||||||
|
const reject = vi.fn();
|
||||||
|
|
||||||
|
const { promise } = makeCancellablePromise(resolveInFiveSeconds());
|
||||||
|
|
||||||
|
vi.advanceTimersByTime(5000);
|
||||||
|
await promise.then(resolve).catch(reject);
|
||||||
|
|
||||||
|
expect(resolve).toHaveBeenCalledWith('Success');
|
||||||
|
expect(reject).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects promise if not cancelled', async () => {
|
||||||
|
const resolve = vi.fn();
|
||||||
|
const reject = vi.fn();
|
||||||
|
|
||||||
|
const { promise } = makeCancellablePromise(rejectInFiveSeconds());
|
||||||
|
|
||||||
|
vi.runAllTimers();
|
||||||
|
await promise.then(resolve).catch(reject);
|
||||||
|
|
||||||
|
expect(resolve).not.toHaveBeenCalled();
|
||||||
|
expect(reject).toHaveBeenCalledWith(expect.any(Error));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not resolve promise if cancelled', async () => {
|
||||||
|
const resolve = vi.fn();
|
||||||
|
const reject = vi.fn();
|
||||||
|
|
||||||
|
const { promise, cancel } = makeCancellablePromise(rejectInFiveSeconds());
|
||||||
|
promise.then(resolve).catch(reject);
|
||||||
|
|
||||||
|
vi.advanceTimersByTime(2500);
|
||||||
|
cancel();
|
||||||
|
vi.advanceTimersByTime(2500);
|
||||||
|
|
||||||
|
expect(resolve).not.toHaveBeenCalled();
|
||||||
|
expect(reject).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not reject promise if cancelled', () => {
|
||||||
|
const resolve = vi.fn();
|
||||||
|
const reject = vi.fn();
|
||||||
|
|
||||||
|
const { promise, cancel } = makeCancellablePromise(rejectInFiveSeconds());
|
||||||
|
promise.then(resolve).catch(reject);
|
||||||
|
|
||||||
|
vi.advanceTimersByTime(2500);
|
||||||
|
cancel();
|
||||||
|
vi.advanceTimersByTime(2500);
|
||||||
|
|
||||||
|
expect(resolve).not.toHaveBeenCalled();
|
||||||
|
expect(reject).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
19
frontend/node_modules/make-cancellable-promise/src/index.ts
generated
vendored
Normal file
19
frontend/node_modules/make-cancellable-promise/src/index.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
export default function makeCancellablePromise<T>(promise: Promise<T>): {
|
||||||
|
promise: Promise<T>;
|
||||||
|
cancel(): void;
|
||||||
|
} {
|
||||||
|
let isCancelled = false;
|
||||||
|
|
||||||
|
const wrappedPromise: Promise<T> = new Promise((resolve, reject) => {
|
||||||
|
promise
|
||||||
|
.then((value) => !isCancelled && resolve(value))
|
||||||
|
.catch((error) => !isCancelled && reject(error));
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
promise: wrappedPromise,
|
||||||
|
cancel() {
|
||||||
|
isCancelled = true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
21
frontend/node_modules/make-event-props/LICENSE
generated
vendored
Normal file
21
frontend/node_modules/make-event-props/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018–2024 Wojciech Maj
|
||||||
|
|
||||||
|
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.
|
||||||
40
frontend/node_modules/make-event-props/README.md
generated
vendored
Normal file
40
frontend/node_modules/make-event-props/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[](https://www.npmjs.com/package/make-event-props)  [](https://github.com/wojtekmaj/make-event-props/actions)
|
||||||
|
|
||||||
|
# Make-Event-Props
|
||||||
|
|
||||||
|
A function that, given props, returns an object of event callback props optionally curried with additional arguments.
|
||||||
|
|
||||||
|
This package allows you to pass event callback props to a rendered DOM element without the risk of applying any invalid props that could cause unwanted side effects.
|
||||||
|
|
||||||
|
## tl;dr
|
||||||
|
|
||||||
|
- Install by executing `npm install make-event-props` or `yarn add make-event-props`.
|
||||||
|
- Import by adding `import makeEventProps from 'make-event-props'`.
|
||||||
|
- Create your event props object:
|
||||||
|
```ts
|
||||||
|
const eventProps = useMemo(
|
||||||
|
() => makeEventProps(props, (eventName) => additionalArgs),
|
||||||
|
[additionalArgs],
|
||||||
|
);
|
||||||
|
```
|
||||||
|
- Use your event props:
|
||||||
|
```tsx
|
||||||
|
return <div {...eventProps} />;
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The MIT License.
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td >
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/5426427?v=4&s=128" width="64" height="64" alt="Wojciech Maj">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="https://github.com/wojtekmaj">Wojciech Maj</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
58
frontend/node_modules/make-event-props/package.json
generated
vendored
Normal file
58
frontend/node_modules/make-event-props/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
{
|
||||||
|
"name": "make-event-props",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"description": "Returns an object with on-event callback props curried with provided args.",
|
||||||
|
"type": "module",
|
||||||
|
"sideEffects": false,
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"source": "./src/index.ts",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"exports": {
|
||||||
|
".": "./dist/index.js",
|
||||||
|
"./*": "./*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc --project tsconfig.build.json",
|
||||||
|
"clean": "rimraf dist",
|
||||||
|
"format": "biome format",
|
||||||
|
"lint": "biome lint",
|
||||||
|
"prepack": "yarn clean && yarn build",
|
||||||
|
"test": "yarn lint && yarn tsc && yarn format && yarn unit",
|
||||||
|
"tsc": "tsc",
|
||||||
|
"unit": "vitest --typecheck"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"react",
|
||||||
|
"event",
|
||||||
|
"event props"
|
||||||
|
],
|
||||||
|
"author": {
|
||||||
|
"name": "Wojciech Maj",
|
||||||
|
"email": "kontakt@wojtekmaj.pl"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@biomejs/biome": "1.9.0",
|
||||||
|
"@types/react": "*",
|
||||||
|
"husky": "^9.0.0",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"rimraf": "^6.0.0",
|
||||||
|
"typescript": "^5.5.2",
|
||||||
|
"vitest": "^3.0.5"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public",
|
||||||
|
"provenance": true
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"src"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/wojtekmaj/make-event-props.git"
|
||||||
|
},
|
||||||
|
"funding": "https://github.com/wojtekmaj/make-event-props?sponsor=1",
|
||||||
|
"packageManager": "yarn@4.3.1"
|
||||||
|
}
|
||||||
86
frontend/node_modules/make-event-props/src/__snapshots__/index.spec.tsx.snap
generated
vendored
Normal file
86
frontend/node_modules/make-event-props/src/__snapshots__/index.spec.tsx.snap
generated
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`allEvents > should contain all events 1`] = `
|
||||||
|
Set {
|
||||||
|
"onAbort",
|
||||||
|
"onAnimationEnd",
|
||||||
|
"onAnimationIteration",
|
||||||
|
"onAnimationStart",
|
||||||
|
"onBlur",
|
||||||
|
"onCanPlay",
|
||||||
|
"onCanPlayThrough",
|
||||||
|
"onChange",
|
||||||
|
"onClick",
|
||||||
|
"onCompositionEnd",
|
||||||
|
"onCompositionStart",
|
||||||
|
"onCompositionUpdate",
|
||||||
|
"onContextMenu",
|
||||||
|
"onCopy",
|
||||||
|
"onCut",
|
||||||
|
"onDoubleClick",
|
||||||
|
"onDrag",
|
||||||
|
"onDragEnd",
|
||||||
|
"onDragEnter",
|
||||||
|
"onDragExit",
|
||||||
|
"onDragLeave",
|
||||||
|
"onDragOver",
|
||||||
|
"onDragStart",
|
||||||
|
"onDrop",
|
||||||
|
"onDurationChange",
|
||||||
|
"onEmptied",
|
||||||
|
"onEncrypted",
|
||||||
|
"onEnded",
|
||||||
|
"onError",
|
||||||
|
"onFocus",
|
||||||
|
"onGotPointerCapture",
|
||||||
|
"onInput",
|
||||||
|
"onInvalid",
|
||||||
|
"onKeyDown",
|
||||||
|
"onKeyPress",
|
||||||
|
"onKeyUp",
|
||||||
|
"onLoad",
|
||||||
|
"onLoadStart",
|
||||||
|
"onLoadedData",
|
||||||
|
"onLoadedMetadata",
|
||||||
|
"onLostPointerCapture",
|
||||||
|
"onMouseDown",
|
||||||
|
"onMouseEnter",
|
||||||
|
"onMouseLeave",
|
||||||
|
"onMouseMove",
|
||||||
|
"onMouseOut",
|
||||||
|
"onMouseOver",
|
||||||
|
"onMouseUp",
|
||||||
|
"onPaste",
|
||||||
|
"onPause",
|
||||||
|
"onPlay",
|
||||||
|
"onPlaying",
|
||||||
|
"onPointerCancel",
|
||||||
|
"onPointerDown",
|
||||||
|
"onPointerEnter",
|
||||||
|
"onPointerLeave",
|
||||||
|
"onPointerMove",
|
||||||
|
"onPointerOut",
|
||||||
|
"onPointerOver",
|
||||||
|
"onPointerUp",
|
||||||
|
"onProgress",
|
||||||
|
"onRateChange",
|
||||||
|
"onReset",
|
||||||
|
"onScroll",
|
||||||
|
"onSeeked",
|
||||||
|
"onSeeking",
|
||||||
|
"onSelect",
|
||||||
|
"onStalled",
|
||||||
|
"onSubmit",
|
||||||
|
"onSuspend",
|
||||||
|
"onTimeUpdate",
|
||||||
|
"onToggle",
|
||||||
|
"onTouchCancel",
|
||||||
|
"onTouchEnd",
|
||||||
|
"onTouchMove",
|
||||||
|
"onTouchStart",
|
||||||
|
"onTransitionEnd",
|
||||||
|
"onVolumeChange",
|
||||||
|
"onWaiting",
|
||||||
|
"onWheel",
|
||||||
|
}
|
||||||
|
`;
|
||||||
229
frontend/node_modules/make-event-props/src/index.spec.tsx
generated
vendored
Normal file
229
frontend/node_modules/make-event-props/src/index.spec.tsx
generated
vendored
Normal file
|
|
@ -0,0 +1,229 @@
|
||||||
|
import { assertType, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
import makeEventProps, { allEvents } from './index.js';
|
||||||
|
|
||||||
|
describe('makeEventProps()', () => {
|
||||||
|
const fakeEvent = {};
|
||||||
|
|
||||||
|
it('returns object with valid and only valid event callbacks', () => {
|
||||||
|
const props = {
|
||||||
|
onClick: vi.fn(),
|
||||||
|
someInvalidProp: vi.fn(),
|
||||||
|
};
|
||||||
|
const result = makeEventProps(props);
|
||||||
|
|
||||||
|
expect(result).toMatchObject({ onClick: expect.any(Function) });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls getArgs function on event invoke if given', () => {
|
||||||
|
const props = {
|
||||||
|
onClick: vi.fn(),
|
||||||
|
someInvalidProp: vi.fn(),
|
||||||
|
};
|
||||||
|
const getArgs = vi.fn();
|
||||||
|
const result = makeEventProps(props, getArgs);
|
||||||
|
|
||||||
|
// getArgs shall not be invoked before a given event is fired
|
||||||
|
expect(getArgs).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
result.onClick(fakeEvent);
|
||||||
|
|
||||||
|
expect(getArgs).toHaveBeenCalledTimes(1);
|
||||||
|
expect(getArgs).toHaveBeenCalledWith('onClick');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('properly calls callbacks given in props given no getArgs function', () => {
|
||||||
|
const props = {
|
||||||
|
onClick: vi.fn(),
|
||||||
|
};
|
||||||
|
const result = makeEventProps(props);
|
||||||
|
|
||||||
|
result.onClick(fakeEvent);
|
||||||
|
|
||||||
|
expect(props.onClick).toHaveBeenCalledWith(fakeEvent);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('properly calls callbacks given in props given getArgs function', () => {
|
||||||
|
const props = {
|
||||||
|
onClick: vi.fn(),
|
||||||
|
};
|
||||||
|
const getArgs = vi.fn();
|
||||||
|
const args = {};
|
||||||
|
getArgs.mockReturnValue(args);
|
||||||
|
const result = makeEventProps(props, getArgs);
|
||||||
|
|
||||||
|
result.onClick(fakeEvent);
|
||||||
|
|
||||||
|
expect(props.onClick).toHaveBeenCalledWith(fakeEvent, args);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not filter out valid event props', () => {
|
||||||
|
const props = {
|
||||||
|
onClick: vi.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = makeEventProps(props);
|
||||||
|
|
||||||
|
assertType<React.MouseEventHandler>(result.onClick);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter out invalid event props', () => {
|
||||||
|
const props = {
|
||||||
|
someInvalidProp: vi.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = makeEventProps(props);
|
||||||
|
|
||||||
|
expect(result).not.toHaveProperty('someInvalidProp');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow valid onClick handler to be passed', () => {
|
||||||
|
const props = {
|
||||||
|
onClick: (_event: React.MouseEvent) => {
|
||||||
|
// Intentionally empty
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-no-error
|
||||||
|
makeEventProps(props);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not allow invalid onClick handler to be passed', () => {
|
||||||
|
const props = {
|
||||||
|
onClick: 'potato',
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error-next-line
|
||||||
|
makeEventProps(props);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow onClick handler with extra args to be passed if getArgs is provided', () => {
|
||||||
|
const props = {
|
||||||
|
onClick: (_event: React.MouseEvent, _args: string) => {
|
||||||
|
// Intentionally empty
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-no-error
|
||||||
|
makeEventProps(props, () => 'hello');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not allow onClick handler with extra args to be passed if getArgs is not provided', () => {
|
||||||
|
const props = {
|
||||||
|
onClick: (_event: React.MouseEvent, _args: string) => {
|
||||||
|
// Intentionally empty
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error-next-line
|
||||||
|
makeEventProps(props);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not allow onClick handler with extra args to be passed if getArgs is provided but returns different type', () => {
|
||||||
|
const props = {
|
||||||
|
onClick: (_event: React.MouseEvent, _args: string) => {
|
||||||
|
// Intentionally empty
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error-next-line
|
||||||
|
makeEventProps(props, () => 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow div onClick handler to be passed to div', () => {
|
||||||
|
const props = {
|
||||||
|
onClick: (_event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
// Intentionally empty
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = makeEventProps(props);
|
||||||
|
|
||||||
|
// @ts-expect-no-error
|
||||||
|
// biome-ignore lint/a11y/useKeyWithClickEvents: This is only a test
|
||||||
|
<div onClick={result.onClick} />;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not allow div onClick handler to be passed to button', () => {
|
||||||
|
const props = {
|
||||||
|
onClick: (_event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
// Intentionally empty
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = makeEventProps(props);
|
||||||
|
|
||||||
|
// @ts-expect-error-next-line
|
||||||
|
<button onClick={result.onClick} type="submit" />;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow div onClick handler with extra args to be passed to div if getArgs is provided', () => {
|
||||||
|
const props = {
|
||||||
|
onClick: (_event: React.MouseEvent<HTMLDivElement>, _args: string) => {
|
||||||
|
// Intentionally empty
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = makeEventProps(props, () => 'hello');
|
||||||
|
|
||||||
|
// @ts-expect-no-error
|
||||||
|
// biome-ignore lint/a11y/useKeyWithClickEvents: This is only a test
|
||||||
|
<div onClick={result.onClick} />;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not allow div onClick handler with extra args to be passed to button if getArgs is provided', () => {
|
||||||
|
const props = {
|
||||||
|
onClick: (_event: React.MouseEvent<HTMLDivElement>, _args: string) => {
|
||||||
|
// Intentionally empty
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = makeEventProps(props, () => 'hello');
|
||||||
|
|
||||||
|
// @ts-expect-error-next-line
|
||||||
|
<button onClick={result.onClick} type="submit" />;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow onClick handler with valid extra args to be passed with args explicitly typed', () => {
|
||||||
|
const props = {
|
||||||
|
onClick: (_event: React.MouseEvent<HTMLDivElement>, _args: string) => {
|
||||||
|
// Intentionally empty
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-no-error
|
||||||
|
makeEventProps<string>(props, () => 'hello');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not allow onClick handler with invalid extra args to be passed with args explicitly typed', () => {
|
||||||
|
const props = {
|
||||||
|
onClick: (_event: React.MouseEvent<HTMLDivElement>, _args: number) => {
|
||||||
|
// Intentionally empty
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error-next-line
|
||||||
|
makeEventProps<string>(props, () => 'hello');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow getArgs returning valid type to be passed with args explicitly typed', () => {
|
||||||
|
const props = {};
|
||||||
|
|
||||||
|
// @ts-expect-no-error
|
||||||
|
makeEventProps<string>(props, () => 'hello');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not allow getArgs returning invalid type to be passed with args explicitly typed', () => {
|
||||||
|
const props = {};
|
||||||
|
|
||||||
|
// @ts-expect-error-next-line
|
||||||
|
makeEventProps<string>(props, () => 5);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('allEvents', () => {
|
||||||
|
it('should contain all events', () => {
|
||||||
|
const sortedAllEvents = new Set([...allEvents].sort());
|
||||||
|
expect(sortedAllEvents).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
250
frontend/node_modules/make-event-props/src/index.ts
generated
vendored
Normal file
250
frontend/node_modules/make-event-props/src/index.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,250 @@
|
||||||
|
// As defined on the list of supported events: https://reactjs.org/docs/events.html
|
||||||
|
export const clipboardEvents = ['onCopy', 'onCut', 'onPaste'] as const;
|
||||||
|
export const compositionEvents = [
|
||||||
|
'onCompositionEnd',
|
||||||
|
'onCompositionStart',
|
||||||
|
'onCompositionUpdate',
|
||||||
|
] as const;
|
||||||
|
export const focusEvents = ['onFocus', 'onBlur'] as const;
|
||||||
|
export const formEvents = ['onInput', 'onInvalid', 'onReset', 'onSubmit'] as const;
|
||||||
|
export const imageEvents = ['onLoad', 'onError'] as const;
|
||||||
|
export const keyboardEvents = ['onKeyDown', 'onKeyPress', 'onKeyUp'] as const;
|
||||||
|
export const mediaEvents = [
|
||||||
|
'onAbort',
|
||||||
|
'onCanPlay',
|
||||||
|
'onCanPlayThrough',
|
||||||
|
'onDurationChange',
|
||||||
|
'onEmptied',
|
||||||
|
'onEncrypted',
|
||||||
|
'onEnded',
|
||||||
|
'onError',
|
||||||
|
'onLoadedData',
|
||||||
|
'onLoadedMetadata',
|
||||||
|
'onLoadStart',
|
||||||
|
'onPause',
|
||||||
|
'onPlay',
|
||||||
|
'onPlaying',
|
||||||
|
'onProgress',
|
||||||
|
'onRateChange',
|
||||||
|
'onSeeked',
|
||||||
|
'onSeeking',
|
||||||
|
'onStalled',
|
||||||
|
'onSuspend',
|
||||||
|
'onTimeUpdate',
|
||||||
|
'onVolumeChange',
|
||||||
|
'onWaiting',
|
||||||
|
] as const;
|
||||||
|
export const mouseEvents = [
|
||||||
|
'onClick',
|
||||||
|
'onContextMenu',
|
||||||
|
'onDoubleClick',
|
||||||
|
'onMouseDown',
|
||||||
|
'onMouseEnter',
|
||||||
|
'onMouseLeave',
|
||||||
|
'onMouseMove',
|
||||||
|
'onMouseOut',
|
||||||
|
'onMouseOver',
|
||||||
|
'onMouseUp',
|
||||||
|
] as const;
|
||||||
|
export const dragEvents = [
|
||||||
|
'onDrag',
|
||||||
|
'onDragEnd',
|
||||||
|
'onDragEnter',
|
||||||
|
'onDragExit',
|
||||||
|
'onDragLeave',
|
||||||
|
'onDragOver',
|
||||||
|
'onDragStart',
|
||||||
|
'onDrop',
|
||||||
|
] as const;
|
||||||
|
export const selectionEvents = ['onSelect'] as const;
|
||||||
|
export const touchEvents = ['onTouchCancel', 'onTouchEnd', 'onTouchMove', 'onTouchStart'] as const;
|
||||||
|
export const pointerEvents = [
|
||||||
|
'onPointerDown',
|
||||||
|
'onPointerMove',
|
||||||
|
'onPointerUp',
|
||||||
|
'onPointerCancel',
|
||||||
|
'onGotPointerCapture',
|
||||||
|
'onLostPointerCapture',
|
||||||
|
'onPointerEnter',
|
||||||
|
'onPointerLeave',
|
||||||
|
'onPointerOver',
|
||||||
|
'onPointerOut',
|
||||||
|
] as const;
|
||||||
|
export const uiEvents = ['onScroll'] as const;
|
||||||
|
export const wheelEvents = ['onWheel'] as const;
|
||||||
|
export const animationEvents = [
|
||||||
|
'onAnimationStart',
|
||||||
|
'onAnimationEnd',
|
||||||
|
'onAnimationIteration',
|
||||||
|
] as const;
|
||||||
|
export const transitionEvents = ['onTransitionEnd'] as const;
|
||||||
|
export const otherEvents = ['onToggle'] as const;
|
||||||
|
export const changeEvents = ['onChange'] as const;
|
||||||
|
|
||||||
|
export const allEvents: readonly [
|
||||||
|
'onCopy',
|
||||||
|
'onCut',
|
||||||
|
'onPaste',
|
||||||
|
'onCompositionEnd',
|
||||||
|
'onCompositionStart',
|
||||||
|
'onCompositionUpdate',
|
||||||
|
'onFocus',
|
||||||
|
'onBlur',
|
||||||
|
'onInput',
|
||||||
|
'onInvalid',
|
||||||
|
'onReset',
|
||||||
|
'onSubmit',
|
||||||
|
'onLoad',
|
||||||
|
'onError',
|
||||||
|
'onKeyDown',
|
||||||
|
'onKeyPress',
|
||||||
|
'onKeyUp',
|
||||||
|
'onAbort',
|
||||||
|
'onCanPlay',
|
||||||
|
'onCanPlayThrough',
|
||||||
|
'onDurationChange',
|
||||||
|
'onEmptied',
|
||||||
|
'onEncrypted',
|
||||||
|
'onEnded',
|
||||||
|
'onError',
|
||||||
|
'onLoadedData',
|
||||||
|
'onLoadedMetadata',
|
||||||
|
'onLoadStart',
|
||||||
|
'onPause',
|
||||||
|
'onPlay',
|
||||||
|
'onPlaying',
|
||||||
|
'onProgress',
|
||||||
|
'onRateChange',
|
||||||
|
'onSeeked',
|
||||||
|
'onSeeking',
|
||||||
|
'onStalled',
|
||||||
|
'onSuspend',
|
||||||
|
'onTimeUpdate',
|
||||||
|
'onVolumeChange',
|
||||||
|
'onWaiting',
|
||||||
|
'onClick',
|
||||||
|
'onContextMenu',
|
||||||
|
'onDoubleClick',
|
||||||
|
'onMouseDown',
|
||||||
|
'onMouseEnter',
|
||||||
|
'onMouseLeave',
|
||||||
|
'onMouseMove',
|
||||||
|
'onMouseOut',
|
||||||
|
'onMouseOver',
|
||||||
|
'onMouseUp',
|
||||||
|
'onDrag',
|
||||||
|
'onDragEnd',
|
||||||
|
'onDragEnter',
|
||||||
|
'onDragExit',
|
||||||
|
'onDragLeave',
|
||||||
|
'onDragOver',
|
||||||
|
'onDragStart',
|
||||||
|
'onDrop',
|
||||||
|
'onSelect',
|
||||||
|
'onTouchCancel',
|
||||||
|
'onTouchEnd',
|
||||||
|
'onTouchMove',
|
||||||
|
'onTouchStart',
|
||||||
|
'onPointerDown',
|
||||||
|
'onPointerMove',
|
||||||
|
'onPointerUp',
|
||||||
|
'onPointerCancel',
|
||||||
|
'onGotPointerCapture',
|
||||||
|
'onLostPointerCapture',
|
||||||
|
'onPointerEnter',
|
||||||
|
'onPointerLeave',
|
||||||
|
'onPointerOver',
|
||||||
|
'onPointerOut',
|
||||||
|
'onScroll',
|
||||||
|
'onWheel',
|
||||||
|
'onAnimationStart',
|
||||||
|
'onAnimationEnd',
|
||||||
|
'onAnimationIteration',
|
||||||
|
'onTransitionEnd',
|
||||||
|
'onChange',
|
||||||
|
'onToggle',
|
||||||
|
] = [
|
||||||
|
...clipboardEvents,
|
||||||
|
...compositionEvents,
|
||||||
|
...focusEvents,
|
||||||
|
...formEvents,
|
||||||
|
...imageEvents,
|
||||||
|
...keyboardEvents,
|
||||||
|
...mediaEvents,
|
||||||
|
...mouseEvents,
|
||||||
|
...dragEvents,
|
||||||
|
...selectionEvents,
|
||||||
|
...touchEvents,
|
||||||
|
...pointerEvents,
|
||||||
|
...uiEvents,
|
||||||
|
...wheelEvents,
|
||||||
|
...animationEvents,
|
||||||
|
...transitionEvents,
|
||||||
|
...changeEvents,
|
||||||
|
...otherEvents,
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
type AllEvents = (typeof allEvents)[number];
|
||||||
|
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: Impossible to type
|
||||||
|
type EventHandler<ArgsType> = (event: any, args: ArgsType) => void;
|
||||||
|
|
||||||
|
// Creates inferred type for event handler without args.
|
||||||
|
type EventHandlerWithoutArgs<ArgsType, OriginalEventHandler> = OriginalEventHandler extends (
|
||||||
|
event: infer Event,
|
||||||
|
args: ArgsType,
|
||||||
|
) => void
|
||||||
|
? (event: Event) => void
|
||||||
|
: never;
|
||||||
|
|
||||||
|
export type EventProps<ArgsType> = {
|
||||||
|
[K in AllEvents]?: EventHandler<ArgsType>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props<ArgsType> = Record<string, unknown> & EventProps<ArgsType>;
|
||||||
|
|
||||||
|
type EventPropsWithoutArgs<ArgsType, PropsType> = {
|
||||||
|
[K in keyof PropsType as K extends AllEvents ? K : never]: EventHandlerWithoutArgs<
|
||||||
|
ArgsType,
|
||||||
|
PropsType[K]
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type GetArgs<ArgsType> = (eventName: string) => ArgsType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an object with on-event callback props curried with provided args.
|
||||||
|
*
|
||||||
|
* @template ArgsType Type of arguments to curry on-event callbacks with.
|
||||||
|
* @param {PropsType} props Props passed to a component.
|
||||||
|
* @param {GetArgs<ArgsType>} [getArgs] A function that returns argument(s) on-event callbacks
|
||||||
|
* shall be curried with.
|
||||||
|
*/
|
||||||
|
export default function makeEventProps<
|
||||||
|
ArgsType,
|
||||||
|
PropsType extends Props<ArgsType> = Props<ArgsType>,
|
||||||
|
>(props: PropsType, getArgs?: GetArgs<ArgsType>): EventPropsWithoutArgs<ArgsType, PropsType> {
|
||||||
|
const eventProps: EventPropsWithoutArgs<ArgsType, PropsType> = {} as EventPropsWithoutArgs<
|
||||||
|
ArgsType,
|
||||||
|
PropsType
|
||||||
|
>;
|
||||||
|
|
||||||
|
for (const eventName of allEvents) {
|
||||||
|
type EventHandlerType = EventPropsWithoutArgs<ArgsType, PropsType>[typeof eventName];
|
||||||
|
|
||||||
|
const eventHandler = props[eventName];
|
||||||
|
|
||||||
|
if (!eventHandler) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getArgs) {
|
||||||
|
eventProps[eventName] = ((event) =>
|
||||||
|
eventHandler(event, getArgs(eventName))) as EventHandlerType;
|
||||||
|
} else {
|
||||||
|
eventProps[eventName] = eventHandler as EventHandlerType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return eventProps;
|
||||||
|
}
|
||||||
21
frontend/node_modules/merge-refs/LICENSE
generated
vendored
Normal file
21
frontend/node_modules/merge-refs/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017–2024 Wojciech Maj
|
||||||
|
|
||||||
|
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.
|
||||||
47
frontend/node_modules/merge-refs/README.md
generated
vendored
Normal file
47
frontend/node_modules/merge-refs/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
[](https://www.npmjs.com/package/merge-refs)  [](https://github.com/wojtekmaj/merge-refs/actions)
|
||||||
|
|
||||||
|
# Merge-Refs
|
||||||
|
|
||||||
|
A function that merges React refs into one. Filters out invalid (eg. falsy) refs as well and returns original ref if only one valid ref was given.
|
||||||
|
|
||||||
|
## tl;dr
|
||||||
|
|
||||||
|
- Install by executing `npm install merge-refs` or `yarn add merge-refs`.
|
||||||
|
- Import by adding `import mergeRefs from 'merge-refs'`.
|
||||||
|
- Use it in `ref` like so: `<div ref={mergeRefs(ref, someOtherRef)} />`
|
||||||
|
|
||||||
|
## Accepted refs
|
||||||
|
|
||||||
|
- Refs created using `createRef()`
|
||||||
|
- Refs created using `useRef()`
|
||||||
|
- Functional refs
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
function Hello() {
|
||||||
|
const ref1 = useRef<HTMLDivElement>(); // I'm going to be updated!
|
||||||
|
const ref2 = (element: HTMLDivElement) => {
|
||||||
|
// I'm going to be called!
|
||||||
|
};
|
||||||
|
|
||||||
|
return <div ref={mergeRefs(ref1, ref2)} />;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The MIT License.
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td >
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/5426427?v=4&s=128" width="64" height="64" alt="Wojciech Maj">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="https://github.com/wojtekmaj">Wojciech Maj</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
70
frontend/node_modules/merge-refs/package.json
generated
vendored
Normal file
70
frontend/node_modules/merge-refs/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
{
|
||||||
|
"name": "merge-refs",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"description": "A function that merges React refs into one.",
|
||||||
|
"type": "module",
|
||||||
|
"sideEffects": false,
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"source": "./src/index.ts",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"exports": {
|
||||||
|
".": "./dist/index.js",
|
||||||
|
"./*": "./*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc --project tsconfig.build.json",
|
||||||
|
"clean": "rimraf dist",
|
||||||
|
"format": "biome format",
|
||||||
|
"lint": "biome lint",
|
||||||
|
"prepack": "yarn clean && yarn build",
|
||||||
|
"test": "yarn lint && yarn tsc && yarn format && yarn unit",
|
||||||
|
"tsc": "tsc",
|
||||||
|
"unit": "vitest"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"react",
|
||||||
|
"react ref",
|
||||||
|
"react refs",
|
||||||
|
"merge"
|
||||||
|
],
|
||||||
|
"author": {
|
||||||
|
"name": "Wojciech Maj",
|
||||||
|
"email": "kontakt@wojtekmaj.pl"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@biomejs/biome": "1.9.0",
|
||||||
|
"@testing-library/dom": "^10.0.0",
|
||||||
|
"@testing-library/react": "^16.0.0",
|
||||||
|
"@types/react": "*",
|
||||||
|
"happy-dom": "^15.10.2",
|
||||||
|
"husky": "^9.0.0",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"rimraf": "^6.0.0",
|
||||||
|
"typescript": "^5.5.2",
|
||||||
|
"vitest": "^3.0.5"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public",
|
||||||
|
"provenance": true
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"src"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/wojtekmaj/merge-refs.git"
|
||||||
|
},
|
||||||
|
"funding": "https://github.com/wojtekmaj/merge-refs?sponsor=1",
|
||||||
|
"packageManager": "yarn@4.3.1"
|
||||||
|
}
|
||||||
68
frontend/node_modules/merge-refs/src/index.spec.tsx
generated
vendored
Normal file
68
frontend/node_modules/merge-refs/src/index.spec.tsx
generated
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
import { describe, expect, it, vi } from 'vitest';
|
||||||
|
import { createRef } from 'react';
|
||||||
|
import { render } from '@testing-library/react';
|
||||||
|
|
||||||
|
import mergeRefs from './index.js';
|
||||||
|
|
||||||
|
describe('mergeRefs()', () => {
|
||||||
|
it('returns falsy result given no arguments', () => {
|
||||||
|
const result = mergeRefs();
|
||||||
|
|
||||||
|
expect(result).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns falsy result given falsy arguments', () => {
|
||||||
|
const result = mergeRefs(null, null);
|
||||||
|
|
||||||
|
expect(result).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns original ref given only one ref', () => {
|
||||||
|
const ref = vi.fn();
|
||||||
|
|
||||||
|
const result = mergeRefs(ref);
|
||||||
|
|
||||||
|
expect(result).toBe(ref);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns original ref given one ref and one falsy argument', () => {
|
||||||
|
const ref = vi.fn();
|
||||||
|
|
||||||
|
const result = mergeRefs(ref, null);
|
||||||
|
|
||||||
|
expect(result).toBe(ref);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns merged refs properly', () => {
|
||||||
|
const ref1 = vi.fn();
|
||||||
|
const ref2 = createRef<HTMLDivElement>();
|
||||||
|
|
||||||
|
const result = mergeRefs(ref1, ref2);
|
||||||
|
|
||||||
|
expect(result).not.toBe(ref1);
|
||||||
|
expect(result).toEqual(expect.any(Function));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles merged functional refs properly', () => {
|
||||||
|
const ref1 = vi.fn();
|
||||||
|
const ref2 = createRef<HTMLDivElement>();
|
||||||
|
|
||||||
|
const mergedRef = mergeRefs(ref1, ref2);
|
||||||
|
|
||||||
|
const { container } = render(<div ref={mergedRef} />);
|
||||||
|
|
||||||
|
expect(ref1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(ref1).toHaveBeenCalledWith(container.firstElementChild);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles merged object refs properly', () => {
|
||||||
|
const ref1 = createRef<HTMLDivElement>();
|
||||||
|
const ref2 = vi.fn();
|
||||||
|
|
||||||
|
const mergedRef = mergeRefs(ref1, ref2);
|
||||||
|
|
||||||
|
const { container } = render(<div ref={mergedRef} />);
|
||||||
|
|
||||||
|
expect(ref1.current).toBe(container.firstElementChild);
|
||||||
|
});
|
||||||
|
});
|
||||||
35
frontend/node_modules/merge-refs/src/index.ts
generated
vendored
Normal file
35
frontend/node_modules/merge-refs/src/index.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import type * as React from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function that merges React refs into one.
|
||||||
|
* Supports both functions and ref objects created using createRef() and useRef().
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* ```tsx
|
||||||
|
* <div ref={mergeRefs(ref1, ref2, ref3)} />
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param {(React.Ref<T> | undefined)[]} inputRefs Array of refs
|
||||||
|
* @returns {React.Ref<T> | React.RefCallback<T>} Merged refs
|
||||||
|
*/
|
||||||
|
export default function mergeRefs<T>(
|
||||||
|
...inputRefs: (React.Ref<T> | undefined)[]
|
||||||
|
): React.Ref<T> | React.RefCallback<T> {
|
||||||
|
const filteredInputRefs = inputRefs.filter(Boolean);
|
||||||
|
|
||||||
|
if (filteredInputRefs.length <= 1) {
|
||||||
|
const firstRef = filteredInputRefs[0];
|
||||||
|
|
||||||
|
return firstRef || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return function mergedRefs(ref) {
|
||||||
|
for (const inputRef of filteredInputRefs) {
|
||||||
|
if (typeof inputRef === 'function') {
|
||||||
|
inputRef(ref);
|
||||||
|
} else if (inputRef) {
|
||||||
|
(inputRef as React.MutableRefObject<T | null>).current = ref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
15
frontend/node_modules/pdfjs-dist/CODE_OF_CONDUCT.md
generated
vendored
Normal file
15
frontend/node_modules/pdfjs-dist/CODE_OF_CONDUCT.md
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Community Participation Guidelines
|
||||||
|
|
||||||
|
This repository is governed by Mozilla's code of conduct and etiquette guidelines.
|
||||||
|
For more details, please read the
|
||||||
|
[Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/).
|
||||||
|
|
||||||
|
## How to Report
|
||||||
|
For more information on how to report violations of the Community Participation Guidelines, please read our '[How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)' page.
|
||||||
|
|
||||||
|
<!--
|
||||||
|
## Project Specific Etiquette
|
||||||
|
|
||||||
|
In some cases, there will be additional project etiquette i.e.: (https://bugzilla.mozilla.org/page.cgi?id=etiquette.html).
|
||||||
|
Please update for your project.
|
||||||
|
-->
|
||||||
177
frontend/node_modules/pdfjs-dist/LICENSE
generated
vendored
Normal file
177
frontend/node_modules/pdfjs-dist/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,177 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
14
frontend/node_modules/pdfjs-dist/README.md
generated
vendored
Normal file
14
frontend/node_modules/pdfjs-dist/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# PDF.js
|
||||||
|
|
||||||
|
PDF.js is a Portable Document Format (PDF) library that is built with HTML5.
|
||||||
|
Our goal is to create a general-purpose, web standards-based platform for
|
||||||
|
parsing and rendering PDFs.
|
||||||
|
|
||||||
|
This is a pre-built version of the PDF.js source code. It is automatically
|
||||||
|
generated by the build scripts.
|
||||||
|
|
||||||
|
For usage with older browsers/environments, without native support for the
|
||||||
|
latest JavaScript features, please see the `legacy/` folder.
|
||||||
|
Please see [this wiki page](https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions#faq-support) for information about supported browsers/environments.
|
||||||
|
|
||||||
|
See https://github.com/mozilla/pdf.js for learning and contributing.
|
||||||
BIN
frontend/node_modules/pdfjs-dist/cmaps/78-EUC-H.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/78-EUC-H.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/78-EUC-V.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/78-EUC-V.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/78-H.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/78-H.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/78-RKSJ-H.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/78-RKSJ-H.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/78-RKSJ-V.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/78-RKSJ-V.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/78-V.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/78-V.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/78ms-RKSJ-H.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/78ms-RKSJ-H.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/78ms-RKSJ-V.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/78ms-RKSJ-V.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/83pv-RKSJ-H.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/83pv-RKSJ-H.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/90ms-RKSJ-H.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/90ms-RKSJ-H.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/90ms-RKSJ-V.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/90ms-RKSJ-V.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/90msp-RKSJ-H.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/90msp-RKSJ-H.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/90msp-RKSJ-V.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/90msp-RKSJ-V.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/90pv-RKSJ-H.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/90pv-RKSJ-H.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/90pv-RKSJ-V.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/90pv-RKSJ-V.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/Add-H.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/Add-H.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/Add-RKSJ-H.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/Add-RKSJ-H.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/Add-RKSJ-V.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/Add-RKSJ-V.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/Add-V.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/Add-V.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-CNS1-0.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-CNS1-0.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-CNS1-1.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-CNS1-1.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-CNS1-2.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-CNS1-2.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-CNS1-3.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-CNS1-3.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-CNS1-4.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-CNS1-4.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-CNS1-5.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-CNS1-5.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-CNS1-6.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-CNS1-6.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-CNS1-UCS2.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-CNS1-UCS2.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-GB1-0.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-GB1-0.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-GB1-1.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-GB1-1.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-GB1-2.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-GB1-2.bcmap
generated
vendored
Normal file
Binary file not shown.
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-GB1-3.bcmap
generated
vendored
Normal file
BIN
frontend/node_modules/pdfjs-dist/cmaps/Adobe-GB1-3.bcmap
generated
vendored
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue