From f9eb67da112c37d1c1e5a13c04b3ec8bdb9dbf99 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Mon, 26 Feb 2024 00:06:04 +0100 Subject: [PATCH 001/818] Direct dependency specification in go.mod --- backend/go.mod | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/go.mod b/backend/go.mod index 57c5c73..8c06f45 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -5,6 +5,5 @@ go 1.21.1 require ( github.com/jmoiron/sqlx v1.3.5 github.com/mattn/go-sqlite3 v1.14.22 + github.com/BurntSushi/toml v1.3.2 ) - -require github.com/BurntSushi/toml v1.3.2 // indirect From 3ba446be5b535beb308879beb417583e62624fb0 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Mon, 26 Feb 2024 00:12:13 +0100 Subject: [PATCH 002/818] Splitting migration scripts --- backend/migrations/0010.sql | 50 ------------------- backend/migrations/0010_users.sql | 5 ++ backend/migrations/0020_projects.sql | 7 +++ backend/migrations/0030_time_reports.sql | 18 +++++++ .../0040_time_report_collections.sql | 7 +++ backend/migrations/0050_user_roles.sql | 7 +++ 6 files changed, 44 insertions(+), 50 deletions(-) delete mode 100644 backend/migrations/0010.sql create mode 100644 backend/migrations/0010_users.sql create mode 100644 backend/migrations/0020_projects.sql create mode 100644 backend/migrations/0030_time_reports.sql create mode 100644 backend/migrations/0040_time_report_collections.sql create mode 100644 backend/migrations/0050_user_roles.sql diff --git a/backend/migrations/0010.sql b/backend/migrations/0010.sql deleted file mode 100644 index df314b1..0000000 --- a/backend/migrations/0010.sql +++ /dev/null @@ -1,50 +0,0 @@ -PRAGMA foreign_keys = ON; - -CREATE TABLE IF NOT EXISTS users ( - id INTEGER PRIMARY KEY, - username VARCHAR(255) NOT NULL, - password VARCHAR(255) NOT NULL -); - -CREATE TABLE IF NOT EXISTS projects ( - id INTEGER PRIMARY KEY, - name VARCHAR(255) NOT NULL, - description TEXT NOT NULL, - user_id INTEGER NOT NULL, - FOREIGN KEY (user_id) REFERENCES users (id) -); - -CREATE TABLE IF NOT EXISTS time_reports ( - id INTEGER PRIMARY KEY, - project_id INTEGER NOT NULL, - start DATETIME NOT NULL, - end DATETIME NOT NULL, - FOREIGN KEY (project_id) REFERENCES projects (id) -); - -CREATE TRIGGER IF NOT EXISTS time_reports_start_before_end - BEFORE INSERT ON time_reports - FOR EACH ROW - BEGIN - SELECT - CASE - WHEN NEW.start >= NEW.end THEN - RAISE (ABORT, 'start must be before end') - END; - END; - -CREATE TABLE IF NOT EXISTS report_collection ( - id INTEGER PRIMARY KEY, - project_id INTEGER NOT NULL, - date DATE NOT NULL, - signed_by INTEGER, -- NULL if not signed - FOREIGN KEY (signed_by) REFERENCES users (id) -); - -CREATE TABLE IF NOT EXISTS user_roles ( - user_id INTEGER NOT NULL, - project_id INTEGER NOT NULL, - role STRING NOT NULL, - FOREIGN KEY (user_id) REFERENCES users (id) - FOREIGN KEY (project_id) REFERENCES projects (id) -); \ No newline at end of file diff --git a/backend/migrations/0010_users.sql b/backend/migrations/0010_users.sql new file mode 100644 index 0000000..d36847e --- /dev/null +++ b/backend/migrations/0010_users.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY, + username VARCHAR(255) NOT NULL, + password VARCHAR(255) NOT NULL +); \ No newline at end of file diff --git a/backend/migrations/0020_projects.sql b/backend/migrations/0020_projects.sql new file mode 100644 index 0000000..293aea5 --- /dev/null +++ b/backend/migrations/0020_projects.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS projects ( + id INTEGER PRIMARY KEY, + name VARCHAR(255) NOT NULL, + description TEXT NOT NULL, + user_id INTEGER NOT NULL, + FOREIGN KEY (user_id) REFERENCES users (id) +); \ No newline at end of file diff --git a/backend/migrations/0030_time_reports.sql b/backend/migrations/0030_time_reports.sql new file mode 100644 index 0000000..b860a35 --- /dev/null +++ b/backend/migrations/0030_time_reports.sql @@ -0,0 +1,18 @@ +CREATE TABLE IF NOT EXISTS time_reports ( + id INTEGER PRIMARY KEY, + project_id INTEGER NOT NULL, + start DATETIME NOT NULL, + end DATETIME NOT NULL, + FOREIGN KEY (project_id) REFERENCES projects (id) +); + +CREATE TRIGGER IF NOT EXISTS time_reports_start_before_end + BEFORE INSERT ON time_reports + FOR EACH ROW + BEGIN + SELECT + CASE + WHEN NEW.start >= NEW.end THEN + RAISE (ABORT, 'start must be before end') + END; + END; \ No newline at end of file diff --git a/backend/migrations/0040_time_report_collections.sql b/backend/migrations/0040_time_report_collections.sql new file mode 100644 index 0000000..054593b --- /dev/null +++ b/backend/migrations/0040_time_report_collections.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS user_roles ( + user_id INTEGER NOT NULL, + project_id INTEGER NOT NULL, + role STRING NOT NULL, + FOREIGN KEY (user_id) REFERENCES users (id) + FOREIGN KEY (project_id) REFERENCES projects (id) +); \ No newline at end of file diff --git a/backend/migrations/0050_user_roles.sql b/backend/migrations/0050_user_roles.sql new file mode 100644 index 0000000..054593b --- /dev/null +++ b/backend/migrations/0050_user_roles.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS user_roles ( + user_id INTEGER NOT NULL, + project_id INTEGER NOT NULL, + role STRING NOT NULL, + FOREIGN KEY (user_id) REFERENCES users (id) + FOREIGN KEY (project_id) REFERENCES projects (id) +); \ No newline at end of file From 033402ffaf2d99652edd253ccf80fbcf2d813afb Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Mon, 26 Feb 2024 00:13:39 +0100 Subject: [PATCH 003/818] Mistake in migration scripts --- backend/migrations/0040_time_report_collections.sql | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/migrations/0040_time_report_collections.sql b/backend/migrations/0040_time_report_collections.sql index 054593b..7dbe6bd 100644 --- a/backend/migrations/0040_time_report_collections.sql +++ b/backend/migrations/0040_time_report_collections.sql @@ -1,7 +1,7 @@ -CREATE TABLE IF NOT EXISTS user_roles ( - user_id INTEGER NOT NULL, +CREATE TABLE IF NOT EXISTS report_collection ( + id INTEGER PRIMARY KEY, project_id INTEGER NOT NULL, - role STRING NOT NULL, - FOREIGN KEY (user_id) REFERENCES users (id) - FOREIGN KEY (project_id) REFERENCES projects (id) + date DATE NOT NULL, + signed_by INTEGER, -- NULL if not signed + FOREIGN KEY (signed_by) REFERENCES users (id) ); \ No newline at end of file From 296ed987d8067b9f1914d444c46b43af6529204f Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 27 Feb 2024 04:56:12 +0100 Subject: [PATCH 004/818] Make tests verbose and disable testing cache --- backend/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/Makefile b/backend/Makefile index 0482c03..c76e28b 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -30,7 +30,7 @@ clean: # Test target test: - $(GOTEST) ./... + $(GOTEST) ./... -count=1 -v # Get dependencies target deps: @@ -50,4 +50,4 @@ migrate: done # Default target -default: build \ No newline at end of file +default: build From 5be29d86af6db323c3aea129e68c1cc943982112 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 27 Feb 2024 04:59:24 +0100 Subject: [PATCH 005/818] Makefile changes so that test depends on db.sqlite3 --- backend/Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/backend/Makefile b/backend/Makefile index c76e28b..c417e73 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -29,7 +29,7 @@ clean: rm -f db.sqlite3 # Test target -test: +test: db.sqlite3 $(GOTEST) ./... -count=1 -v # Get dependencies target @@ -49,5 +49,9 @@ migrate: sqlite3 $(DB_FILE) < $$file; \ done +# Target added primarily for CI/CD to ensure that the database is created before running tests +db.sqlite3: + make migrate + # Default target default: build From 06076f93b7500dcd000d8660f90f9bd9ce3729e0 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 27 Feb 2024 05:00:04 +0100 Subject: [PATCH 006/818] Database interactions demo --- backend/internal/database/db.go | 36 ++++++++++++++++++++++++---- backend/internal/database/db_test.go | 16 +++++++++++++ 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index d99efde..48ea95c 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -7,26 +7,54 @@ import ( _ "github.com/mattn/go-sqlite3" ) -func DbConnect() *sqlx.DB { +// This struct is a wrapper type that holds the database connection +// Internally DB holds a connection pool, so it's safe for concurrent use +type Db struct { + *sqlx.DB +} + +const userInsert = "INSERT INTO users (username, password) VALUES (?, ?)" + +// DbConnect connects to the database +func DbConnect() *Db { // Check for the environment variable dbpath := os.Getenv("SQLITE_DB_PATH") // Default to something reasonable if dbpath == "" { - dbpath = "./db.sqlite3" + // This should obviously not be like this + dbpath = "../../db.sqlite3" // This is disaster waiting to happen + // WARNING + + // If the file doesn't exist, panic + if _, err := os.Stat(dbpath); os.IsNotExist(err) { + panic("Database file does not exist: " + dbpath) + } } // Open the database - // db, err := sqlx.Connect("sqlite3", ":memory:") db, err := sqlx.Connect("sqlite3", dbpath) if err != nil { panic(err) } + // Ping forces the connection to be established err = db.Ping() if err != nil { panic(err) } - return db + return &Db{db} +} + +// AddUser adds a user to the database +func (d *Db) AddUser(username string, password string) error { + _, err := d.Exec(userInsert, username, password) + return err +} + +// Removes a user from the database +func (d *Db) RemoveUser(username string) error { + _, err := d.Exec("DELETE FROM users WHERE username = ?", username) + return err } diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index d7b26c9..6027ad1 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -8,3 +8,19 @@ func TestDbConnect(t *testing.T) { db := DbConnect() _ = db } + +func TestDbAddUser(t *testing.T) { + db := DbConnect() + err := db.AddUser("test", "password") + if err != nil { + t.Error("AddUser failed:", err) + } +} + +func TestDbRemoveUser(t *testing.T) { + db := DbConnect() + err := db.RemoveUser("test") + if err != nil { + t.Error("RemoveUser failed:", err) + } +} From 60e7b73f660192246285cf0a21f3b5aa8513591b Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 27 Feb 2024 05:50:28 +0100 Subject: [PATCH 007/818] Unique username constraint on users table --- backend/migrations/0010_users.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/migrations/0010_users.sql b/backend/migrations/0010_users.sql index d36847e..75c286c 100644 --- a/backend/migrations/0010_users.sql +++ b/backend/migrations/0010_users.sql @@ -1,5 +1,5 @@ CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY, - username VARCHAR(255) NOT NULL, + username VARCHAR(255) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL ); \ No newline at end of file From ce1ce89b000f2d991e038f5df0e4696b1936d04c Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 27 Feb 2024 05:51:16 +0100 Subject: [PATCH 008/818] Some more example database interface code --- backend/internal/database/db.go | 15 +++++++++++++++ backend/internal/database/db_test.go | 17 +++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 48ea95c..335a4ea 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -58,3 +58,18 @@ func (d *Db) RemoveUser(username string) error { _, err := d.Exec("DELETE FROM users WHERE username = ?", username) return err } + +func (d *Db) GetUserId(username string) (int, error) { + var id int + err := d.Get(&id, "SELECT id FROM users WHERE username = ?", username) + return id, err +} + +func (d *Db) AddProject(name string, description string, username string) error { + userId, err := d.GetUserId(username) + if err != nil { + return err + } + _, err = d.Exec("INSERT INTO projects (name, description, user_id) VALUES (?, ?, ?)", name, description, userId) + return err +} diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index 6027ad1..bad7dac 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -4,6 +4,9 @@ import ( "testing" ) +// Tests are not guaranteed to be sequential +// Writing tests like this will bite you, eventually + func TestDbConnect(t *testing.T) { db := DbConnect() _ = db @@ -17,6 +20,20 @@ func TestDbAddUser(t *testing.T) { } } +func TestDbGetUserId(t *testing.T) { + db := DbConnect() + + var id int + + id, err := db.GetUserId("test") + if err != nil { + t.Error("GetUserId failed:", err) + } + if id != 1 { + t.Error("GetUserId failed: expected 1, got", id) + } +} + func TestDbRemoveUser(t *testing.T) { db := DbConnect() err := db.RemoveUser("test") From 6e48c0a0888448f901fd3261e04a52876b7e4541 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 27 Feb 2024 06:09:09 +0100 Subject: [PATCH 009/818] Database migration readme --- backend/migrations/README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 backend/migrations/README.md diff --git a/backend/migrations/README.md b/backend/migrations/README.md new file mode 100644 index 0000000..18b5e34 --- /dev/null +++ b/backend/migrations/README.md @@ -0,0 +1,14 @@ +# Database migrations + +This directory contains all the database migrations for the backend. + +[!WARNING] +Keep in mind that these migrations are **not yet stable**. + +## Running migrations + +In the root of the backend directory, run: + +```bash +make migrate +``` From 6c82aa2047c58ae6daa2b507a7497639e50da32b Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 27 Feb 2024 07:43:20 +0100 Subject: [PATCH 010/818] Backup target for makefile --- backend/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/Makefile b/backend/Makefile index c417e73..6318212 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -53,5 +53,10 @@ migrate: db.sqlite3: make migrate +backup: + sqlite3 $(DB_FILE) .dump | gzip -9 > BACKUP_$(DB_FILE)_$(shell date +"%Y-%m-%d_%H:%M:%S").sql.gz + # Restore with: + # gzip -cd BACKUP_$(DB_FILE)_*.sql.gz | sqlite3 $(DB_FILE) + # Default target default: build From 6f51151d64371c374c9deb54198c0cb881b5c7c9 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 27 Feb 2024 07:45:38 +0100 Subject: [PATCH 011/818] Save backups in a backup directory --- .gitignore | 3 +-- backend/Makefile | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 063a7e7..1d09217 100644 --- a/.gitignore +++ b/.gitignore @@ -48,5 +48,4 @@ dist/ *.7z *.bak - - +backend/backups diff --git a/backend/Makefile b/backend/Makefile index 6318212..db8094c 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -54,9 +54,10 @@ db.sqlite3: make migrate backup: - sqlite3 $(DB_FILE) .dump | gzip -9 > BACKUP_$(DB_FILE)_$(shell date +"%Y-%m-%d_%H:%M:%S").sql.gz + mkdir -p backups + sqlite3 $(DB_FILE) .dump | gzip -9 > ./backups/BACKUP_$(DB_FILE)_$(shell date +"%Y-%m-%d_%H:%M:%S").sql.gz # Restore with: - # gzip -cd BACKUP_$(DB_FILE)_*.sql.gz | sqlite3 $(DB_FILE) + # gzip -cd BACKUP_FILE.sql.gz | sqlite3 $(DB_FILE) # Default target default: build From 229ebc3a088a2a8c41879f5b7cc08cd663ec883f Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 27 Feb 2024 07:59:42 +0100 Subject: [PATCH 012/818] AddProject database interface with tests --- backend/internal/database/db.go | 8 +++----- backend/internal/database/db_test.go | 8 ++++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 335a4ea..e0006d1 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -14,6 +14,7 @@ type Db struct { } const userInsert = "INSERT INTO users (username, password) VALUES (?, ?)" +const projectInsert = "INSERT INTO projects (name, description, user_id) SELECT ?, ?, id FROM users WHERE username = ?" // DbConnect connects to the database func DbConnect() *Db { @@ -65,11 +66,8 @@ func (d *Db) GetUserId(username string) (int, error) { return id, err } +// Creates a new project in the database, associated with a user func (d *Db) AddProject(name string, description string, username string) error { - userId, err := d.GetUserId(username) - if err != nil { - return err - } - _, err = d.Exec("INSERT INTO projects (name, description, user_id) VALUES (?, ?, ?)", name, description, userId) + _, err := d.Exec(projectInsert, name, description, username) return err } diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index bad7dac..ab0355a 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -34,6 +34,14 @@ func TestDbGetUserId(t *testing.T) { } } +func TestDbAddProject(t *testing.T) { + db := DbConnect() + err := db.AddProject("test", "description", "test") + if err != nil { + t.Error("AddProject failed:", err) + } +} + func TestDbRemoveUser(t *testing.T) { db := DbConnect() err := db.RemoveUser("test") From 9d6cddb7245e6351b41332c63943fa0470fca4a1 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 27 Feb 2024 08:05:07 +0100 Subject: [PATCH 013/818] Various breaking changes to the database schema --- backend/migrations/0010_users.sql | 6 +++++- backend/migrations/0020_projects.sql | 8 ++++++-- backend/migrations/0030_time_reports.sql | 3 ++- backend/migrations/0040_time_report_collections.sql | 2 ++ backend/migrations/0050_user_roles.sql | 2 +- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/backend/migrations/0010_users.sql b/backend/migrations/0010_users.sql index 75c286c..7cb23fd 100644 --- a/backend/migrations/0010_users.sql +++ b/backend/migrations/0010_users.sql @@ -1,5 +1,9 @@ CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY, + userId TEXT DEFAULT (HEX(RANDOMBLOB(4))) NOT NULL UNIQUE, username VARCHAR(255) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL -); \ No newline at end of file +); + +CREATE INDEX IF NOT EXISTS users_username_index ON users (username); +CREATE INDEX IF NOT EXISTS users_userId_index ON users (userId); \ No newline at end of file diff --git a/backend/migrations/0020_projects.sql b/backend/migrations/0020_projects.sql index 293aea5..8592e75 100644 --- a/backend/migrations/0020_projects.sql +++ b/backend/migrations/0020_projects.sql @@ -1,7 +1,11 @@ CREATE TABLE IF NOT EXISTS projects ( id INTEGER PRIMARY KEY, - name VARCHAR(255) NOT NULL, + projectId TEXT DEFAULT (HEX(RANDOMBLOB(4))) NOT NULL UNIQUE, + name VARCHAR(255) NOT NULL UNIQUE, description TEXT NOT NULL, user_id INTEGER NOT NULL, FOREIGN KEY (user_id) REFERENCES users (id) -); \ No newline at end of file +); + +CREATE INDEX IF NOT EXISTS projects_projectId_index ON projects (projectId); +CREATE INDEX IF NOT EXISTS projects_user_id_index ON projects (user_id); \ No newline at end of file diff --git a/backend/migrations/0030_time_reports.sql b/backend/migrations/0030_time_reports.sql index b860a35..e8f3ec1 100644 --- a/backend/migrations/0030_time_reports.sql +++ b/backend/migrations/0030_time_reports.sql @@ -1,9 +1,10 @@ CREATE TABLE IF NOT EXISTS time_reports ( id INTEGER PRIMARY KEY, + reportId TEXT DEFAULT (HEX(RANDOMBLOB(6))) NOT NULL UNIQUE, project_id INTEGER NOT NULL, start DATETIME NOT NULL, end DATETIME NOT NULL, - FOREIGN KEY (project_id) REFERENCES projects (id) + FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE ); CREATE TRIGGER IF NOT EXISTS time_reports_start_before_end diff --git a/backend/migrations/0040_time_report_collections.sql b/backend/migrations/0040_time_report_collections.sql index 7dbe6bd..be406ff 100644 --- a/backend/migrations/0040_time_report_collections.sql +++ b/backend/migrations/0040_time_report_collections.sql @@ -1,7 +1,9 @@ CREATE TABLE IF NOT EXISTS report_collection ( id INTEGER PRIMARY KEY, + owner_id INTEGER NOT NULL, project_id INTEGER NOT NULL, date DATE NOT NULL, signed_by INTEGER, -- NULL if not signed + FOREIGN KEY (owner_id) REFERENCES users (id) FOREIGN KEY (signed_by) REFERENCES users (id) ); \ No newline at end of file diff --git a/backend/migrations/0050_user_roles.sql b/backend/migrations/0050_user_roles.sql index 054593b..32e03dc 100644 --- a/backend/migrations/0050_user_roles.sql +++ b/backend/migrations/0050_user_roles.sql @@ -1,7 +1,7 @@ CREATE TABLE IF NOT EXISTS user_roles ( user_id INTEGER NOT NULL, project_id INTEGER NOT NULL, - role STRING NOT NULL, + role STRING NOT NULL, -- 'admin' or 'member' FOREIGN KEY (user_id) REFERENCES users (id) FOREIGN KEY (project_id) REFERENCES projects (id) ); \ No newline at end of file From 06632c16da7bae37ef4bd79ce5e92ecd4fddee52 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 27 Feb 2024 23:11:27 +0100 Subject: [PATCH 014/818] Better interface for writeconfig --- backend/internal/config/config.go | 2 +- backend/internal/config/config_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go index aec7512..5bebc34 100644 --- a/backend/internal/config/config.go +++ b/backend/internal/config/config.go @@ -22,7 +22,7 @@ type Config struct { } // WriteConfigToFile writes a Config to a file -func WriteConfigToFile(c *Config, filename string) error { +func (c *Config) WriteConfigToFile(filename string) error { f, err := os.Create(filename) if err != nil { return err diff --git a/backend/internal/config/config_test.go b/backend/internal/config/config_test.go index cc462ff..cb02a31 100644 --- a/backend/internal/config/config_test.go +++ b/backend/internal/config/config_test.go @@ -26,7 +26,7 @@ func TestNewConfig(t *testing.T) { func TestWriteConfig(t *testing.T) { c := NewConfig() - err := WriteConfigToFile(c, "test.toml") + err := c.WriteConfigToFile("test.toml") if err != nil { t.Errorf("Expected no error, got %s", err) } @@ -37,7 +37,7 @@ func TestWriteConfig(t *testing.T) { func TestReadConfig(t *testing.T) { c := NewConfig() - err := WriteConfigToFile(c, "test.toml") + err := c.WriteConfigToFile("test.toml") if err != nil { t.Errorf("Expected no error, got %s", err) } From e1cd596c13d33a0ac99c079e9678d9653724e82b Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 28 Feb 2024 03:21:13 +0100 Subject: [PATCH 015/818] Config parsing and database api changes --- backend/cmd/main.go | 13 +++++-- backend/internal/database/db.go | 53 +++++++++++++++++++--------- backend/internal/database/db_test.go | 43 ++++++++++++++++------ 3 files changed, 81 insertions(+), 28 deletions(-) diff --git a/backend/cmd/main.go b/backend/cmd/main.go index 699ebf5..bb71e31 100644 --- a/backend/cmd/main.go +++ b/backend/cmd/main.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "net/http" + "ttime/internal/config" "ttime/internal/database" _ "github.com/mattn/go-sqlite3" @@ -40,7 +41,15 @@ func handler(w http.ResponseWriter, r *http.Request) { } func main() { - database.DbConnect() + conf, err := config.ReadConfigFromFile("config.toml") + if err != nil { + conf = config.NewConfig() + conf.WriteConfigToFile("config.toml") + } + + println(conf) + + database.DbConnect("db.sqlite3") b := &ButtonState{PressCount: 0} // Mounting the handlers @@ -54,7 +63,7 @@ func main() { println("Visit http://localhost:8080/hello to see the hello handler in action") println("Visit http://localhost:8080/button to see the button handler in action") println("Press Ctrl+C to stop the server") - err := http.ListenAndServe(":8080", nil) + err = http.ListenAndServe(":8080", nil) if err != nil { panic(err) } diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index e0006d1..0334cc4 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -1,7 +1,9 @@ package database import ( + "log" "os" + "path/filepath" "github.com/jmoiron/sqlx" _ "github.com/mattn/go-sqlite3" @@ -17,22 +19,7 @@ const userInsert = "INSERT INTO users (username, password) VALUES (?, ?)" const projectInsert = "INSERT INTO projects (name, description, user_id) SELECT ?, ?, id FROM users WHERE username = ?" // DbConnect connects to the database -func DbConnect() *Db { - // Check for the environment variable - dbpath := os.Getenv("SQLITE_DB_PATH") - - // Default to something reasonable - if dbpath == "" { - // This should obviously not be like this - dbpath = "../../db.sqlite3" // This is disaster waiting to happen - // WARNING - - // If the file doesn't exist, panic - if _, err := os.Stat(dbpath); os.IsNotExist(err) { - panic("Database file does not exist: " + dbpath) - } - } - +func DbConnect(dbpath string) *Db { // Open the database db, err := sqlx.Connect("sqlite3", dbpath) if err != nil { @@ -71,3 +58,37 @@ func (d *Db) AddProject(name string, description string, username string) error _, err := d.Exec(projectInsert, name, description, username) return err } + +// Reads a directory of migration files and applies them to the database. +// This will eventually be used on an embedded directory +func (d *Db) Migrate(dirname string) error { + files, err := os.ReadDir(dirname) + if err != nil { + return err + } + + tr := d.MustBegin() + + // Iterate over each SQL file and execute it + for _, file := range files { + if file.IsDir() || filepath.Ext(file.Name()) != ".sql" { + continue + } + + sqlFile := filepath.Join(dirname, file.Name()) + sqlBytes, err := os.ReadFile(sqlFile) + if err != nil { + return err + } + + sqlQuery := string(sqlBytes) + _, err = tr.Exec(sqlQuery) + if err != nil { + return err + } + log.Println("Executed SQL file:", file.Name()) + } + + tr.Commit() + return nil +} diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index ab0355a..62e47db 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -5,27 +5,42 @@ import ( ) // Tests are not guaranteed to be sequential -// Writing tests like this will bite you, eventually + +func setupState() (*Db, error) { + db := DbConnect(":memory:") + err := db.Migrate("../../migrations") + if err != nil { + return nil, err + } + return db, nil +} func TestDbConnect(t *testing.T) { - db := DbConnect() + db := DbConnect(":memory:") _ = db } func TestDbAddUser(t *testing.T) { - db := DbConnect() - err := db.AddUser("test", "password") + db, err := setupState() + if err != nil { + t.Error("setupState failed:", err) + } + err = db.AddUser("test", "password") if err != nil { t.Error("AddUser failed:", err) } } func TestDbGetUserId(t *testing.T) { - db := DbConnect() + db, err := setupState() + if err != nil { + t.Error("setupState failed:", err) + } + db.AddUser("test", "password") var id int - id, err := db.GetUserId("test") + id, err = db.GetUserId("test") if err != nil { t.Error("GetUserId failed:", err) } @@ -35,16 +50,24 @@ func TestDbGetUserId(t *testing.T) { } func TestDbAddProject(t *testing.T) { - db := DbConnect() - err := db.AddProject("test", "description", "test") + db, err := setupState() + if err != nil { + t.Error("setupState failed:", err) + } + + err = db.AddProject("test", "description", "test") if err != nil { t.Error("AddProject failed:", err) } } func TestDbRemoveUser(t *testing.T) { - db := DbConnect() - err := db.RemoveUser("test") + db, err := setupState() + if err != nil { + t.Error("setupState failed:", err) + } + + err = db.RemoveUser("test") if err != nil { t.Error("RemoveUser failed:", err) } From 77e3324f79297842207b71be40eaad32c1f90de6 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 28 Feb 2024 03:21:33 +0100 Subject: [PATCH 016/818] Gitignore the config file --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 1d09217..9ad11ec 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,5 @@ dist/ *.bak backend/backups + +config.toml From 34c071282511f82d1c053647c753f32a9d1704f6 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 28 Feb 2024 03:30:05 +0100 Subject: [PATCH 017/818] Moving the migrations directory into database for embedding --- backend/internal/database/db.go | 10 ++++++++-- .../{ => internal/database}/migrations/0010_users.sql | 0 .../database}/migrations/0020_projects.sql | 0 .../database}/migrations/0030_time_reports.sql | 0 .../migrations/0040_time_report_collections.sql | 0 .../database}/migrations/0050_user_roles.sql | 0 backend/{ => internal/database}/migrations/README.md | 0 7 files changed, 8 insertions(+), 2 deletions(-) rename backend/{ => internal/database}/migrations/0010_users.sql (100%) rename backend/{ => internal/database}/migrations/0020_projects.sql (100%) rename backend/{ => internal/database}/migrations/0030_time_reports.sql (100%) rename backend/{ => internal/database}/migrations/0040_time_report_collections.sql (100%) rename backend/{ => internal/database}/migrations/0050_user_roles.sql (100%) rename backend/{ => internal/database}/migrations/README.md (100%) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 0334cc4..c14ba97 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -1,6 +1,7 @@ package database import ( + "embed" "log" "os" "path/filepath" @@ -15,6 +16,9 @@ type Db struct { *sqlx.DB } +//go:embed migrations +var scripts embed.FS + const userInsert = "INSERT INTO users (username, password) VALUES (?, ?)" const projectInsert = "INSERT INTO projects (name, description, user_id) SELECT ?, ?, id FROM users WHERE username = ?" @@ -62,7 +66,8 @@ func (d *Db) AddProject(name string, description string, username string) error // Reads a directory of migration files and applies them to the database. // This will eventually be used on an embedded directory func (d *Db) Migrate(dirname string) error { - files, err := os.ReadDir(dirname) + // Read the embedded scripts directory + files, err := scripts.ReadDir("migrations") if err != nil { return err } @@ -75,7 +80,8 @@ func (d *Db) Migrate(dirname string) error { continue } - sqlFile := filepath.Join(dirname, file.Name()) + // This is perhaps not the most elegant way to do this + sqlFile := filepath.Join("migrations", file.Name()) sqlBytes, err := os.ReadFile(sqlFile) if err != nil { return err diff --git a/backend/migrations/0010_users.sql b/backend/internal/database/migrations/0010_users.sql similarity index 100% rename from backend/migrations/0010_users.sql rename to backend/internal/database/migrations/0010_users.sql diff --git a/backend/migrations/0020_projects.sql b/backend/internal/database/migrations/0020_projects.sql similarity index 100% rename from backend/migrations/0020_projects.sql rename to backend/internal/database/migrations/0020_projects.sql diff --git a/backend/migrations/0030_time_reports.sql b/backend/internal/database/migrations/0030_time_reports.sql similarity index 100% rename from backend/migrations/0030_time_reports.sql rename to backend/internal/database/migrations/0030_time_reports.sql diff --git a/backend/migrations/0040_time_report_collections.sql b/backend/internal/database/migrations/0040_time_report_collections.sql similarity index 100% rename from backend/migrations/0040_time_report_collections.sql rename to backend/internal/database/migrations/0040_time_report_collections.sql diff --git a/backend/migrations/0050_user_roles.sql b/backend/internal/database/migrations/0050_user_roles.sql similarity index 100% rename from backend/migrations/0050_user_roles.sql rename to backend/internal/database/migrations/0050_user_roles.sql diff --git a/backend/migrations/README.md b/backend/internal/database/migrations/README.md similarity index 100% rename from backend/migrations/README.md rename to backend/internal/database/migrations/README.md From 2349fad4f49ef8ee934855d7f4f2fc313a22c297 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 28 Feb 2024 08:39:42 +0100 Subject: [PATCH 018/818] Make use of the config --- backend/cmd/main.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/backend/cmd/main.go b/backend/cmd/main.go index bb71e31..ea8d968 100644 --- a/backend/cmd/main.go +++ b/backend/cmd/main.go @@ -47,9 +47,11 @@ func main() { conf.WriteConfigToFile("config.toml") } - println(conf) + // Pretty print the current config + str, _ := json.MarshalIndent(conf, "", " ") + fmt.Println(string(str)) - database.DbConnect("db.sqlite3") + database.DbConnect(conf.DbPath) b := &ButtonState{PressCount: 0} // Mounting the handlers @@ -58,12 +60,14 @@ func main() { http.HandleFunc("/hello", handler) http.HandleFunc("/api/button", b.pressHandler) - // Start the server on port 8080 - println("Currently listening on http://localhost:8080") - println("Visit http://localhost:8080/hello to see the hello handler in action") - println("Visit http://localhost:8080/button to see the button handler in action") + // Construct a server URL + server_url := fmt.Sprintf(":%d", conf.Port) + + println("Server running on port", conf.Port) + println("Visit http://localhost" + server_url) println("Press Ctrl+C to stop the server") - err = http.ListenAndServe(":8080", nil) + + err = http.ListenAndServe(server_url, nil) if err != nil { panic(err) } From 47040c8dc255a0bd546567f20f3f31526d39f88c Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 28 Feb 2024 10:34:51 +0100 Subject: [PATCH 019/818] Format target --- backend/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/Makefile b/backend/Makefile index aeab862..a5b55df 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -46,5 +46,9 @@ migrate: sqlite3 $(DB_FILE) < $$file; \ done +# Format +fmt: + $(GOCMD) fmt ./... + # Default target default: build \ No newline at end of file From 4c7e660a20c62a62a8cee38c568cc78ce49eddd1 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 28 Feb 2024 10:58:32 +0100 Subject: [PATCH 020/818] Correcting path in makefile --- backend/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/Makefile b/backend/Makefile index 2bd6895..b8cfe3c 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -9,7 +9,7 @@ GOGET = $(GOCMD) get DB_FILE = db.sqlite3 # Directory containing migration SQL scripts -MIGRATIONS_DIR = migrations +MIGRATIONS_DIR = internal/database/migrations # Build target build: From 4c7a3fd9a0ff0fa237a6790575949d80457204e0 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 28 Feb 2024 11:28:57 +0100 Subject: [PATCH 021/818] Pulling in fiber as a dep --- backend/go.mod | 18 +++++++++++++++++- backend/go.sum | 27 +++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/backend/go.mod b/backend/go.mod index 8c06f45..fdbbaf7 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -3,7 +3,23 @@ module ttime go 1.21.1 require ( + github.com/BurntSushi/toml v1.3.2 github.com/jmoiron/sqlx v1.3.5 github.com/mattn/go-sqlite3 v1.14.22 - github.com/BurntSushi/toml v1.3.2 +) + +// These are all for fiber +require ( + github.com/andybalholm/brotli v1.0.5 // indirect + github.com/gofiber/fiber/v2 v2.52.1 // indirect + github.com/google/uuid v1.5.0 // indirect + github.com/klauspost/compress v1.17.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.51.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/sys v0.15.0 // indirect ) diff --git a/backend/go.sum b/backend/go.sum index afab06f..9ddb540 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -1,11 +1,38 @@ github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/gofiber/fiber/v2 v2.52.1 h1:1RoU2NS+b98o1L77sdl5mboGPiW+0Ypsi5oLmcYlgHI= +github.com/gofiber/fiber/v2 v2.52.1/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= +github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= From d51b9f78c2e20234b6c8482c3719fb6b1e21c129 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 28 Feb 2024 11:29:32 +0100 Subject: [PATCH 022/818] Porting to fiber --- backend/cmd/main.go | 56 ++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 34 deletions(-) diff --git a/backend/cmd/main.go b/backend/cmd/main.go index ea8d968..ee394ca 100644 --- a/backend/cmd/main.go +++ b/backend/cmd/main.go @@ -3,11 +3,10 @@ package main import ( "encoding/json" "fmt" - "io" - "net/http" "ttime/internal/config" "ttime/internal/database" + "github.com/gofiber/fiber/v2" _ "github.com/mattn/go-sqlite3" ) @@ -19,25 +18,15 @@ type ButtonState struct { // This is what a handler with a receiver looks like // Keep in mind that concurrent state access is not (usually) safe // And will in practice be guarded by a mutex -func (b *ButtonState) pressHandler(w http.ResponseWriter, r *http.Request) { - if r.Method == "POST" { - b.PressCount++ - } - - response, err := json.Marshal(b) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - fmt.Println("Request received") - io.WriteString(w, string(response)) +func (b *ButtonState) pressHandlerGet(c *fiber.Ctx) error { + fmt.Println("Get request received") + return c.JSON(b) } -// This is what a handler looks like -func handler(w http.ResponseWriter, r *http.Request) { - fmt.Println("Request received") - io.WriteString(w, "This is my website!\n") +func (b *ButtonState) pressHandlerPost(c *fiber.Ctx) error { + fmt.Println("Post request received") + b.PressCount++ + return c.JSON(b) } func main() { @@ -52,23 +41,22 @@ func main() { fmt.Println(string(str)) database.DbConnect(conf.DbPath) + + app := fiber.New() + + app.Static("/", "./static") + b := &ButtonState{PressCount: 0} + app.Get("/api/button", b.pressHandlerGet) + app.Post("/api/button", b.pressHandlerPost) + app.Post("/api/project", func(c *fiber.Ctx) error { + return c.JSON(fiber.Map{ + "message": "Project created", + }) + }) - // Mounting the handlers - fs := http.FileServer(http.Dir("static")) - http.Handle("/", fs) - http.HandleFunc("/hello", handler) - http.HandleFunc("/api/button", b.pressHandler) - - // Construct a server URL - server_url := fmt.Sprintf(":%d", conf.Port) - - println("Server running on port", conf.Port) - println("Visit http://localhost" + server_url) - println("Press Ctrl+C to stop the server") - - err = http.ListenAndServe(server_url, nil) + err = app.Listen(fmt.Sprintf(":%d", conf.Port)) if err != nil { - panic(err) + fmt.Println("Error starting server: ", err) } } From 360689ce0ca427fec45efff7270db45c4d3b064c Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 28 Feb 2024 20:35:53 +0100 Subject: [PATCH 023/818] Configure tailwind for the frontend --- frontend/.eslintrc.cjs | 2 +- frontend/package-lock.json | 1049 +++++++++++++++++++++++++++++++++++ frontend/package.json | 3 + frontend/postcss.config.js | 6 + frontend/src/index.css | 4 + frontend/tailwind.config.js | 13 + 6 files changed, 1076 insertions(+), 1 deletion(-) create mode 100644 frontend/postcss.config.js create mode 100644 frontend/tailwind.config.js diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs index 6606c58..da4b045 100644 --- a/frontend/.eslintrc.cjs +++ b/frontend/.eslintrc.cjs @@ -9,7 +9,7 @@ module.exports = { 'plugin:react-hooks/recommended', 'plugin:prettier/recommended', ], - ignorePatterns: ['dist', '.eslintrc.cjs'], + ignorePatterns: ['dist', '.eslintrc.cjs', 'tailwind.config.js', 'postcss.config.js'], parser: '@typescript-eslint/parser', plugins: ['react-refresh', 'prettier'], rules: { diff --git a/frontend/package-lock.json b/frontend/package-lock.json index fc12e8b..f26f4a2 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -17,13 +17,16 @@ "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", "@vitejs/plugin-react-swc": "^3.5.0", + "autoprefixer": "^10.4.17", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.5", + "postcss": "^8.4.35", "prettier": "3.2.5", + "tailwindcss": "^3.4.1", "typescript": "^5.2.2", "vite": "^5.1.0" } @@ -37,6 +40,18 @@ "node": ">=0.10.0" } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.19.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", @@ -538,6 +553,98 @@ "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.4.tgz", + "integrity": "sha512-Oud2QPM5dHviZNn4y/WhhYKSXksv+1xLEIsNrAbGcFzUN3ubqWRFT5gwPchNc5NuzILOU4tPBDTZ4VwhL8Y7cw==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.23.tgz", + "integrity": "sha512-9/4foRoUKp8s96tSkh8DlAAc5A0Ty8vLXld+l9gjKKY6ckwI8G15f0hskGmuLZu78ZlGa1vtsfOa+lnB4vG6Jg==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -573,6 +680,16 @@ "node": ">= 8" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@pkgr/core": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", @@ -1283,6 +1400,31 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1413,6 +1555,43 @@ "has-symbols": "^1.0.3" } }, + "node_modules/autoprefixer": { + "version": "10.4.17", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz", + "integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.22.2", + "caniuse-lite": "^1.0.30001578", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz", @@ -1431,6 +1610,15 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -1452,6 +1640,38 @@ "node": ">=8" } }, + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -1480,6 +1700,35 @@ "node": ">=6" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001591", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz", + "integrity": "sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1496,6 +1745,42 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1514,6 +1799,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1534,6 +1828,18 @@ "node": ">= 8" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -1598,6 +1904,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1610,6 +1922,12 @@ "node": ">=8" } }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -1622,6 +1940,24 @@ "node": ">=6.0.0" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.4.686", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.686.tgz", + "integrity": "sha512-3avY1B+vUzNxEgkBDpKOP8WarvUAEwpRaiCL0He5OKWEFxzaOFiq4WoZEZe7qh0ReS7DiWoHMnYoQCKxNZNzSg==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, "node_modules/es-abstract": { "version": "1.22.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", @@ -1800,6 +2136,15 @@ "@esbuild/win32-x64": "0.19.12" } }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2242,6 +2587,35 @@ "is-callable": "^1.1.3" } }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2644,6 +3018,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -2720,6 +3106,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-generator-function": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", @@ -2942,6 +3337,33 @@ "set-function-name": "^2.0.1" } }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3014,6 +3436,21 @@ "node": ">= 0.8.0" } }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3095,12 +3532,32 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -3125,6 +3582,30 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -3134,6 +3615,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", @@ -3332,6 +3822,31 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -3359,6 +3874,24 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/postcss": { "version": "8.4.35", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", @@ -3387,6 +3920,144 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-import/node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", + "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.11" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.15", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", + "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -3492,6 +4163,27 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.5.tgz", @@ -3765,6 +4457,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -3783,6 +4487,71 @@ "node": ">=0.10.0" } }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", @@ -3860,6 +4629,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -3872,6 +4654,50 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -3912,12 +4738,87 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/tailwindcss": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", + "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.19.1", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3942,6 +4843,12 @@ "typescript": ">=4.2.0" } }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -4065,6 +4972,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -4074,6 +5011,12 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, "node_modules/vite": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.1.tgz", @@ -4220,6 +5163,100 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -4232,6 +5269,18 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/yaml": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.0.tgz", + "integrity": "sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 48a0d73..8c5756e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -20,13 +20,16 @@ "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", "@vitejs/plugin-react-swc": "^3.5.0", + "autoprefixer": "^10.4.17", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.5", + "postcss": "^8.4.35", "prettier": "3.2.5", + "tailwindcss": "^3.4.1", "typescript": "^5.2.2", "vite": "^5.1.0" } diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/frontend/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/frontend/src/index.css b/frontend/src/index.css index 6119ad9..e7d4bb2 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,3 +1,7 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + :root { font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js new file mode 100644 index 0000000..ac21ae1 --- /dev/null +++ b/frontend/tailwind.config.js @@ -0,0 +1,13 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + "./index.html", + "./src/**/*.{js,ts,jsx,tsx}", + ], + + theme: { + extend: {}, + }, + plugins: [], +} + From 568fb0323e6a43c1417baf56c71e2c0fe78c027d Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 29 Feb 2024 07:29:12 +0100 Subject: [PATCH 024/818] Makefile changes related to watch --- backend/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/Makefile b/backend/Makefile index b8cfe3c..c5a63e8 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -20,7 +20,7 @@ run: build ./bin/server watch: build - watchexec -w . -r make run + watchexec -c -w . -r make run # Clean target clean: From 4d510e5c510544516080a374a6bfef0b271262b2 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 29 Feb 2024 20:02:13 +0100 Subject: [PATCH 025/818] Rename App -> Server, less confusing --- backend/cmd/main.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/cmd/main.go b/backend/cmd/main.go index ee394ca..b43b204 100644 --- a/backend/cmd/main.go +++ b/backend/cmd/main.go @@ -42,20 +42,20 @@ func main() { database.DbConnect(conf.DbPath) - app := fiber.New() + server := fiber.New() - app.Static("/", "./static") + server.Static("/", "./static") b := &ButtonState{PressCount: 0} - app.Get("/api/button", b.pressHandlerGet) - app.Post("/api/button", b.pressHandlerPost) - app.Post("/api/project", func(c *fiber.Ctx) error { + server.Get("/api/button", b.pressHandlerGet) + server.Post("/api/button", b.pressHandlerPost) + server.Post("/api/project", func(c *fiber.Ctx) error { return c.JSON(fiber.Map{ "message": "Project created", }) }) - err = app.Listen(fmt.Sprintf(":%d", conf.Port)) + err = server.Listen(fmt.Sprintf(":%d", conf.Port)) if err != nil { fmt.Println("Error starting server: ", err) } From b39ce645d63db238406d33544090e5cf76029179 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 29 Feb 2024 20:33:20 +0100 Subject: [PATCH 026/818] Proper interface for the database --- backend/internal/database/db.go | 11 ++++++++++- backend/internal/database/db_test.go | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index c14ba97..b850289 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -10,6 +10,15 @@ import ( _ "github.com/mattn/go-sqlite3" ) +// Interface for the database +type Database interface { + AddUser(username string, password string) error + RemoveUser(username string) error + GetUserId(username string) (int, error) + AddProject(name string, description string, username string) error + Migrate(dirname string) error +} + // This struct is a wrapper type that holds the database connection // Internally DB holds a connection pool, so it's safe for concurrent use type Db struct { @@ -23,7 +32,7 @@ const userInsert = "INSERT INTO users (username, password) VALUES (?, ?)" const projectInsert = "INSERT INTO projects (name, description, user_id) SELECT ?, ?, id FROM users WHERE username = ?" // DbConnect connects to the database -func DbConnect(dbpath string) *Db { +func DbConnect(dbpath string) Database { // Open the database db, err := sqlx.Connect("sqlite3", dbpath) if err != nil { diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index 62e47db..716bde8 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -6,7 +6,7 @@ import ( // Tests are not guaranteed to be sequential -func setupState() (*Db, error) { +func setupState() (Database, error) { db := DbConnect(":memory:") err := db.Migrate("../../migrations") if err != nil { From a749857c98bb1290e7e99cf98273514864cd6c61 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Fri, 1 Mar 2024 07:47:06 +0100 Subject: [PATCH 027/818] User_roles table constraints --- .../internal/database/migrations/0050_user_roles.sql | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/backend/internal/database/migrations/0050_user_roles.sql b/backend/internal/database/migrations/0050_user_roles.sql index 32e03dc..56e597b 100644 --- a/backend/internal/database/migrations/0050_user_roles.sql +++ b/backend/internal/database/migrations/0050_user_roles.sql @@ -4,4 +4,13 @@ CREATE TABLE IF NOT EXISTS user_roles ( role STRING NOT NULL, -- 'admin' or 'member' FOREIGN KEY (user_id) REFERENCES users (id) FOREIGN KEY (project_id) REFERENCES projects (id) -); \ No newline at end of file + PRIMARY KEY (user_id, project_id) +); + +-- Make sure that the role is either 'admin' or 'member' +CREATE TRIGGER IF NOT EXISTS user_role_admin_or_member + BEFORE INSERT ON user_roles + FOR EACH ROW + BEGIN + SELECT RAISE(ABORT, 'Invalid role') WHERE NEW.role NOT IN ('admin', 'member'); + END; \ No newline at end of file From 5cefed405f13136e9fbc86a21e33016e2819d807 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sat, 2 Mar 2024 02:31:20 +0100 Subject: [PATCH 028/818] Reduce verbosity of the tests --- backend/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/Makefile b/backend/Makefile index c5a63e8..525356c 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -30,7 +30,7 @@ clean: # Test target test: db.sqlite3 - $(GOTEST) ./... -count=1 -v + $(GOTEST) ./... -count=1 # Get dependencies target deps: From 469f8665543bbc013a5f1bdcf63b38ede1c90a88 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sat, 2 Mar 2024 02:38:26 +0100 Subject: [PATCH 029/818] Cleaning up examples in main and splitting the code into suitable packages --- backend/cmd/main.go | 40 +++++-------------- backend/internal/handlers/global_state.go | 36 +++++++++++++++++ .../internal/handlers/global_state_test.go | 15 +++++++ backend/internal/types/users.go | 23 +++++++++++ backend/internal/types/users_test.go | 35 ++++++++++++++++ 5 files changed, 120 insertions(+), 29 deletions(-) create mode 100644 backend/internal/handlers/global_state.go create mode 100644 backend/internal/handlers/global_state_test.go create mode 100644 backend/internal/types/users.go create mode 100644 backend/internal/types/users_test.go diff --git a/backend/cmd/main.go b/backend/cmd/main.go index b43b204..b238470 100644 --- a/backend/cmd/main.go +++ b/backend/cmd/main.go @@ -5,30 +5,12 @@ import ( "fmt" "ttime/internal/config" "ttime/internal/database" + "ttime/internal/handlers" "github.com/gofiber/fiber/v2" _ "github.com/mattn/go-sqlite3" ) -// The button state as represented in memory -type ButtonState struct { - PressCount int `json:"pressCount"` -} - -// This is what a handler with a receiver looks like -// Keep in mind that concurrent state access is not (usually) safe -// And will in practice be guarded by a mutex -func (b *ButtonState) pressHandlerGet(c *fiber.Ctx) error { - fmt.Println("Get request received") - return c.JSON(b) -} - -func (b *ButtonState) pressHandlerPost(c *fiber.Ctx) error { - fmt.Println("Post request received") - b.PressCount++ - return c.JSON(b) -} - func main() { conf, err := config.ReadConfigFromFile("config.toml") if err != nil { @@ -40,21 +22,21 @@ func main() { str, _ := json.MarshalIndent(conf, "", " ") fmt.Println(string(str)) - database.DbConnect(conf.DbPath) - + // Connect to the database + db := database.DbConnect(conf.DbPath) + // Get our global state + gs := handlers.NewGlobalState(db) + // Create the server server := fiber.New() + // Mount our static files (Beware of the security implications of this!) + // This will likely be replaced by an embedded filesystem in the future server.Static("/", "./static") - b := &ButtonState{PressCount: 0} - server.Get("/api/button", b.pressHandlerGet) - server.Post("/api/button", b.pressHandlerPost) - server.Post("/api/project", func(c *fiber.Ctx) error { - return c.JSON(fiber.Map{ - "message": "Project created", - }) - }) + // Register our handlers + server.Post("/api/register", gs.Register) + // Announce the port we are listening on and start the server err = server.Listen(fmt.Sprintf(":%d", conf.Port)) if err != nil { fmt.Println("Error starting server: ", err) diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go new file mode 100644 index 0000000..4dae3d8 --- /dev/null +++ b/backend/internal/handlers/global_state.go @@ -0,0 +1,36 @@ +package handlers + +import ( + "ttime/internal/database" + "ttime/internal/types" + + "github.com/gofiber/fiber/v2" +) + +// The actual interface that we will use +type GlobalState interface { + Register(c *fiber.Ctx) error +} + +// "Constructor" +func NewGlobalState(db database.Database) GlobalState { + return &GState{Db: db} +} + +// The global state, which implements all the handlers +type GState struct { + Db database.Database +} + +func (gs *GState) Register(c *fiber.Ctx) error { + u := new(types.User) + if err := c.BodyParser(u); err != nil { + return c.Status(400).SendString(err.Error()) + } + + if err := gs.Db.AddUser(u.Username, u.Password); err != nil { + return c.Status(500).SendString(err.Error()) + } + + return c.Status(200).SendString("User added") +} diff --git a/backend/internal/handlers/global_state_test.go b/backend/internal/handlers/global_state_test.go new file mode 100644 index 0000000..c0b64f7 --- /dev/null +++ b/backend/internal/handlers/global_state_test.go @@ -0,0 +1,15 @@ +package handlers + +import ( + "testing" + "ttime/internal/database" +) + +// The actual interface that we will use +func TestGlobalState(t *testing.T) { + db := database.DbConnect(":memory:") + gs := NewGlobalState(db) + if gs == nil { + t.Error("NewGlobalState returned nil") + } +} diff --git a/backend/internal/types/users.go b/backend/internal/types/users.go new file mode 100644 index 0000000..fa735d7 --- /dev/null +++ b/backend/internal/types/users.go @@ -0,0 +1,23 @@ +package types + +// User struct represents a user in the system +type User struct { + UserId string `json:"userId"` + Username string `json:"username"` + Password string `json:"password"` +} + +// If the user needs to be served over the api, we dont want to send the password +// ToPublicUser converts a User to a PublicUser +func (u *User) ToPublicUser() (*PublicUser, error) { + return &PublicUser{ + UserId: u.UserId, + Username: u.Username, + }, nil +} + +// PublicUser represents a user that is safe to send over the API (no password) +type PublicUser struct { + UserId string `json:"userId"` + Username string `json:"username"` +} diff --git a/backend/internal/types/users_test.go b/backend/internal/types/users_test.go new file mode 100644 index 0000000..811839d --- /dev/null +++ b/backend/internal/types/users_test.go @@ -0,0 +1,35 @@ +package types + +import "testing" + +// NewUser returns a new User +func TestNewUser(t *testing.T) { + u := User{ + UserId: "123", + Username: "test", + Password: "password", + } + + if u.UserId != "123" { + t.Errorf("Expected user id to be 123, got %s", u.UserId) + } + if u.Username != "test" { + t.Errorf("Expected username to be test, got %s", u.Username) + } + if u.Password != "password" { + t.Errorf("Expected password to be password, got %s", u.Password) + } +} + +func TestToPublicUser(t *testing.T) { + u := User{ + UserId: "123", + Username: "test", + Password: "password", + } + + _, err := u.ToPublicUser() + if err != nil { + t.Errorf("Expected no error, got %s", err) + } +} From 63596db60c36c06cfc8018aded848226f759433d Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sat, 2 Mar 2024 02:40:17 +0100 Subject: [PATCH 030/818] Migrations readme warnings --- backend/internal/database/migrations/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/internal/database/migrations/README.md b/backend/internal/database/migrations/README.md index 18b5e34..8a0c271 100644 --- a/backend/internal/database/migrations/README.md +++ b/backend/internal/database/migrations/README.md @@ -4,6 +4,7 @@ This directory contains all the database migrations for the backend. [!WARNING] Keep in mind that these migrations are **not yet stable**. +In practice, this means that the database schema may change at any time, and that the migrations may not be backwards compatible. ## Running migrations From e93c4abba8f93c861eec5e5b30ce25e65cf899c5 Mon Sep 17 00:00:00 2001 From: Imbus <66123528+imbus64@users.noreply.github.com> Date: Sat, 2 Mar 2024 02:52:35 +0100 Subject: [PATCH 031/818] Create go.yml --- .github/workflows/go.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/go.yml diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..f6d518c --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,28 @@ +# This workflow will build a golang project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go + +name: Go + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.21' + + - name: Build + run: go build -v ./backend/... + + - name: Test + run: go test -v ./backend/... From b202665d75a7a553426480c4303152fc85588f1f Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sat, 2 Mar 2024 02:56:00 +0100 Subject: [PATCH 032/818] Fix gh workflow --- .github/workflows/go.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index f6d518c..8c24059 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -22,7 +22,7 @@ jobs: go-version: '1.21' - name: Build - run: go build -v ./backend/... + run: go build -v ./... - name: Test - run: go test -v ./backend/... + run: go test -v ./... From 2808a0058fac87d3662a249d2685185981c4bb5a Mon Sep 17 00:00:00 2001 From: Imbus <66123528+imbus64@users.noreply.github.com> Date: Sat, 2 Mar 2024 03:01:26 +0100 Subject: [PATCH 033/818] Create node.js.yml --- .github/workflows/node.js.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/node.js.yml diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml new file mode 100644 index 0000000..fe3aef7 --- /dev/null +++ b/.github/workflows/node.js.yml @@ -0,0 +1,35 @@ +# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs + +name: Node.js CI + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + + runs-on: ubuntu-latest + + defaults: + run: + working-directory: ./frontend + + strategy: + matrix: + node-version: [14.x, 16.x, 18.x] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + + steps: + - uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + - run: npm ci + - run: npm run build --if-present + - run: npm test From 3653146ce0f66366b757987ba72dbd7973ee4c32 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sat, 2 Mar 2024 03:07:41 +0100 Subject: [PATCH 034/818] Wrestling with github CI/CD --- .github/workflows/go.yml | 5 +++++ .github/workflows/node.js.yml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 8c24059..629e749 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -13,6 +13,11 @@ jobs: build: runs-on: ubuntu-latest + + defaults: + run: + working-directory: ./backend + steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index fe3aef7..5b5a201 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -20,7 +20,7 @@ jobs: strategy: matrix: - node-version: [14.x, 16.x, 18.x] + node-version: [20.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: From 1be992bc34cbe8f551bbb3b55a6a67be507c1a97 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sat, 2 Mar 2024 03:14:45 +0100 Subject: [PATCH 035/818] Further wrestling with CI/CD --- .github/workflows/node.js.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 5b5a201..548f6ee 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -30,6 +30,7 @@ jobs: with: node-version: ${{ matrix.node-version }} cache: 'npm' + cache-dependency-path: '**/package-lock.json' - run: npm ci - run: npm run build --if-present - run: npm test From 15a83882ef0bd3c25005504a9f4c8d00613bc5fa Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sat, 2 Mar 2024 03:19:05 +0100 Subject: [PATCH 036/818] Omitting test from CI, including lint --- .github/workflows/node.js.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 548f6ee..742134a 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -32,5 +32,6 @@ jobs: cache: 'npm' cache-dependency-path: '**/package-lock.json' - run: npm ci + - run: npm run lint - run: npm run build --if-present - - run: npm test + # - run: npm test # Not implemented yet From 54cca2b1513061f93c78103c8213ce2a12fc9710 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sat, 2 Mar 2024 03:47:53 +0100 Subject: [PATCH 037/818] Pulling in jest as a dependency for the frontent, example test included --- .github/workflows/node.js.yml | 2 +- frontend/.eslintrc.cjs | 2 +- frontend/jest.config.cjs | 5 + frontend/package-lock.json | 3117 ++++++++++++++++- frontend/package.json | 8 +- .../Components/__tests__/CountButton.test.ts | 12 + 6 files changed, 3087 insertions(+), 59 deletions(-) create mode 100644 frontend/jest.config.cjs create mode 100644 frontend/src/Components/__tests__/CountButton.test.ts diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 742134a..49f8c3b 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -34,4 +34,4 @@ jobs: - run: npm ci - run: npm run lint - run: npm run build --if-present - # - run: npm test # Not implemented yet + - run: npm test diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs index da4b045..447a464 100644 --- a/frontend/.eslintrc.cjs +++ b/frontend/.eslintrc.cjs @@ -9,7 +9,7 @@ module.exports = { 'plugin:react-hooks/recommended', 'plugin:prettier/recommended', ], - ignorePatterns: ['dist', '.eslintrc.cjs', 'tailwind.config.js', 'postcss.config.js'], + ignorePatterns: ['dist', '.eslintrc.cjs', 'tailwind.config.js', 'postcss.config.js', 'jest.config.cjs'], parser: '@typescript-eslint/parser', plugins: ['react-refresh', 'prettier'], rules: { diff --git a/frontend/jest.config.cjs b/frontend/jest.config.cjs new file mode 100644 index 0000000..28ea68a --- /dev/null +++ b/frontend/jest.config.cjs @@ -0,0 +1,5 @@ +module.exports = { + transform: { + '^.+\\.(t|j)sx?$': '@swc/jest', + }, +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index f26f4a2..5131784 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -12,6 +12,9 @@ "react-dom": "^18.2.0" }, "devDependencies": { + "@swc/core": "^1.4.2", + "@swc/jest": "^0.2.36", + "@types/jest": "^29.5.12", "@types/react": "^18.2.55", "@types/react-dom": "^18.2.19", "@typescript-eslint/eslint-plugin": "^6.21.0", @@ -24,8 +27,10 @@ "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.5", + "jest": "^29.7.0", "postcss": "^8.4.35", "prettier": "3.2.5", + "react-test-renderer": "^18.2.0", "tailwindcss": "^3.4.1", "typescript": "^5.2.2", "vite": "^5.1.0" @@ -52,6 +57,683 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz", + "integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.24.0", + "@babel/parser": "^7.24.0", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.0", + "@babel/types": "^7.24.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.6", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", + "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.0.tgz", + "integrity": "sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.0", + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", + "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.0.tgz", + "integrity": "sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.19.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", @@ -597,15 +1279,413 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.4.tgz", - "integrity": "sha512-Oud2QPM5dHviZNn4y/WhhYKSXksv+1xLEIsNrAbGcFzUN3ubqWRFT5gwPchNc5NuzILOU4tPBDTZ4VwhL8Y7cw==", + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/create-cache-key-function": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", + "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" @@ -621,9 +1701,9 @@ } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "engines": { "node": ">=6.0.0" @@ -636,9 +1716,9 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.23", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.23.tgz", - "integrity": "sha512-9/4foRoUKp8s96tSkh8DlAAc5A0Ty8vLXld+l9gjKKY6ckwI8G15f0hskGmuLZu78ZlGa1vtsfOa+lnB4vG6Jg==", + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.24.tgz", + "integrity": "sha512-+VaWXDa6+l6MhflBvVXjIEAzb59nQ2JUK3bwRp2zRpPtU+8TFRy9Gg/5oIcNlkEL5PGlBFGfemUVvIgLnTzq7Q==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -871,14 +1951,38 @@ "win32" ] }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, "node_modules/@swc/core": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.4.0.tgz", - "integrity": "sha512-wc5DMI5BJftnK0Fyx9SNJKkA0+BZSJQx8430yutWmsILkHMBD3Yd9GhlMaxasab9RhgKqZp7Ht30hUYO5ZDvQg==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.4.2.tgz", + "integrity": "sha512-vWgY07R/eqj1/a0vsRKLI9o9klGZfpLNOVEnrv4nrccxBgYPjcf22IWwAoaBJ+wpA7Q4fVjCUM8lP0m01dpxcg==", "dev": true, "hasInstallScript": true, "dependencies": { - "@swc/counter": "^0.1.1", + "@swc/counter": "^0.1.2", "@swc/types": "^0.1.5" }, "engines": { @@ -889,16 +1993,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.4.0", - "@swc/core-darwin-x64": "1.4.0", - "@swc/core-linux-arm-gnueabihf": "1.4.0", - "@swc/core-linux-arm64-gnu": "1.4.0", - "@swc/core-linux-arm64-musl": "1.4.0", - "@swc/core-linux-x64-gnu": "1.4.0", - "@swc/core-linux-x64-musl": "1.4.0", - "@swc/core-win32-arm64-msvc": "1.4.0", - "@swc/core-win32-ia32-msvc": "1.4.0", - "@swc/core-win32-x64-msvc": "1.4.0" + "@swc/core-darwin-arm64": "1.4.2", + "@swc/core-darwin-x64": "1.4.2", + "@swc/core-linux-arm-gnueabihf": "1.4.2", + "@swc/core-linux-arm64-gnu": "1.4.2", + "@swc/core-linux-arm64-musl": "1.4.2", + "@swc/core-linux-x64-gnu": "1.4.2", + "@swc/core-linux-x64-musl": "1.4.2", + "@swc/core-win32-arm64-msvc": "1.4.2", + "@swc/core-win32-ia32-msvc": "1.4.2", + "@swc/core-win32-x64-msvc": "1.4.2" }, "peerDependencies": { "@swc/helpers": "^0.5.0" @@ -910,9 +2014,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.4.0.tgz", - "integrity": "sha512-UTJ/Vz+s7Pagef6HmufWt6Rs0aUu+EJF4Pzuwvr7JQQ5b1DZeAAUeUtkUTFx/PvCbM8Xfw4XdKBUZfrIKCfW8A==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.4.2.tgz", + "integrity": "sha512-1uSdAn1MRK5C1m/TvLZ2RDvr0zLvochgrZ2xL+lRzugLlCTlSA+Q4TWtrZaOz+vnnFVliCpw7c7qu0JouhgQIw==", "cpu": [ "arm64" ], @@ -926,9 +2030,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.4.0.tgz", - "integrity": "sha512-f8v58u2GsGak8EtZFN9guXqE0Ep10Suny6xriaW2d8FGqESPyNrnBzli3aqkSeQk5gGqu2zJ7WiiKp3XoUOidA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.4.2.tgz", + "integrity": "sha512-TYD28+dCQKeuxxcy7gLJUCFLqrwDZnHtC2z7cdeGfZpbI2mbfppfTf2wUPzqZk3gEC96zHd4Yr37V3Tvzar+lQ==", "cpu": [ "x64" ], @@ -942,9 +2046,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.4.0.tgz", - "integrity": "sha512-q2KAkBzmPcTnRij/Y1fgHCKAGevUX/H4uUESrw1J5gmUg9Qip6onKV80lTumA1/aooGJ18LOsB31qdbwmZk9OA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.4.2.tgz", + "integrity": "sha512-Eyqipf7ZPGj0vplKHo8JUOoU1un2sg5PjJMpEesX0k+6HKE2T8pdyeyXODN0YTFqzndSa/J43EEPXm+rHAsLFQ==", "cpu": [ "arm" ], @@ -958,9 +2062,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.4.0.tgz", - "integrity": "sha512-SknGu96W0mzHtLHWm+62fk5+Omp9fMPFO7AWyGFmz2tr8EgRRXtTSrBUnWhAbgcalnhen48GsvtMdxf1KNputg==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.4.2.tgz", + "integrity": "sha512-wZn02DH8VYPv3FC0ub4my52Rttsus/rFw+UUfzdb3tHMHXB66LqN+rR0ssIOZrH6K+VLN6qpTw9VizjyoH0BxA==", "cpu": [ "arm64" ], @@ -974,9 +2078,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.4.0.tgz", - "integrity": "sha512-/k3TDvpBRMDNskHooNN1KqwUhcwkfBlIYxRTnJvsfT2C7My4pffR+4KXmt0IKynlTTbCdlU/4jgX4801FSuliw==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.4.2.tgz", + "integrity": "sha512-3G0D5z9hUj9bXNcwmA1eGiFTwe5rWkuL3DsoviTj73TKLpk7u64ND0XjEfO0huVv4vVu9H1jodrKb7nvln/dlw==", "cpu": [ "arm64" ], @@ -990,9 +2094,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.4.0.tgz", - "integrity": "sha512-GYsTMvNt5+WTVlwwQzOOWsPMw6P/F41u5PGHWmfev8Nd4QJ1h3rWPySKk4mV42IJwH9MgQCVSl3ygwNqwl6kFg==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.4.2.tgz", + "integrity": "sha512-LFxn9U8cjmYHw3jrdPNqPAkBGglKE3tCZ8rA7hYyp0BFxuo7L2ZcEnPm4RFpmSCCsExFH+LEJWuMGgWERoktvg==", "cpu": [ "x64" ], @@ -1006,9 +2110,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.4.0.tgz", - "integrity": "sha512-jGVPdM/VwF7kK/uYRW5N6FwzKf/FnDjGIR3RPvQokjYJy7Auk+3Oj21C0Jev7sIT9RYnO/TrFEoEozKeD/z2Qw==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.4.2.tgz", + "integrity": "sha512-dp0fAmreeVVYTUcb4u9njTPrYzKnbIH0EhH2qvC9GOYNNREUu2GezSIDgonjOXkHiTCvopG4xU7y56XtXj4VrQ==", "cpu": [ "x64" ], @@ -1022,9 +2126,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.4.0.tgz", - "integrity": "sha512-biHYm1AronEKlt47O/H8sSOBM2BKXMmWT+ApvlxUw50m1RGNnVnE0bgY7tylFuuSiWyXsQPJbmUV708JqORXVg==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.4.2.tgz", + "integrity": "sha512-HlVIiLMQkzthAdqMslQhDkoXJ5+AOLUSTV6fm6shFKZKqc/9cJvr4S8UveNERL9zUficA36yM3bbfo36McwnvQ==", "cpu": [ "arm64" ], @@ -1038,9 +2142,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.4.0.tgz", - "integrity": "sha512-TL5L2tFQb19kJwv6+elToGBj74QXCn9j+hZfwQatvZEJRA5rDK16eH6oAE751dGUArhnWlW3Vj65hViPvTuycw==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.4.2.tgz", + "integrity": "sha512-WCF8faPGjCl4oIgugkp+kL9nl3nUATlzKXCEGFowMEmVVCFM0GsqlmGdPp1pjZoWc9tpYanoXQDnp5IvlDSLhA==", "cpu": [ "ia32" ], @@ -1054,9 +2158,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.4.0.tgz", - "integrity": "sha512-e2xVezU7XZ2Stzn4i7TOQe2Kn84oYdG0M3A7XI7oTdcpsKCcKwgiMoroiAhqCv+iN20KNqhnWwJiUiTj/qN5AA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.4.2.tgz", + "integrity": "sha512-oV71rwiSpA5xre2C5570BhCsg1HF97SNLsZ/12xv7zayGzqr3yvFALFJN8tHKpqUdCB4FGPjoP3JFdV3i+1wUw==", "cpu": [ "x64" ], @@ -1075,24 +2179,134 @@ "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", "dev": true }, + "node_modules/@swc/jest": { + "version": "0.2.36", + "resolved": "https://registry.npmjs.org/@swc/jest/-/jest-0.2.36.tgz", + "integrity": "sha512-8X80dp81ugxs4a11z1ka43FPhP+/e+mJNXJSxiNYk8gIX/jPBtY4gQTrKu/KIoco8bzKuPI5lUxjfLiGsfvnlw==", + "dev": true, + "dependencies": { + "@jest/create-cache-key-function": "^29.7.0", + "@swc/counter": "^0.1.3", + "jsonc-parser": "^3.2.0" + }, + "engines": { + "npm": ">= 7.0.0" + }, + "peerDependencies": { + "@swc/core": "*" + } + }, "node_modules/@swc/types": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==", "dev": true }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.12", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", + "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/node": { + "version": "20.11.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", + "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, "node_modules/@types/prop-types": { "version": "15.7.11", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", @@ -1131,6 +2345,27 @@ "integrity": "sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==", "dev": true }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", @@ -1376,6 +2611,33 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1604,6 +2866,122 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1672,6 +3050,21 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -1700,6 +3093,15 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/camelcase-css": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", @@ -1745,6 +3147,15 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -1781,6 +3192,94 @@ "node": ">= 6" } }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "dev": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1814,6 +3313,33 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1863,12 +3389,35 @@ } } }, + "node_modules/dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/define-data-property": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.3.tgz", @@ -1904,12 +3453,30 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", "dev": true }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1952,12 +3519,33 @@ "integrity": "sha512-3avY1B+vUzNxEgkBDpKOP8WarvUAEwpRaiCL0He5OKWEFxzaOFiq4WoZEZe7qh0ReS7DiWoHMnYoQCKxNZNzSg==", "dev": true }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/es-abstract": { "version": "1.22.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", @@ -2415,6 +4003,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", @@ -2457,6 +4058,60 @@ "node": ">=0.10.0" } }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2518,6 +4173,15 @@ "reusify": "^1.0.4" } }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -2672,6 +4336,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -2691,6 +4373,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-symbol-description": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", @@ -2824,6 +4527,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -2911,6 +4620,21 @@ "node": ">= 0.4" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -2936,6 +4660,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -2991,6 +4734,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, "node_modules/is-async-function": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", @@ -3115,6 +4864,15 @@ "node": ">=8" } }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/is-generator-function": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", @@ -3233,6 +4991,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -3324,6 +5094,72 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz", + "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/iterator.prototype": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", @@ -3355,6 +5191,578 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve/node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/jiti": { "version": "1.21.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", @@ -3381,12 +5789,30 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -3399,6 +5825,24 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "dev": true + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -3423,6 +5867,24 @@ "json-buffer": "3.0.1" } }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -3495,6 +5957,36 @@ "node": ">=10" } }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3517,6 +6009,15 @@ "node": ">=8.6" } }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/minimatch": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", @@ -3582,6 +6083,12 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, "node_modules/node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", @@ -3606,6 +6113,18 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -3730,6 +6249,21 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -3777,6 +6311,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3789,6 +6332,24 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -3892,6 +6453,70 @@ "node": ">= 6" } }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/postcss": { "version": "8.4.35", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", @@ -4094,6 +6719,51 @@ "node": ">=6.0.0" } }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -4114,6 +6784,22 @@ "node": ">=6" } }, + "node_modules/pure-rand": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", + "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -4163,6 +6849,39 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "node_modules/react-shallow-renderer": { + "version": "16.15.0", + "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz", + "integrity": "sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==", + "dev": true, + "dependencies": { + "object-assign": "^4.1.1", + "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-test-renderer": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-18.2.0.tgz", + "integrity": "sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==", + "dev": true, + "dependencies": { + "react-is": "^18.2.0", + "react-shallow-renderer": "^16.15.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-test-renderer/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -4223,6 +6942,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "2.0.0-next.5", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", @@ -4240,6 +6968,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -4249,6 +6998,15 @@ "node": ">=4" } }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -4469,6 +7227,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -4478,6 +7242,15 @@ "node": ">=8" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -4487,6 +7260,56 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -4642,6 +7465,24 @@ "node": ">=8" } }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -4792,6 +7633,42 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -4819,6 +7696,21 @@ "node": ">=0.8" } }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4867,6 +7759,15 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -4972,6 +7873,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", @@ -5017,6 +7924,20 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/v8-to-istanbul": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, "node_modules/vite": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.1.tgz", @@ -5072,6 +7993,15 @@ } } }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5263,6 +8193,34 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -5281,6 +8239,53 @@ "node": ">= 14" } }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 8c5756e..9dce126 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -8,13 +8,17 @@ "build": "tsc && vite build", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview", - "format": "prettier --config .prettierrc '**/*.ts' '**/*.tsx' '**/*.js' '**/*.json' --write" + "format": "prettier --config .prettierrc '**/*.ts' '**/*.tsx' '**/*.js' '**/*.json' --write", + "test": "jest" }, "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0" }, "devDependencies": { + "@swc/core": "^1.4.2", + "@swc/jest": "^0.2.36", + "@types/jest": "^29.5.12", "@types/react": "^18.2.55", "@types/react-dom": "^18.2.19", "@typescript-eslint/eslint-plugin": "^6.21.0", @@ -27,8 +31,10 @@ "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.5", + "jest": "^29.7.0", "postcss": "^8.4.35", "prettier": "3.2.5", + "react-test-renderer": "^18.2.0", "tailwindcss": "^3.4.1", "typescript": "^5.2.2", "vite": "^5.1.0" diff --git a/frontend/src/Components/__tests__/CountButton.test.ts b/frontend/src/Components/__tests__/CountButton.test.ts new file mode 100644 index 0000000..ecd577b --- /dev/null +++ b/frontend/src/Components/__tests__/CountButton.test.ts @@ -0,0 +1,12 @@ +import { describe, expect, test } from "@jest/globals"; + +function sum(a: number, b: number): number { + return a + b; +} + +// This is obviously not testing the proper component +describe("sum module", () => { + test("adds 1 + 2 to equal 3", () => { + expect(sum(1, 1)).toBe(2); + }); +}); From c54eccc625b6c8562f2dd49e073315697c9b74df Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sat, 2 Mar 2024 04:18:29 +0100 Subject: [PATCH 038/818] golangci-lint in CI as well as a makefile target --- .github/workflows/golangci-lint.yml | 54 +++++++++++++++++++++++++++++ backend/Makefile | 4 +++ 2 files changed, 58 insertions(+) create mode 100644 .github/workflows/golangci-lint.yml diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 0000000..64587eb --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,54 @@ +name: golangci-lint +on: + push: + branches: [ "master" ] + pull_request: + +permissions: + contents: read + # Optional: allow read access to pull request. Use with `only-new-issues` option. + # pull-requests: read + +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + cache: false + - name: golangci-lint + uses: golangci/golangci-lint-action@v4 + with: + # Require: The version of golangci-lint to use. + # When `install-mode` is `binary` (default) the value can be v1.2 or v1.2.3 or `latest` to use the latest version. + # When `install-mode` is `goinstall` the value can be v1.2.3, `latest`, or the hash of a commit. + version: v1.54 + + # Optional: working directory, useful for monorepos + working-directory: ./backend + + # Optional: golangci-lint command line arguments. + # + # Note: By default, the `.golangci.yml` file should be at the root of the repository. + # The location of the configuration file can be changed by using `--config=` + # args: --timeout=30m --config=/my/path/.golangci.yml --issues-exit-code=0 + + # Optional: show only new issues if it's a pull request. The default value is `false`. + # only-new-issues: true + + # Optional: if set to true, then all caching functionality will be completely disabled, + # takes precedence over all other caching options. + # skip-cache: true + + # Optional: if set to true, then the action won't cache or restore ~/go/pkg. + # skip-pkg-cache: true + + # Optional: if set to true, then the action won't cache or restore ~/.cache/go-build. + # skip-build-cache: true + + # Optional: The mode to install golangci-lint. It can be 'binary' or 'goinstall'. + # install-mode: "goinstall" diff --git a/backend/Makefile b/backend/Makefile index 525356c..23eefa0 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -62,6 +62,10 @@ backup: # Format fmt: $(GOCMD) fmt ./... + +# Lint +lint: + golangci-lint run ./... # Default target default: build From 7a12b946a6efb925985537aeb3b2639a7cac5fb1 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sat, 2 Mar 2024 04:29:50 +0100 Subject: [PATCH 039/818] Fixing some issues found by the linter --- backend/cmd/main.go | 2 +- backend/internal/database/db.go | 5 ++++- backend/internal/database/db_test.go | 4 +++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/backend/cmd/main.go b/backend/cmd/main.go index b238470..5762bab 100644 --- a/backend/cmd/main.go +++ b/backend/cmd/main.go @@ -15,7 +15,7 @@ func main() { conf, err := config.ReadConfigFromFile("config.toml") if err != nil { conf = config.NewConfig() - conf.WriteConfigToFile("config.toml") + _ = conf.WriteConfigToFile("config.toml") } // Pretty print the current config diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index b850289..6e86641 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -104,6 +104,9 @@ func (d *Db) Migrate(dirname string) error { log.Println("Executed SQL file:", file.Name()) } - tr.Commit() + if tr.Commit() != nil { + return err + } + return nil } diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index 716bde8..6830668 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -36,7 +36,9 @@ func TestDbGetUserId(t *testing.T) { if err != nil { t.Error("setupState failed:", err) } - db.AddUser("test", "password") + if db.AddUser("test", "password") != nil { + t.Error("AddUser failed") + } var id int From 59ab430b06cb0cd1c9bd1a9298a472ec40743fc7 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sat, 2 Mar 2024 04:41:34 +0100 Subject: [PATCH 040/818] Justfile target for a complete test run --- Justfile | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Justfile b/Justfile index eadfabd..d4eb3a3 100644 --- a/Justfile +++ b/Justfile @@ -21,6 +21,14 @@ save-release: build-container-release load-release file: podman load --input {{file}} +# Tests every part of the project +testall: + cd backend && make test + cd backend && make lint + cd frontend && npm i + cd frontend && npm test + cd frontend && npm run lint + # Cleans up everything related to the project clean: remove-podman-containers podman image rm -f ttime-server @@ -33,4 +41,4 @@ clean: remove-podman-containers # Cleans up everything related to podman, not just the project. Make sure you understand what this means. [confirm] podman-clean: - podman system reset --force \ No newline at end of file + podman system reset --force From 9e790696a58e1c301ab04c43ade3f9f853e403a6 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sat, 2 Mar 2024 04:47:12 +0100 Subject: [PATCH 041/818] Remove npm i from justfile testall target --- Justfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Justfile b/Justfile index d4eb3a3..0d99e3a 100644 --- a/Justfile +++ b/Justfile @@ -25,7 +25,6 @@ load-release file: testall: cd backend && make test cd backend && make lint - cd frontend && npm i cd frontend && npm test cd frontend && npm run lint From a22dcb9f4e642f0450afd85627e76d8b21368d0e Mon Sep 17 00:00:00 2001 From: borean Date: Mon, 4 Mar 2024 02:50:02 +0100 Subject: [PATCH 042/818] first push, broken import, WIP --- backend/internal/model/timereport | 19 ++++ backend/internal/model/user.go | 139 ++++++++++++++++++++++++++++++ go.work.sum | 5 ++ 3 files changed, 163 insertions(+) create mode 100644 backend/internal/model/timereport create mode 100644 backend/internal/model/user.go create mode 100644 go.work.sum diff --git a/backend/internal/model/timereport b/backend/internal/model/timereport new file mode 100644 index 0000000..d48c5fb --- /dev/null +++ b/backend/internal/model/timereport @@ -0,0 +1,19 @@ +package model + +type TimeReport struct { + reportId string + projectName string + userName string + userRole string + reportDate string + timeWorked uint64 + isSigned bool + reportStatus string // Example "draft", "signed", "unsigned" +} + +type Project struct { + timeReports []TimeReport + projectName string + projectmembers map[string]User + projectRoles map[string]User +} diff --git a/backend/internal/model/user.go b/backend/internal/model/user.go new file mode 100644 index 0000000..c125a60 --- /dev/null +++ b/backend/internal/model/user.go @@ -0,0 +1,139 @@ +package model + +import ( + "errors" +) + +type Account struct { + fullName string + userName string +} + +type Administrator struct { + projects map[string]Project + Account + ProjectMember // comp +} + +// Administrator reciever functions +func (administrator Administrator) addUser(project *Project, user *User) error { + // WIP + return errors.New("WIP") +} + +func (administrator Administrator) removerUser(project *Project, user *User) error { + // WIP + return errors.New("WIP") +} + +func (administrator Administrator) deleteProject(project *Project) error { + // WIP + return errors.New("WIP") +} + +func (administrator Administrator) changeUserRole(project *Project, user *User) error { + // WIP + return errors.New("WIP") +} + +func (administrator *Administrator) login() error { + // WIP + return errors.New("WIP") +} + +func (administrator *Administrator) logout() error { + // WIP + return errors.New("WIP") +} + +type ProjectManager struct { + managedProjects map[string]Project // projekt som förvaltas av projektledaren + totalTime uint64 // total totalt tid arbetat av projektledaren + Account + ProjectMember // comp +} + +// ProjectManager reciever functions +func (projectManager ProjectManager) signReport(timeReport *TimeReport, user User) error { + // WIP + return errors.New("WIP") +} + +func (projectManager ProjectManager) unsignReport(timeReport *TimeReport, user User) error { + // WIP + return errors.New("WIP") +} + +func (projectManager ProjectManager) getAllReports(project *Project) ([]TimeReport, error) { + // WIP + return project.timeReports, errors.New("WIP") +} + +func (projectManager ProjectManager) assignRole(user *User, project *Project, newRole string) error { + // WIP + return errors.New("WIP") +} + +func (projectManager ProjectManager) removeMember(project *Project, user *User) error { + // WIP + return errors.New("WIP") +} + +func (projectManager ProjectManager) getTotalTime(project *Project) (uint64, error) { + // WIP + return 0, errors.New("WIP") +} + +func (projectManager *ProjectManager) login() error { + // WIP + return errors.New("WIP") +} + +func (projectManager *ProjectManager) logout() error { + // WIP + return errors.New("WIP") +} + +type ProjectMember struct { + timereports []TimeReport + role string // ????? + Account // comp +} + +// User reciever functions + +// function used to create a time report, returning a *TimeReport is questionable +func (ProjectMember *ProjectMember) createTimeReport(Project *Project) (*TimeReport, error) { + // WIP + return &TimeReport{}, errors.New("WIP") +} + +func (ProjectMember ProjectMember) getTimeReport(timereports *[]TimeReport) (*TimeReport, error) { + // WIP + return &TimeReport{}, errors.New("WIP") +} + +func (ProjectMember *ProjectMember) editTimeReport(timereport *TimeReport) { + // timereport.editReport() + // WIP +} + +func (projectUser ProjectMember) deleteTimeReport(timeReport *TimeReport) error { // Ska bara project manager kunna göra detta? fråga ledarna! + // WIP + return errors.New("WIP") +} + +func (projectUser *ProjectMember) login() error { + // WIP + return errors.New("WIP") +} + +func (projectUser *ProjectMember) logout() error { + // WIP + return errors.New("WIP") +} + +type User interface { + login() + logout() +} diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 0000000..f0a8e9c --- /dev/null +++ b/go.work.sum @@ -0,0 +1,5 @@ +github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= +github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= From ae9ee91bc4bd809f97bf71f6958b0a9fa26987cd Mon Sep 17 00:00:00 2001 From: borean Date: Mon, 4 Mar 2024 19:34:35 +0100 Subject: [PATCH 043/818] file correction --- backend/internal/model/timereport.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 backend/internal/model/timereport.go diff --git a/backend/internal/model/timereport.go b/backend/internal/model/timereport.go new file mode 100644 index 0000000..d48c5fb --- /dev/null +++ b/backend/internal/model/timereport.go @@ -0,0 +1,19 @@ +package model + +type TimeReport struct { + reportId string + projectName string + userName string + userRole string + reportDate string + timeWorked uint64 + isSigned bool + reportStatus string // Example "draft", "signed", "unsigned" +} + +type Project struct { + timeReports []TimeReport + projectName string + projectmembers map[string]User + projectRoles map[string]User +} From 9d39ff5bc2831619fe9247a501ad966463d79fc2 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 6 Mar 2024 09:41:36 +0100 Subject: [PATCH 044/818] Re-implemented demo button --- backend/cmd/main.go | 4 ++++ backend/internal/handlers/global_state.go | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/backend/cmd/main.go b/backend/cmd/main.go index 5762bab..b0cee26 100644 --- a/backend/cmd/main.go +++ b/backend/cmd/main.go @@ -36,6 +36,10 @@ func main() { // Register our handlers server.Post("/api/register", gs.Register) + // Register handlers for example button count + server.Get("/api/button", gs.GetButtonCount) + server.Post("/api/button", gs.IncrementButtonCount) + // Announce the port we are listening on and start the server err = server.Listen(fmt.Sprintf(":%d", conf.Port)) if err != nil { diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go index 4dae3d8..6c8a9dc 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -10,16 +10,19 @@ import ( // The actual interface that we will use type GlobalState interface { Register(c *fiber.Ctx) error + GetButtonCount(c *fiber.Ctx) error + IncrementButtonCount(c *fiber.Ctx) error } // "Constructor" func NewGlobalState(db database.Database) GlobalState { - return &GState{Db: db} + return &GState{Db: db, ButtonCount: 0} } // The global state, which implements all the handlers type GState struct { - Db database.Database + Db database.Database + ButtonCount int } func (gs *GState) Register(c *fiber.Ctx) error { @@ -34,3 +37,12 @@ func (gs *GState) Register(c *fiber.Ctx) error { return c.Status(200).SendString("User added") } + +func (gs *GState) GetButtonCount(c *fiber.Ctx) error { + return c.Status(200).JSON(fiber.Map{"pressCount": gs.ButtonCount}) +} + +func (gs *GState) IncrementButtonCount(c *fiber.Ctx) error { + gs.ButtonCount++ + return c.Status(200).JSON(fiber.Map{"pressCount": gs.ButtonCount}) +} From 4878ce1c398d0b6dc23119f7c295943cea5ecd37 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 6 Mar 2024 09:51:26 +0100 Subject: [PATCH 045/818] Useless fix for the vite default template with tailwind --- frontend/src/App.css | 4 ++++ frontend/src/App.tsx | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/frontend/src/App.css b/frontend/src/App.css index b9d355d..bb6c55a 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -5,6 +5,10 @@ text-align: center; } +a { + display:inline-block; +} + .logo { height: 6em; padding: 1.5em; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 20e5f1f..2722ed9 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -8,16 +8,16 @@ function App(): JSX.Element { <>
- Vite logo + Vite logo - React logo + React logo

Vite + React

-

+

Edit src/App.tsx and save to test HMR

From 56693350bea3b716a5a66b97f0739cf2607706ce Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 6 Mar 2024 10:09:59 +0100 Subject: [PATCH 046/818] Pulling in and configuring react-router-dom --- frontend/package-lock.json | 100 ++++++++++++++++++++++++++++++++++++- frontend/package.json | 6 ++- frontend/src/main.tsx | 10 +++- 3 files changed, 113 insertions(+), 3 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5131784..eecde70 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,8 +8,12 @@ "name": "tmp", "version": "0.0.0", "dependencies": { + "localforage": "^1.10.0", + "match-sorter": "^6.3.4", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.22.2", + "sort-by": "^0.0.2" }, "devDependencies": { "@swc/core": "^1.4.2", @@ -670,6 +674,17 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz", + "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.24.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", @@ -1782,6 +1797,14 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@remix-run/router": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.2.tgz", + "integrity": "sha512-+Rnav+CaoTE5QJc4Jcwh5toUpnVLKYbpU6Ys0zqbakqbaLQHeglLVHPfxOiQqdNmUy5C2lXz5dwC6tQNX2JW2Q==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.10.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.10.0.tgz", @@ -4644,6 +4667,11 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -5898,6 +5926,14 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -5913,6 +5949,14 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "dependencies": { + "lie": "3.1.1" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -5981,6 +6025,15 @@ "tmpl": "1.0.5" } }, + "node_modules/match-sorter": { + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.4.tgz", + "integrity": "sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==", + "dependencies": { + "@babel/runtime": "^7.23.8", + "remove-accents": "0.5.0" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -6849,6 +6902,36 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "node_modules/react-router": { + "version": "6.22.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.2.tgz", + "integrity": "sha512-YD3Dzprzpcq+tBMHBS822tCjnWD3iIZbTeSXMY9LPSG541EfoBGyZ3bS25KEnaZjLcmQpw2AVLkFyfgXY8uvcw==", + "dependencies": { + "@remix-run/router": "1.15.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.22.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.2.tgz", + "integrity": "sha512-WgqxD2qySEIBPZ3w0sHH+PUAiamDeszls9tzqMPBDA1YYVucTBXLU7+gtRfcSnhe92A3glPnvSxK2dhNoAVOIQ==", + "dependencies": { + "@remix-run/router": "1.15.2", + "react-router": "6.22.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/react-shallow-renderer": { "version": "16.15.0", "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz", @@ -6924,6 +7007,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, "node_modules/regexp.prototype.flags": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", @@ -6942,6 +7030,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/remove-accents": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", + "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==" + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7242,6 +7335,11 @@ "node": ">=8" } }, + "node_modules/sort-by": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/sort-by/-/sort-by-0.0.2.tgz", + "integrity": "sha512-iOX5oHA4a0eqTMFiWrHYqv924UeRKFBLhym7iwSVG37Egg2wApgZKAjyzM9WZjMwKv6+8Zi+nIaJ7FYsO9EkoA==" + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 9dce126..e8d78c1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,8 +12,12 @@ "test": "jest" }, "dependencies": { + "localforage": "^1.10.0", + "match-sorter": "^6.3.4", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.22.2", + "sort-by": "^0.0.2" }, "devDependencies": { "@swc/core": "^1.4.2", diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 05dff10..b980985 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -2,11 +2,19 @@ import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App.tsx"; import "./index.css"; +import { createBrowserRouter, RouterProvider } from "react-router-dom"; + +const router = createBrowserRouter([ + { + path: "/", + element: , + }, +]); const root = document.getElementById("root") ?? document.createElement("div"); ReactDOM.createRoot(root).render( - + , ); From 70ae3598567beedc3e6351cb73bc977e30c6fe07 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 6 Mar 2024 10:14:54 +0100 Subject: [PATCH 047/818] Example of how to extend the API --- backend/cmd/main.go | 1 + backend/internal/handlers/global_state.go | 40 +++++++++++++++++++++-- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/backend/cmd/main.go b/backend/cmd/main.go index b0cee26..2593019 100644 --- a/backend/cmd/main.go +++ b/backend/cmd/main.go @@ -35,6 +35,7 @@ func main() { // Register our handlers server.Post("/api/register", gs.Register) + server.Delete("/api/userdelete", gs.UserDelete) // Perhaps just use POST to avoid headaches // Register handlers for example button count server.Get("/api/button", gs.GetButtonCount) diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go index 6c8a9dc..2d3e9f6 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -9,9 +9,28 @@ import ( // The actual interface that we will use type GlobalState interface { - Register(c *fiber.Ctx) error - GetButtonCount(c *fiber.Ctx) error - IncrementButtonCount(c *fiber.Ctx) error + Register(c *fiber.Ctx) error // To register a new user + UserDelete(c *fiber.Ctx) error // To delete a user + // LoginPost(c *fiber.Ctx) error // To get the token + // LoginUpdate(c *fiber.Ctx) error // To renew the token + // CreateProject(c *fiber.Ctx) error // To create a new project + // GetProjects(c *fiber.Ctx) error // To get all projects + // GetProject(c *fiber.Ctx) error // To get a specific project + // UpdateProject(c *fiber.Ctx) error // To update a project + // DeleteProject(c *fiber.Ctx) error // To delete a project + // CreateTask(c *fiber.Ctx) error // To create a new task + // GetTasks(c *fiber.Ctx) error // To get all tasks + // GetTask(c *fiber.Ctx) error // To get a specific task + // UpdateTask(c *fiber.Ctx) error // To update a task + // DeleteTask(c *fiber.Ctx) error // To delete a task + // CreateCollection(c *fiber.Ctx) error // To create a new collection + // GetCollections(c *fiber.Ctx) error // To get all collections + // GetCollection(c *fiber.Ctx) error // To get a specific collection + // UpdateCollection(c *fiber.Ctx) error // To update a collection + // DeleteCollection(c *fiber.Ctx) error // To delete a collection + // SignCollection(c *fiber.Ctx) error // To sign a collection + GetButtonCount(c *fiber.Ctx) error // For demonstration purposes + IncrementButtonCount(c *fiber.Ctx) error // For demonstration purposes } // "Constructor" @@ -38,6 +57,21 @@ func (gs *GState) Register(c *fiber.Ctx) error { return c.Status(200).SendString("User added") } +// This path should obviously be protected in the future +// UserDelete deletes a user from the database +func (gs *GState) UserDelete(c *fiber.Ctx) error { + u := new(types.User) + if err := c.BodyParser(u); err != nil { + return c.Status(400).SendString(err.Error()) + } + + if err := gs.Db.RemoveUser(u.Username); err != nil { + return c.Status(500).SendString(err.Error()) + } + + return c.Status(200).SendString("User deleted") +} + func (gs *GState) GetButtonCount(c *fiber.Ctx) error { return c.Status(200).JSON(fiber.Map{"pressCount": gs.ButtonCount}) } From 43afae5d77a708e074c913a71e324877fd316b44 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 6 Mar 2024 11:14:08 +0100 Subject: [PATCH 048/818] Cleaning up CSS, minor refactor into pages Home & Settings, better docs --- frontend/src/App.css | 46 ------------------------------ frontend/src/App.tsx | 31 -------------------- frontend/src/Pages/Home.tsx | 36 ++++++++++++++++++++++++ frontend/src/Pages/Settings.tsx | 17 +++++++++++ frontend/src/index.css | 50 +++++++++++++++++++++++++++++++++ frontend/src/main.tsx | 12 ++++++-- 6 files changed, 113 insertions(+), 79 deletions(-) delete mode 100644 frontend/src/App.css delete mode 100644 frontend/src/App.tsx create mode 100644 frontend/src/Pages/Home.tsx create mode 100644 frontend/src/Pages/Settings.tsx diff --git a/frontend/src/App.css b/frontend/src/App.css deleted file mode 100644 index bb6c55a..0000000 --- a/frontend/src/App.css +++ /dev/null @@ -1,46 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -a { - display:inline-block; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx deleted file mode 100644 index 2722ed9..0000000 --- a/frontend/src/App.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import reactLogo from "./assets/react.svg"; -import viteLogo from "/vite.svg"; -import "./App.css"; -import { CountButton } from "./Components/CountButton"; - -function App(): JSX.Element { - return ( - <> -
- - Vite logo - - - React logo - -
-

Vite + React

-
- -

- Edit src/App.tsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- - ); -} - -export default App; diff --git a/frontend/src/Pages/Home.tsx b/frontend/src/Pages/Home.tsx new file mode 100644 index 0000000..7ce73a6 --- /dev/null +++ b/frontend/src/Pages/Home.tsx @@ -0,0 +1,36 @@ +import reactLogo from "../assets/react.svg"; +import viteLogo from "/vite.svg"; +import "../index.css"; +import { CountButton } from "../Components/CountButton"; +import { Link } from "react-router-dom"; + +/** + * The home page of the application + * @returns {JSX.Element} The home page + */ +export default function HomePage(): JSX.Element { + return ( + <> +
+ + Vite logo + + + React logo + +
+

Vite + React

+
+ + To Settings +
+

+ Click on the Vite and React logos to learn more +

+ + ); +} diff --git a/frontend/src/Pages/Settings.tsx b/frontend/src/Pages/Settings.tsx new file mode 100644 index 0000000..b5bf81c --- /dev/null +++ b/frontend/src/Pages/Settings.tsx @@ -0,0 +1,17 @@ +import "../index.css"; +import { Link } from "react-router-dom"; + +/** + * The settings page of the application + * @returns {JSX.Element} The settings page + */ +export default function SettingsPage(): JSX.Element { + return ( + <> +

Very Fancy Settings Page

+
+ To Home +
+ + ); +} diff --git a/frontend/src/index.css b/frontend/src/index.css index e7d4bb2..94ce567 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -2,6 +2,11 @@ @tailwind components; @tailwind utilities; +/* + * We are using tailwind, so do not add any custom CSS here. + * Most of this is going to get cleaned up eventually. + */ + :root { font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; @@ -70,3 +75,48 @@ button:focus-visible { background-color: #f9f9f9; } } + +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +a { + display: inline-block; +} + +.logo { + transition: filter 0.25s; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index b980985..cefddf1 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1,18 +1,26 @@ import React from "react"; import ReactDOM from "react-dom/client"; -import App from "./App.tsx"; +import SettingsPage from "./Pages/Settings.tsx"; +import HomePage from "./Pages/Home.tsx"; import "./index.css"; import { createBrowserRouter, RouterProvider } from "react-router-dom"; +// This is where the routes are mounted const router = createBrowserRouter([ { path: "/", - element: , + element: , + }, + { + path: "/settings", + element: , }, ]); +// Semi-hacky way to get the root element const root = document.getElementById("root") ?? document.createElement("div"); +// Render the router at the root ReactDOM.createRoot(root).render( From dcd9879d8d10ce1acd1d1b7bfc34f15ef8843dfa Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 6 Mar 2024 11:14:57 +0100 Subject: [PATCH 049/818] Demo of components with children and other props --- frontend/src/Components/TaskList.tsx | 36 +++++++++++++++++++++++ frontend/src/Containers/ContainerDemo.tsx | 24 +++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 frontend/src/Components/TaskList.tsx create mode 100644 frontend/src/Containers/ContainerDemo.tsx diff --git a/frontend/src/Components/TaskList.tsx b/frontend/src/Components/TaskList.tsx new file mode 100644 index 0000000..1e9cb76 --- /dev/null +++ b/frontend/src/Components/TaskList.tsx @@ -0,0 +1,36 @@ +/** + * The shape of a task + */ +interface Task { + id: number; + name: string; +} + +/** + * The props for the TaskList component + */ +interface TaskProps { + tasks: Task[]; +} + +/** + * A simple list of tasks + * @param props - The tasks to display + * @returns {JSX.Element} The task list + * @example + * const tasks = [{ id: 1, name: "Do the thing" }]; + * return ; + */ +export function TaskList(props: TaskProps): JSX.Element { + return ( +
+

Task List

+

Tasks go here

+
    + {props.tasks.map((task) => ( +
  • {task.name}
  • + ))} +
+
+ ); +} diff --git a/frontend/src/Containers/ContainerDemo.tsx b/frontend/src/Containers/ContainerDemo.tsx new file mode 100644 index 0000000..ac1cfc6 --- /dev/null +++ b/frontend/src/Containers/ContainerDemo.tsx @@ -0,0 +1,24 @@ +import { ReactNode } from "react"; + +/** The props for the container */ +interface ContainerProps { + children: ReactNode; +} + +/** + * Contains children + * @param props - The children to contain + * @returns {JSX.Element} The container + */ +export function Container(props: ContainerProps): JSX.Element { + return
{props.children}
; +} + +/** + * Contains even more children + * @param props + * @returns {JSX.Element} + */ +export function Container2(props: ContainerProps): JSX.Element { + return {props.children}; +} From 85d080cebb9f9acf2f049d3d9e4845b0b303e78c Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 6 Mar 2024 11:24:53 +0100 Subject: [PATCH 050/818] Set fiber as direct dependency --- backend/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/go.mod b/backend/go.mod index fdbbaf7..88c21c3 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -11,7 +11,7 @@ require ( // These are all for fiber require ( github.com/andybalholm/brotli v1.0.5 // indirect - github.com/gofiber/fiber/v2 v2.52.1 // indirect + github.com/gofiber/fiber/v2 v2.52.1 github.com/google/uuid v1.5.0 // indirect github.com/klauspost/compress v1.17.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect From f484674524ad3232a1bc1b9c848baa3e1feff6bf Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 6 Mar 2024 12:51:00 +0100 Subject: [PATCH 051/818] Pulled in jwt middleware for fiber --- backend/go.mod | 20 +++++++++++++------- backend/go.sum | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/backend/go.mod b/backend/go.mod index 88c21c3..ab9be66 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -8,18 +8,24 @@ require ( github.com/mattn/go-sqlite3 v1.14.22 ) +require ( + github.com/MicahParks/keyfunc/v2 v2.1.0 // indirect + github.com/gofiber/contrib/jwt v1.0.8 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect +) + // These are all for fiber require ( - github.com/andybalholm/brotli v1.0.5 // indirect - github.com/gofiber/fiber/v2 v2.52.1 - github.com/google/uuid v1.5.0 // indirect - github.com/klauspost/compress v1.17.0 // indirect + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/gofiber/fiber/v2 v2.52.2 + github.com/google/uuid v1.6.0 // indirect + github.com/klauspost/compress v1.17.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.51.0 // indirect + github.com/valyala/fasthttp v1.52.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/sys v0.18.0 // indirect ) diff --git a/backend/go.sum b/backend/go.sum index 9ddb540..42908f6 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -1,17 +1,31 @@ github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/MicahParks/keyfunc/v2 v2.1.0 h1:6ZXKb9Rp6qp1bDbJefnG7cTH8yMN1IC/4nf+GVjO99k= +github.com/MicahParks/keyfunc/v2 v2.1.0/go.mod h1:rW42fi+xgLJ2FRRXAfNx9ZA8WpD4OeE/yHVMteCkw9k= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/gofiber/contrib/jwt v1.0.8 h1:/GeOsm/Mr1OGr0GTy+RIVSz5VgNNyP3ZgK4wdqxF/WY= +github.com/gofiber/contrib/jwt v1.0.8/go.mod h1:gWWBtBiLmKXRN7xy6a96QO0KGvPEyxdh8x496Ujtg84= github.com/gofiber/fiber/v2 v2.52.1 h1:1RoU2NS+b98o1L77sdl5mboGPiW+0Ypsi5oLmcYlgHI= github.com/gofiber/fiber/v2 v2.52.1/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= +github.com/gofiber/fiber/v2 v2.52.2 h1:b0rYH6b06Df+4NyrbdptQL8ifuxw/Tf2DgfkZkDaxEo= +github.com/gofiber/fiber/v2 v2.52.2/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -26,13 +40,19 @@ github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= +github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0= +github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= From d023bb0adff8059bd9495ecbdb1baec44f297bf1 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 6 Mar 2024 12:51:46 +0100 Subject: [PATCH 052/818] Untested implementation of login and renew endpoints --- backend/cmd/main.go | 15 ++++++- backend/internal/handlers/global_state.go | 54 ++++++++++++++++++++++- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/backend/cmd/main.go b/backend/cmd/main.go index 2593019..bae7a83 100644 --- a/backend/cmd/main.go +++ b/backend/cmd/main.go @@ -9,6 +9,8 @@ import ( "github.com/gofiber/fiber/v2" _ "github.com/mattn/go-sqlite3" + + jwtware "github.com/gofiber/contrib/jwt" ) func main() { @@ -33,14 +35,23 @@ func main() { // This will likely be replaced by an embedded filesystem in the future server.Static("/", "./static") - // Register our handlers + // Register our unprotected routes server.Post("/api/register", gs.Register) - server.Delete("/api/userdelete", gs.UserDelete) // Perhaps just use POST to avoid headaches // Register handlers for example button count server.Get("/api/button", gs.GetButtonCount) server.Post("/api/button", gs.IncrementButtonCount) + server.Post("/api/login", gs.Login) + + // Every route from here on will require a valid JWT + server.Use(jwtware.New(jwtware.Config{ + SigningKey: jwtware.SigningKey{Key: []byte("secret")}, + })) + + server.Post("/api/loginrenew", gs.LoginRenew) + server.Delete("/api/userdelete", gs.UserDelete) // Perhaps just use POST to avoid headaches + // Announce the port we are listening on and start the server err = server.Listen(fmt.Sprintf(":%d", conf.Port)) if err != nil { diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go index 2d3e9f6..5dc8895 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -1,18 +1,20 @@ package handlers import ( + "time" "ttime/internal/database" "ttime/internal/types" "github.com/gofiber/fiber/v2" + "github.com/golang-jwt/jwt/v5" ) // The actual interface that we will use type GlobalState interface { Register(c *fiber.Ctx) error // To register a new user UserDelete(c *fiber.Ctx) error // To delete a user - // LoginPost(c *fiber.Ctx) error // To get the token - // LoginUpdate(c *fiber.Ctx) error // To renew the token + Login(c *fiber.Ctx) error // To get the token + LoginRenew(c *fiber.Ctx) error // To renew the token // CreateProject(c *fiber.Ctx) error // To create a new project // GetProjects(c *fiber.Ctx) error // To get all projects // GetProject(c *fiber.Ctx) error // To get a specific project @@ -80,3 +82,51 @@ func (gs *GState) IncrementButtonCount(c *fiber.Ctx) error { gs.ButtonCount++ return c.Status(200).JSON(fiber.Map{"pressCount": gs.ButtonCount}) } + +// Login is a simple login handler that returns a JWT token +func (gs *GState) Login(c *fiber.Ctx) error { + // To test: curl --data "user=user&pass=pass" http://localhost:8080/api/login + user := c.FormValue("user") + pass := c.FormValue("pass") + + // Throws Unauthorized error + if user != "user" || pass != "pass" { + return c.SendStatus(fiber.StatusUnauthorized) + } + + // Create the Claims + claims := jwt.MapClaims{ + "name": user, + "admin": false, + "exp": time.Now().Add(time.Hour * 72).Unix(), + } + + // Create token + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + + // Generate encoded token and send it as response. + t, err := token.SignedString([]byte("secret")) + if err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + + return c.JSON(fiber.Map{"token": t}) +} + +// LoginRenew is a simple handler that renews the token +func (gs *GState) LoginRenew(c *fiber.Ctx) error { + user := c.Locals("user").(*jwt.Token) + claims := user.Claims.(jwt.MapClaims) + claims["exp"] = time.Now().Add(time.Hour * 72).Unix() + renewed := jwt.MapClaims{ + "name": claims["name"], + "admin": claims["admin"], + "exp": claims["exp"], + } + token := jwt.NewWithClaims(jwt.SigningMethodHS256, renewed) + t, err := token.SignedString([]byte("secret")) + if err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + return c.JSON(fiber.Map{"token": t}) +} From c5f0bc509e484ac0f8114f7e03a3cba15e87c370 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 6 Mar 2024 15:15:41 +0100 Subject: [PATCH 053/818] Makefile target for installing just (hack of doom) --- backend/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/Makefile b/backend/Makefile index 23eefa0..dcc79b4 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -69,3 +69,7 @@ lint: # Default target default: build + +install-just: + @echo "Installing just" + @curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin From 524baf90d28ca6006884629c9030de02710b250e Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 6 Mar 2024 15:34:43 +0100 Subject: [PATCH 054/818] Hotfix for just errors related to podman --- Justfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Justfile b/Justfile index 0d99e3a..c23909d 100644 --- a/Justfile +++ b/Justfile @@ -11,7 +11,7 @@ start-release: build-container-release remove-podman-containers # Removes and stops any containers related to the project [private] remove-podman-containers: - podman container rm -f ttime + podman container rm -fi ttime # Saves the release container to a tarball, pigz is just gzip but multithreaded save-release: build-container-release @@ -30,7 +30,7 @@ testall: # Cleans up everything related to the project clean: remove-podman-containers - podman image rm -f ttime-server + podman image rm -fi ttime-server rm -rf frontend/dist rm -rf frontend/node_modules rm -f ttime-server.tar.gz From 4cfb227a1e2650fc69dcab5af071418edb99dae6 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 6 Mar 2024 15:40:24 +0100 Subject: [PATCH 055/818] Add install linter target --- Justfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Justfile b/Justfile index c23909d..cf8525c 100644 --- a/Justfile +++ b/Justfile @@ -41,3 +41,6 @@ clean: remove-podman-containers [confirm] podman-clean: podman system reset --force + +install-linter: + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.56.2 From b31349ec37abbac098a7a61ebfc6da768c97e86f Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 6 Mar 2024 15:44:18 +0100 Subject: [PATCH 056/818] Change test order in testall target --- Justfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Justfile b/Justfile index cf8525c..432fbd1 100644 --- a/Justfile +++ b/Justfile @@ -23,10 +23,10 @@ load-release file: # Tests every part of the project testall: - cd backend && make test - cd backend && make lint cd frontend && npm test cd frontend && npm run lint + cd backend && make test + cd backend && make lint # Cleans up everything related to the project clean: remove-podman-containers From 7879394da326d426780d933f939c5adee320e1d8 Mon Sep 17 00:00:00 2001 From: Mattias Date: Thu, 7 Mar 2024 10:05:45 +0100 Subject: [PATCH 057/818] Added a few pages and components to the frontend --- frontend/index.html | 4 +- frontend/src/Components/BasicWindow.tsx | 18 +++++++ frontend/src/Components/Button.tsx | 10 ++++ frontend/src/Components/Footer.tsx | 13 +++++ frontend/src/Components/Header.tsx | 41 ++++++++++++++++ frontend/src/Pages/LoginPage.css | 26 ++++++++++ frontend/src/Pages/LoginPage.tsx | 45 ++++++++++++++++++ .../ProjectManagerPages/PMProjectPage.tsx | 26 ++++++++++ .../src/Pages/UserPages/UserProjectPage.tsx | 29 +++++++++++ frontend/src/Pages/YourProjectsPage.tsx | 27 +++++++++++ frontend/src/assets/1.jpg | Bin 0 -> 678376 bytes frontend/src/assets/2.jpg | Bin 0 -> 674675 bytes frontend/src/assets/3.jpg | Bin 0 -> 683724 bytes frontend/src/assets/4.jpg | Bin 0 -> 681330 bytes frontend/src/assets/TTIMElogo.png | Bin 0 -> 267203 bytes frontend/src/assets/favicon.ico | Bin 0 -> 15406 bytes frontend/src/index.css | 4 +- frontend/src/main.tsx | 13 +++-- 18 files changed, 249 insertions(+), 7 deletions(-) create mode 100644 frontend/src/Components/BasicWindow.tsx create mode 100644 frontend/src/Components/Button.tsx create mode 100644 frontend/src/Components/Footer.tsx create mode 100644 frontend/src/Components/Header.tsx create mode 100644 frontend/src/Pages/LoginPage.css create mode 100644 frontend/src/Pages/LoginPage.tsx create mode 100644 frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx create mode 100644 frontend/src/Pages/UserPages/UserProjectPage.tsx create mode 100644 frontend/src/Pages/YourProjectsPage.tsx create mode 100644 frontend/src/assets/1.jpg create mode 100644 frontend/src/assets/2.jpg create mode 100644 frontend/src/assets/3.jpg create mode 100644 frontend/src/assets/4.jpg create mode 100644 frontend/src/assets/TTIMElogo.png create mode 100644 frontend/src/assets/favicon.ico diff --git a/frontend/index.html b/frontend/index.html index e4b78ea..573ba58 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,9 +2,9 @@ - + - Vite + React + TS + TTIME
diff --git a/frontend/src/Components/BasicWindow.tsx b/frontend/src/Components/BasicWindow.tsx new file mode 100644 index 0000000..9899fa1 --- /dev/null +++ b/frontend/src/Components/BasicWindow.tsx @@ -0,0 +1,18 @@ +import Header from './Header'; +import Footer from './Footer'; + +function BasicWindow({ username, content, buttons }: { username: string, content: React.ReactNode, buttons:React.ReactNode }) { + return ( +
+
+
+ {content} +
+
+ {buttons} +
+
+ ); +} + +export default BasicWindow; \ No newline at end of file diff --git a/frontend/src/Components/Button.tsx b/frontend/src/Components/Button.tsx new file mode 100644 index 0000000..a00f5e1 --- /dev/null +++ b/frontend/src/Components/Button.tsx @@ -0,0 +1,10 @@ +function Button({text, onClick}: {text: string; onClick: () => void;}){ + return +} + +export default Button; \ No newline at end of file diff --git a/frontend/src/Components/Footer.tsx b/frontend/src/Components/Footer.tsx new file mode 100644 index 0000000..f1c7e29 --- /dev/null +++ b/frontend/src/Components/Footer.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +function Footer({ children }: { children: React.ReactNode }) { + return ( +
+
+ {children} +
+
+ ); +} + +export default Footer; \ No newline at end of file diff --git a/frontend/src/Components/Header.tsx b/frontend/src/Components/Header.tsx new file mode 100644 index 0000000..fde1fbf --- /dev/null +++ b/frontend/src/Components/Header.tsx @@ -0,0 +1,41 @@ +import React, { useState } from 'react'; +import { Link } from 'react-router-dom'; + + +function Header({ username }: { username: string }) { + const [isOpen, setIsOpen] = useState(false); + + const handleLogout = () => { + // Add any logout logic here + }; + + return ( +
+ + TTIME Logo + + +
setIsOpen(true)} + onMouseLeave={() => setIsOpen(false)} + > + + + {isOpen && ( +
+ + + +
+ )} +
+
+ ); + } + +export default Header; + diff --git a/frontend/src/Pages/LoginPage.css b/frontend/src/Pages/LoginPage.css new file mode 100644 index 0000000..0b2f5db --- /dev/null +++ b/frontend/src/Pages/LoginPage.css @@ -0,0 +1,26 @@ +body{ + overflow: hidden; +} + +@keyframes backgroundTransition { + 0% { + background-image: url('src/assets/1.jpg'); + animation-timing-function: ease-out; + } + 25% { + background-image: url('src/assets/2.jpg'); + animation-timing-function: ease-in; + } + 50% { + background-image: url('src/assets/3.jpg'); + animation-timing-function: ease-out; + } + 75% { + background-image: url('src/assets/4.jpg'); + animation-timing-function: ease-in; + } + 100% { + background-image: url('src/assets/1.jpg'); + animation-timing-function: ease-out; + } +} \ No newline at end of file diff --git a/frontend/src/Pages/LoginPage.tsx b/frontend/src/Pages/LoginPage.tsx new file mode 100644 index 0000000..bbee6b8 --- /dev/null +++ b/frontend/src/Pages/LoginPage.tsx @@ -0,0 +1,45 @@ +import Button from "../Components/Button"; +import Logo from "/src/assets/TTIMElogo.png"; +import './LoginPage.css'; +import { useEffect } from "react"; +import { Link } from "react-router-dom"; + +const PreloadBackgroundAnimation = () => { + useEffect(() => { + const images = ['src/assets/1.jpg', 'src/assets/2.jpg', 'src/assets/3.jpg', 'src/assets/4.jpg']; + + // Pre-load images + for (let i = 0; i < images.length; i++) { + const img = new Image(); + img.src = images[i]; + } + // Start animation + document.body.style.animation = "backgroundTransition 30s infinite"; + }, []); + + return null; + }; + +function LoginPage() { + + return ( + <> + +
+
+ TTIME Logo +

Welcome to TTIME!

+

Please log in to continue

+ + + +
+
+ + ); +} + +export default LoginPage; \ No newline at end of file diff --git a/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx b/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx new file mode 100644 index 0000000..80f35ad --- /dev/null +++ b/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx @@ -0,0 +1,26 @@ +import BasicWindow from "../../Components/BasicWindow"; +import Button from "../../Components/Button"; + +function PMProjectPage() { + + const content = +<> +

ProjectNameExample

+
+

Your Time Reports

+

New Time Report

+

Statistics

+

Unsigned Time Reports

+
+ + + const buttons = + <> + +function Button({ + text, + onClick, +}: { + text: string; + onClick: () => void; +}): JSX.Element { + return ( + + ); } -export default Button; \ No newline at end of file +export default Button; diff --git a/frontend/src/Components/Footer.tsx b/frontend/src/Components/Footer.tsx index f1c7e29..a3b7469 100644 --- a/frontend/src/Components/Footer.tsx +++ b/frontend/src/Components/Footer.tsx @@ -1,13 +1,13 @@ -import React from 'react'; +import React from "react"; -function Footer({ children }: { children: React.ReactNode }) { +function Footer({ children }: { children: React.ReactNode }): JSX.Element { return (
-
+
{children}
); } -export default Footer; \ No newline at end of file +export default Footer; diff --git a/frontend/src/Components/Header.tsx b/frontend/src/Components/Header.tsx index fde1fbf..5c642b8 100644 --- a/frontend/src/Components/Header.tsx +++ b/frontend/src/Components/Header.tsx @@ -1,41 +1,54 @@ -import React, { useState } from 'react'; -import { Link } from 'react-router-dom'; +import { useState } from "react"; +import { Link } from "react-router-dom"; - -function Header({ username }: { username: string }) { +function Header({ username }: { username: string }): JSX.Element { const [isOpen, setIsOpen] = useState(false); - const handleLogout = () => { + const handleLogout = (): void => { // Add any logout logic here }; return ( -
+
- TTIME Logo + TTIME Logo - -
setIsOpen(true)} - onMouseLeave={() => setIsOpen(false)} + onMouseEnter={() => { + setIsOpen(true); + }} + onMouseLeave={() => { + setIsOpen(false); + }} > - + {isOpen && (
- +
)}
); - } +} export default Header; - diff --git a/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx b/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx index 80f35ad..009a5bd 100644 --- a/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx +++ b/frontend/src/Pages/ProjectManagerPages/PMProjectPage.tsx @@ -1,26 +1,38 @@ import BasicWindow from "../../Components/BasicWindow"; import Button from "../../Components/Button"; -function PMProjectPage() { - - const content = -<> -

ProjectNameExample

-
-

Your Time Reports

-

New Time Report

-

Statistics

-

Unsigned Time Reports

-
- - - const buttons = - <> -
+ + {error &&

{error}

} + +

+ + + ); +} diff --git a/frontend/src/Pages/LoginPage.tsx b/frontend/src/Pages/LoginPage.tsx index d8ea651..11a7da2 100644 --- a/frontend/src/Pages/LoginPage.tsx +++ b/frontend/src/Pages/LoginPage.tsx @@ -3,6 +3,7 @@ import Logo from "/src/assets/TTIMElogo.png"; import "./LoginPage.css"; import { useEffect } from "react"; import { Link } from "react-router-dom"; +import Register from "../Components/Register"; const PreloadBackgroundAnimation = (): JSX.Element => { useEffect(() => { @@ -69,6 +70,14 @@ function LoginPage(): JSX.Element { }} /> + + diff --git a/frontend/src/Components/Register.tsx b/frontend/src/Components/Register.tsx index e8d93c5..c873a0e 100644 --- a/frontend/src/Components/Register.tsx +++ b/frontend/src/Components/Register.tsx @@ -66,7 +66,7 @@ export default function Register(): JSX.Element {
From 855dccdfa4bb6a52b7f2d2257ac9b4e4dec3451f Mon Sep 17 00:00:00 2001 From: Davenludd Date: Fri, 15 Mar 2024 14:30:53 +0100 Subject: [PATCH 176/818] Add type="button" to buttons --- frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx | 2 ++ frontend/src/Pages/UserPages/UserNewTimeReportPage.tsx | 2 ++ frontend/src/Pages/UserPages/UserProjectPage.tsx | 1 + frontend/src/Pages/UserPages/UserViewTimeReportsPage.tsx | 1 + 4 files changed, 6 insertions(+) diff --git a/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx b/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx index de1699b..af3bbc1 100644 --- a/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx +++ b/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx @@ -17,12 +17,14 @@ function UserEditTimeReportPage(): JSX.Element { onClick={(): void => { return; }} + type="button" /> + ); +} + +export default BackButton; From 49209663886040d52fb7554e37c1523c8b6ffe94 Mon Sep 17 00:00:00 2001 From: Mattias Date: Fri, 15 Mar 2024 14:52:05 +0100 Subject: [PATCH 181/818] Additional register-layout changes --- frontend/src/Components/Register.tsx | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/frontend/src/Components/Register.tsx b/frontend/src/Components/Register.tsx index c873a0e..e4a3ba0 100644 --- a/frontend/src/Components/Register.tsx +++ b/frontend/src/Components/Register.tsx @@ -2,6 +2,7 @@ import { useState } from "react"; import { NewUser } from "../Types/Users"; import { api } from "../API/API"; import Logo from "../assets/Logo.svg"; +import Button from "./Button"; export default function Register(): JSX.Element { const [username, setUsername] = useState(""); @@ -27,16 +28,18 @@ export default function Register(): JSX.Element { className="logo w-[7vw] mb-10 mt-10" alt="TTIME Logo" /> -

Register new user

+

+ Register New User +

- + />

From 6789cc97cea5fac14116639143fb8ff0a65228f2 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Fri, 15 Mar 2024 14:57:49 +0100 Subject: [PATCH 182/818] Added back button to page --- frontend/src/Pages/AdminPages/AdminManageUsers.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/frontend/src/Pages/AdminPages/AdminManageUsers.tsx b/frontend/src/Pages/AdminPages/AdminManageUsers.tsx index b25b120..610e8d2 100644 --- a/frontend/src/Pages/AdminPages/AdminManageUsers.tsx +++ b/frontend/src/Pages/AdminPages/AdminManageUsers.tsx @@ -1,5 +1,6 @@ import BasicWindow from "../../Components/BasicWindow"; import Button from "../../Components/Button"; +import BackButton from "../../Components/BackButton"; function AdminManageUsers(): JSX.Element { const content = <>; @@ -12,12 +13,7 @@ function AdminManageUsers(): JSX.Element { return; }} /> -
+ ); } - -export default NewTimeReport; From 87c044b5bfe2aadf44576bc0ae6991c52f4ffe0d Mon Sep 17 00:00:00 2001 From: Mattias Date: Sat, 16 Mar 2024 13:11:26 +0100 Subject: [PATCH 201/818] Submit-button has been moved to the timereport --- frontend/src/Pages/UserPages/UserNewTimeReportPage.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/frontend/src/Pages/UserPages/UserNewTimeReportPage.tsx b/frontend/src/Pages/UserPages/UserNewTimeReportPage.tsx index c93527a..ca84770 100644 --- a/frontend/src/Pages/UserPages/UserNewTimeReportPage.tsx +++ b/frontend/src/Pages/UserPages/UserNewTimeReportPage.tsx @@ -13,13 +13,6 @@ function UserNewTimeReportPage(): JSX.Element { const buttons = ( <> - {isOpen && ( From 164ff781b3e4799f977872bbd1455644d83cd7c4 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Mon, 18 Mar 2024 10:43:52 +0100 Subject: [PATCH 313/818] Add useEffect hook to handle authority navigation and log response in YourProjectsPage --- frontend/src/Pages/App.tsx | 19 +++++++++++-------- frontend/src/Pages/YourProjectsPage.tsx | 1 + 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/frontend/src/Pages/App.tsx b/frontend/src/Pages/App.tsx index 7ded3b6..4263815 100644 --- a/frontend/src/Pages/App.tsx +++ b/frontend/src/Pages/App.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useState, useEffect } from "react"; import LoginPage from "./LoginPage"; import { useNavigate } from "react-router-dom"; @@ -6,13 +6,16 @@ import { useNavigate } from "react-router-dom"; function App(): JSX.Element { const navigate = useNavigate(); const [authority, setAuthority] = useState(0); - if (authority === 1) { - navigate("/admin"); - } else if (authority === 2) { - navigate("/pm"); - } else if (authority === 3) { - navigate("/user"); - } + + useEffect(() => { + if (authority === 1) { + navigate("/admin"); + } else if (authority === 2) { + navigate("/pm"); + } else if (authority === 3) { + navigate("/user"); + } + }, [authority, navigate]); return ; } diff --git a/frontend/src/Pages/YourProjectsPage.tsx b/frontend/src/Pages/YourProjectsPage.tsx index 4e43bb6..30912a6 100644 --- a/frontend/src/Pages/YourProjectsPage.tsx +++ b/frontend/src/Pages/YourProjectsPage.tsx @@ -11,6 +11,7 @@ function UserProjectPage(): JSX.Element { const username = localStorage.getItem("username") ?? ""; // replace with actual username const token = localStorage.getItem("accessToken") ?? ""; // replace with actual token const response = await api.getUserProjects(username, token); + console.log(response); if (response.success) { setProjects(response.data ?? []); } else { From 3f8d56963ba03b25c25cd30fd5a8fdd003680aff Mon Sep 17 00:00:00 2001 From: Davenludd Date: Mon, 18 Mar 2024 12:49:58 +0100 Subject: [PATCH 314/818] Add ProjectNameContext to NewWeeklyReport and handle project selection in YourProjectsPage --- frontend/src/Components/NewWeeklyReport.tsx | 3 ++- frontend/src/Pages/YourProjectsPage.tsx | 25 +++++++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/frontend/src/Components/NewWeeklyReport.tsx b/frontend/src/Components/NewWeeklyReport.tsx index f5d92da..4f919aa 100644 --- a/frontend/src/Components/NewWeeklyReport.tsx +++ b/frontend/src/Components/NewWeeklyReport.tsx @@ -3,6 +3,7 @@ import { NewWeeklyReport } from "../Types/goTypes"; import { api } from "../API/API"; import { useNavigate } from "react-router-dom"; import Button from "./Button"; +import { ProjectNameContext } from "../Pages/YourProjectsPage"; export default function NewWeeklyReport(): JSX.Element { const [week, setWeek] = useState(0); @@ -13,7 +14,7 @@ export default function NewWeeklyReport(): JSX.Element { const [studyTime, setStudyTime] = useState(0); const [testingTime, setTestingTime] = useState(0); - // const projectName = useContext(projectNameContext); + const projectName = useContext(ProjectNameContext); const token = localStorage.getItem("accessToken") ?? ""; const handleNewWeeklyReport = async (): Promise => { diff --git a/frontend/src/Pages/YourProjectsPage.tsx b/frontend/src/Pages/YourProjectsPage.tsx index 30912a6..aabc606 100644 --- a/frontend/src/Pages/YourProjectsPage.tsx +++ b/frontend/src/Pages/YourProjectsPage.tsx @@ -1,11 +1,14 @@ -import React, { useState } from "react"; +import React, { useState, createContext, useEffect } from "react"; import { Project } from "../Types/goTypes"; import { api } from "../API/API"; import { Link } from "react-router-dom"; import BasicWindow from "../Components/BasicWindow"; +export const ProjectNameContext = createContext(""); + function UserProjectPage(): JSX.Element { const [projects, setProjects] = useState([]); + const [selectedProject, setSelectedProject] = useState(""); const getProjects = async (): Promise => { const username = localStorage.getItem("username") ?? ""; // replace with actual username @@ -18,20 +21,34 @@ function UserProjectPage(): JSX.Element { console.error(response.message); } }; + // Call getProjects when the component mounts + useEffect(() => { + getProjects(); + }, []); + + const handleProjectClick = (projectName: string): void => { + setSelectedProject(projectName); + }; const content = ( - <> +

Your Projects

{projects.map((project, index) => ( - + { + handleProjectClick(project.name); + }} + key={index} + >

{project.name}

))}
- +
); const buttons = <>; From 76fefd2b2482a4aee882dff68e7a73fb26aa9980 Mon Sep 17 00:00:00 2001 From: dDogge Date: Mon, 18 Mar 2024 13:32:55 +0100 Subject: [PATCH 315/818] Added function to check if someone is admin --- backend/internal/database/db.go | 21 ++++++++++++++ backend/internal/database/db_test.go | 43 ++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 5cbb13f..82a3551 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -32,6 +32,7 @@ type Database interface { GetUserRole(username string, projectname string) (string, error) GetWeeklyReport(username string, projectName string, week int) (types.WeeklyReport, error) SignWeeklyReport(reportId int, projectManagerId int) error + IsSiteAdmin(username string) (bool, error) } // This struct is a wrapper type that holds the database connection @@ -313,6 +314,26 @@ func (d *Db) SignWeeklyReport(reportId int, projectManagerId int) error { return err } +// IsSiteAdmin checks if a given username is a site admin +func (d *Db) IsSiteAdmin(username string) (bool, error) { + // Define the SQL query to check if the user is a site admin + query := ` + SELECT COUNT(*) FROM site_admin + JOIN users ON site_admin.admin_id = users.id + WHERE users.username = ? + ` + + // Execute the query + var count int + err := d.Get(&count, query, username) + if err != nil { + return false, err + } + + // If count is greater than 0, the user is a site admin + return count > 0, nil +} + // Reads a directory of migration files and applies them to the database. // This will eventually be used on an embedded directory func (d *Db) Migrate() error { diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index 09de45b..2378b3d 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -536,3 +536,46 @@ func TestSignWeeklyReportByAnotherProjectManager(t *testing.T) { t.Error("Expected SignWeeklyReport to fail with a project manager who is not in the project, but it didn't") } } + +func TestIsSiteAdmin(t *testing.T) { + db, err := setupState() + if err != nil { + t.Error("setupState failed:", err) + } + + // Add a site admin + err = db.AddUser("admin", "password") + if err != nil { + t.Error("AddUser failed:", err) + } + + // Promote the user to site admin + err = db.PromoteToAdmin("admin") + if err != nil { + t.Error("PromoteToAdmin failed:", err) + } + + // Check if the user is a site admin + isAdmin, err := db.IsSiteAdmin("admin") + if err != nil { + t.Error("IsSiteAdmin failed:", err) + } + if !isAdmin { + t.Error("IsSiteAdmin failed: expected true, got false") + } + + // Add a regular user + err = db.AddUser("regularuser", "password") + if err != nil { + t.Error("AddUser failed:", err) + } + + // Check if the regular user is not a site admin + isRegularUserAdmin, err := db.IsSiteAdmin("regularuser") + if err != nil { + t.Error("IsSiteAdmin failed:", err) + } + if isRegularUserAdmin { + t.Error("IsSiteAdmin failed: expected false, got true") + } +} From 409d973d8acc9402d5f539155eb6d177bccbfa1c Mon Sep 17 00:00:00 2001 From: pavel Hamawand Date: Mon, 18 Mar 2024 14:35:56 +0100 Subject: [PATCH 316/818] minor fix --- frontend/src/Pages/YourProjectsPage.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/Pages/YourProjectsPage.tsx b/frontend/src/Pages/YourProjectsPage.tsx index 2b3fd18..6de09ef 100644 --- a/frontend/src/Pages/YourProjectsPage.tsx +++ b/frontend/src/Pages/YourProjectsPage.tsx @@ -1,6 +1,6 @@ import BasicWindow from "../Components/BasicWindow"; import { ProjectListUser } from "../Components/ProjectListUser"; -import { Project } from "../Types/Project"; +import { Project } from "../Types/goTypes"; function YourProjectsPage(): JSX.Element { //TODO: Change so that it reads projects from database @@ -10,7 +10,6 @@ function YourProjectsPage(): JSX.Element { id: i, name: "Example Project " + i, description: "good", - created: "now", owner: "me", }); } From 69d406720980d55b5bface33a47d89b24d413a40 Mon Sep 17 00:00:00 2001 From: Mattias Date: Mon, 18 Mar 2024 14:40:20 +0100 Subject: [PATCH 317/818] Added new component for viewing weeklyreport --- frontend/src/Components/ViewWeeklyReport.tsx | 243 +++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 frontend/src/Components/ViewWeeklyReport.tsx diff --git a/frontend/src/Components/ViewWeeklyReport.tsx b/frontend/src/Components/ViewWeeklyReport.tsx new file mode 100644 index 0000000..d6d4692 --- /dev/null +++ b/frontend/src/Components/ViewWeeklyReport.tsx @@ -0,0 +1,243 @@ +import { useState, useEffect } from "react"; +import { NewWeeklyReport } from "../Types/goTypes"; +import { api } from "../API/API"; +import { useNavigate } from "react-router-dom"; +import Button from "./Button"; + +export default function GetWeeklyReport(): JSX.Element { + const [projectName, setProjectName] = useState(""); + const [week, setWeek] = useState(0); + const [developmentTime, setDevelopmentTime] = useState(0); + const [meetingTime, setMeetingTime] = useState(0); + const [adminTime, setAdminTime] = useState(0); + const [ownWorkTime, setOwnWorkTime] = useState(0); + const [studyTime, setStudyTime] = useState(0); + const [testingTime, setTestingTime] = useState(0); + + const token = localStorage.getItem("accessToken") ?? ""; + + useEffect(() => { + const fetchWeeklyReport = async (): Promise => { + const response = await api.getWeeklyReport( + "username", + projectName, + "week", + token, + ); + + if (response.success) { + const report: NewWeeklyReport = response.data ?? { + projectName: "", + week: 0, + developmentTime: 0, + meetingTime: 0, + adminTime: 0, + ownWorkTime: 0, + studyTime: 0, + testingTime: 0, + }; + setProjectName(report.projectName); + setWeek(report.week); + setDevelopmentTime(report.developmentTime); + setMeetingTime(report.meetingTime); + setAdminTime(report.adminTime); + setOwnWorkTime(report.ownWorkTime); + setStudyTime(report.studyTime); + setTestingTime(report.testingTime); + } else { + console.error("Failed to fetch weekly report:", response.message); + } + }; + + fetchWeeklyReport(); + }, []); + + const handleNewWeeklyReport = async (): Promise => { + const newWeeklyReport: NewWeeklyReport = { + projectName, + week, + developmentTime, + meetingTime, + adminTime, + ownWorkTime, + studyTime, + testingTime, + }; + + await api.submitWeeklyReport(newWeeklyReport, token); + }; + + const navigate = useNavigate(); + + return ( + <> +
+
{ + if (week === 0) { + alert("Please enter a week number"); + e.preventDefault(); + return; + } + e.preventDefault(); + void handleNewWeeklyReport(); + navigate("/project"); + }} + > +
+ { + const weekNumber = parseInt(e.target.value.split("-W")[1]); + setWeek(weekNumber); + }} + onKeyDown={(event) => { + event.preventDefault(); + }} + onPaste={(event) => { + event.preventDefault(); + }} + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Activity + + Total Time (min) +
Development + { + setDevelopmentTime(parseInt(e.target.value)); + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); + }} + /> +
Meeting + { + setMeetingTime(parseInt(e.target.value)); + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); + }} + /> +
Administration + { + setAdminTime(parseInt(e.target.value)); + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); + }} + /> +
Own Work + { + setOwnWorkTime(parseInt(e.target.value)); + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); + }} + /> +
Studies + { + setStudyTime(parseInt(e.target.value)); + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); + }} + /> +
Testing + { + setTestingTime(parseInt(e.target.value)); + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); + }} + /> +
+
+
+
+ + ); +} From 0634f836893470d4297cc8f0373b2e5d128f52b9 Mon Sep 17 00:00:00 2001 From: Mattias Date: Mon, 18 Mar 2024 14:40:32 +0100 Subject: [PATCH 318/818] Now displaying the right component --- frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx b/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx index d82ea8c..9da19fe 100644 --- a/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx +++ b/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx @@ -1,13 +1,13 @@ import BasicWindow from "../../Components/BasicWindow"; import Button from "../../Components/Button"; import BackButton from "../../Components/BackButton"; -import NewTimeReport from "../../Components/NewWeeklyReport"; +import ViewWeeklyReport from "../../Components/ViewWeeklyReport"; function UserEditTimeReportPage(): JSX.Element { const content = ( <>

Edit Time Report

- + ); From b82f73d192f4c7a3f8f09229e02c2ab165258562 Mon Sep 17 00:00:00 2001 From: Mattias Date: Mon, 18 Mar 2024 14:42:21 +0100 Subject: [PATCH 319/818] Removed button --- frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx b/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx index 9da19fe..0acef75 100644 --- a/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx +++ b/frontend/src/Pages/UserPages/UserEditTimeReportPage.tsx @@ -1,5 +1,4 @@ import BasicWindow from "../../Components/BasicWindow"; -import Button from "../../Components/Button"; import BackButton from "../../Components/BackButton"; import ViewWeeklyReport from "../../Components/ViewWeeklyReport"; @@ -13,13 +12,6 @@ function UserEditTimeReportPage(): JSX.Element { const buttons = ( <> -
; -} From aa5c4017bb3087d475e4fd85a1f94c7a9d5463c6 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 20 Mar 2024 13:54:13 +0100 Subject: [PATCH 483/818] Docs/Comments to DisplayUserProjects component --- frontend/src/Components/DisplayUserProjects.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/src/Components/DisplayUserProjects.tsx b/frontend/src/Components/DisplayUserProjects.tsx index 266f1ce..f4fd782 100644 --- a/frontend/src/Components/DisplayUserProjects.tsx +++ b/frontend/src/Components/DisplayUserProjects.tsx @@ -3,6 +3,10 @@ import { Project } from "../Types/goTypes"; import { Link } from "react-router-dom"; import { api } from "../API/API"; +/** + * Renders a component that displays the projects a user is a part of and links to the projects start-page. + * @returns The JSX element representing the component. + */ function DisplayUserProject(): JSX.Element { const [projects, setProjects] = useState([]); @@ -16,6 +20,7 @@ function DisplayUserProject(): JSX.Element { console.error(response.message); } }; + // Call getProjects when the component mounts useEffect(() => { void getProjects(); From dbeeb609b322d0572abff578e00ac7c0ad44d020 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 20 Mar 2024 13:54:32 +0100 Subject: [PATCH 484/818] Docs/Comments to GetWeeklyReport component --- frontend/src/Components/EditWeeklyReport.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/src/Components/EditWeeklyReport.tsx b/frontend/src/Components/EditWeeklyReport.tsx index 3017204..e824b19 100644 --- a/frontend/src/Components/EditWeeklyReport.tsx +++ b/frontend/src/Components/EditWeeklyReport.tsx @@ -4,6 +4,10 @@ import { api } from "../API/API"; import { useNavigate, useParams } from "react-router-dom"; import Button from "./Button"; +/** + * Renders the component for editing a weekly report. + * @returns JSX.Element + */ export default function GetWeeklyReport(): JSX.Element { const [week, setWeek] = useState(0); const [developmentTime, setDevelopmentTime] = useState(0); From cf6c8cd34c779b86ffa07a96427aa66a1be7d1a8 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 20 Mar 2024 13:54:59 +0100 Subject: [PATCH 485/818] Docs/Comments to Footer component --- frontend/src/Components/Footer.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frontend/src/Components/Footer.tsx b/frontend/src/Components/Footer.tsx index a3b7469..192926f 100644 --- a/frontend/src/Components/Footer.tsx +++ b/frontend/src/Components/Footer.tsx @@ -1,5 +1,13 @@ +//info: Footer component to display the footer of a page where the buttons are placed import React from "react"; +/** + * Footer component. + * + * @param {Object} props - The component props. + * @param {React.ReactNode} props.children - The children elements to render inside the footer (buttons). + * @returns {JSX.Element} The rendered footer component. + */ function Footer({ children }: { children: React.ReactNode }): JSX.Element { return (
From 7e7dbe2cc7266f19565fa7ab508c29b9b11ced99 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 20 Mar 2024 13:55:31 +0100 Subject: [PATCH 486/818] Docs/Comments to header component --- frontend/src/Components/Header.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/src/Components/Header.tsx b/frontend/src/Components/Header.tsx index 5cdb421..eb4fa5a 100644 --- a/frontend/src/Components/Header.tsx +++ b/frontend/src/Components/Header.tsx @@ -1,7 +1,12 @@ +//info: Header component to display the header of the page including the logo and user information where thr user can logout import { useState } from "react"; import { Link } from "react-router-dom"; import backgroundImage from "../assets/1.jpg"; +/** + * Renders the header component. + * @returns JSX.Element representing the header component. + */ function Header(): JSX.Element { const [isOpen, setIsOpen] = useState(false); From d9068949cdbf70fae02e514adc8551f87195f951 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 20 Mar 2024 13:55:56 +0100 Subject: [PATCH 487/818] Docs/Comments to new weekly report form component --- frontend/src/Components/NewWeeklyReport.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend/src/Components/NewWeeklyReport.tsx b/frontend/src/Components/NewWeeklyReport.tsx index e53823d..292ddf5 100644 --- a/frontend/src/Components/NewWeeklyReport.tsx +++ b/frontend/src/Components/NewWeeklyReport.tsx @@ -1,9 +1,15 @@ +//info: New weekly report form component to create a new weekly report to +//sumbit development time, meeting time, admin time, own work time, study time and testing time import { useState } from "react"; import type { NewWeeklyReport } from "../Types/goTypes"; import { api } from "../API/API"; import { useNavigate, useParams } from "react-router-dom"; import Button from "./Button"; +/** + * Renders a form for creating a new weekly report. + * @returns The JSX element representing the new weekly report form. + */ export default function NewWeeklyReport(): JSX.Element { const [week, setWeek] = useState(0); const [developmentTime, setDevelopmentTime] = useState(); From c7c38d4201973c8a9029f7ca0b708aa0dcadb3ef Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 20 Mar 2024 13:56:13 +0100 Subject: [PATCH 488/818] Docs/Comments to registration form component --- frontend/src/Components/Register.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/src/Components/Register.tsx b/frontend/src/Components/Register.tsx index 7b003cb..df07c6e 100644 --- a/frontend/src/Components/Register.tsx +++ b/frontend/src/Components/Register.tsx @@ -6,6 +6,10 @@ import Button from "./Button"; import InputField from "./InputField"; import { useNavigate } from "react-router-dom"; +/** + * Renders a registration form for the admin to add new users in. + * @returns The JSX element representing the registration form. + */ export default function Register(): JSX.Element { const [username, setUsername] = useState(); const [password, setPassword] = useState(); From 29d893362cfb8bcacfeb5e9ee775512feef1fd38 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 20 Mar 2024 13:56:42 +0100 Subject: [PATCH 489/818] Docs/Comments to user project menu component --- frontend/src/Components/UserProjectMenu.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frontend/src/Components/UserProjectMenu.tsx b/frontend/src/Components/UserProjectMenu.tsx index 986b6e6..e307e90 100644 --- a/frontend/src/Components/UserProjectMenu.tsx +++ b/frontend/src/Components/UserProjectMenu.tsx @@ -1,6 +1,13 @@ +//info: User project menu component to display the user project menu where the user can navigate to +//existing time reports in a project and create a new time report import { useParams, Link } from "react-router-dom"; import { JSX } from "react/jsx-runtime"; +/** + * Renders the user project menu component. + * + * @returns JSX.Element representing the user project menu. + */ function UserProjectMenu(): JSX.Element { const { projectName } = useParams(); From d41bfcf8886b6c0d63df8cd7e1a0ab4a75179948 Mon Sep 17 00:00:00 2001 From: al8763be Date: Wed, 20 Mar 2024 14:00:00 +0100 Subject: [PATCH 490/818] Fix for getWeeklyReportsForUser --- frontend/src/API/API.ts | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index fc2367b..c4a9e43 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -4,6 +4,7 @@ import { User, Project, NewProject, + WeeklyReport, } from "../Types/goTypes"; // This type of pattern should be hard to misuse @@ -47,11 +48,12 @@ interface API { projectName: string, week: string, token: string, - ): Promise>; + ): Promise>; getWeeklyReportsForProject( + username: string, projectName: string, token: string, - ): Promise>; + ): Promise>; /** Gets all the projects of a user*/ getUserProjects(token: string): Promise>; /** Gets a project from id*/ @@ -250,7 +252,7 @@ export const api: API = { projectName: string, week: string, token: string, - ): Promise> { + ): Promise> { try { const response = await fetch("/api/getWeeklyReport", { method: "GET", @@ -264,7 +266,7 @@ export const api: API = { if (!response.ok) { return { success: false, message: "Failed to get weekly report" }; } else { - const data = (await response.json()) as NewWeeklyReport; + const data = (await response.json()) as WeeklyReport; return { success: true, data }; } } catch (e) { @@ -272,15 +274,19 @@ export const api: API = { } }, - async getWeeklyReportsForProject(projectName: string, token: string) { + async getWeeklyReportsForProject( + username: string, + projectName: string, + token: string + ): Promise> { try { - const response = await fetch("/api/getWeeklyReportsForProject", { + const response = await fetch("/api/getWeeklyReportsUser", { method: "GET", headers: { "Content-Type": "application/json", Authorization: "Bearer " + token, }, - body: JSON.stringify({ projectName }), + body: JSON.stringify({ username, projectName }), }); if (!response.ok) { @@ -289,7 +295,7 @@ export const api: API = { message: "Failed to get weekly reports for project", }; } else { - const data = (await response.json()) as NewWeeklyReport[]; + const data = (await response.json()) as WeeklyReport[]; return { success: true, data }; } } catch (e) { From 74e134716250fa07ab056f652cda812ba9febdd9 Mon Sep 17 00:00:00 2001 From: al8763be Date: Wed, 20 Mar 2024 14:02:34 +0100 Subject: [PATCH 491/818] Changed error message --- frontend/src/API/API.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index c4a9e43..1e90174 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -277,7 +277,7 @@ export const api: API = { async getWeeklyReportsForProject( username: string, projectName: string, - token: string + token: string, ): Promise> { try { const response = await fetch("/api/getWeeklyReportsUser", { @@ -301,7 +301,7 @@ export const api: API = { } catch (e) { return { success: false, - message: "Failed to get weekly reports for project", + message: "fucked again", }; } }, From 00f72df65f33af749f6476607b471e3ec6766931 Mon Sep 17 00:00:00 2001 From: Johanna Date: Wed, 20 Mar 2024 14:06:15 +0100 Subject: [PATCH 492/818] Added comments --- backend/internal/config/config_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/backend/internal/config/config_test.go b/backend/internal/config/config_test.go index cb02a31..e8ddce8 100644 --- a/backend/internal/config/config_test.go +++ b/backend/internal/config/config_test.go @@ -5,8 +5,12 @@ import ( "testing" ) +// TestNewConfig tests the creation of a new configuration object func TestNewConfig(t *testing.T) { + // Arrange c := NewConfig() + + // Act & Assert if c.Port != 8080 { t.Errorf("Expected port to be 8080, got %d", c.Port) } @@ -24,9 +28,15 @@ func TestNewConfig(t *testing.T) { } } +// TestWriteConfig tests the function to write the configuration to a file func TestWriteConfig(t *testing.T) { + // Arrange c := NewConfig() + + //Act err := c.WriteConfigToFile("test.toml") + + // Assert if err != nil { t.Errorf("Expected no error, got %s", err) } @@ -35,14 +45,23 @@ func TestWriteConfig(t *testing.T) { _ = os.Remove("test.toml") } +// TestReadConfig tests the function to read the configuration from a file func TestReadConfig(t *testing.T) { + // Arrange c := NewConfig() + + // Act err := c.WriteConfigToFile("test.toml") + + // Assert if err != nil { t.Errorf("Expected no error, got %s", err) } + // Act c2, err := ReadConfigFromFile("test.toml") + + // Assert if err != nil { t.Errorf("Expected no error, got %s", err) } From adf9500bbff0e086ee8e89ec60331d7b2d5522a5 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 20 Mar 2024 14:06:54 +0100 Subject: [PATCH 493/818] Fix useEffect dependency in AllTimeReportsInProject component --- frontend/src/Components/AllTimeReportsInProject.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/Components/AllTimeReportsInProject.tsx b/frontend/src/Components/AllTimeReportsInProject.tsx index 41307d3..26f26cf 100644 --- a/frontend/src/Components/AllTimeReportsInProject.tsx +++ b/frontend/src/Components/AllTimeReportsInProject.tsx @@ -31,7 +31,7 @@ function AllTimeReportsInProject(): JSX.Element { // Call getProjects when the component mounts useEffect(() => { void getWeeklyReports(); - }, []); + }); return ( <> From 89cb3d23b7cae3892f76468b436b405737f3929a Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 20 Mar 2024 14:07:12 +0100 Subject: [PATCH 494/818] Fix missing comma in getWeeklyReportsForProject function signature --- frontend/src/API/API.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index 647d11f..ca26c2c 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -279,7 +279,7 @@ export const api: API = { async getWeeklyReportsForProject( username: string, projectName: string, - token: string + token: string, ): Promise> { try { const response = await fetch("/api/getWeeklyReportsUser", { From 0fb06790af3f63e79b35623ea01d4e3fbb55bb34 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 20 Mar 2024 14:09:16 +0100 Subject: [PATCH 495/818] Fix useEffect dependency array in ProjectMembers component --- frontend/src/Components/ProjectMembers.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/Components/ProjectMembers.tsx b/frontend/src/Components/ProjectMembers.tsx index e1f261e..73e29e5 100644 --- a/frontend/src/Components/ProjectMembers.tsx +++ b/frontend/src/Components/ProjectMembers.tsx @@ -57,7 +57,7 @@ function ProjectMembers(): JSX.Element { useEffect(() => { void getProjectMembers(); - }, []); + }); return ( <> From c6969b9503f489fd051ab15d8ad2a9358e284f38 Mon Sep 17 00:00:00 2001 From: borean Date: Wed, 20 Mar 2024 14:16:47 +0100 Subject: [PATCH 496/818] updated api path for DeleteProject --- backend/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/main.go b/backend/main.go index 888cb51..c5ac0bb 100644 --- a/backend/main.go +++ b/backend/main.go @@ -87,7 +87,7 @@ func main() { server.Get("/api/getUserProjects", gs.GetUserProjects) server.Post("/api/loginrenew", gs.LoginRenew) server.Delete("/api/userdelete/:username", gs.UserDelete) // Perhaps just use POST to avoid headaches - server.Delete("api/project", gs.DeleteProject) // WIP + server.Delete("api/project/:projectID", gs.DeleteProject) // WIP server.Post("/api/project", gs.CreateProject) // WIP server.Get("/api/project/:projectId", gs.GetProject) server.Get("/api/project/getAllUsers", gs.GetAllUsersProject) From 47ce98683439fe7c01751a7bfcf361fd39778cca Mon Sep 17 00:00:00 2001 From: Mattias Date: Wed, 20 Mar 2024 14:17:21 +0100 Subject: [PATCH 497/818] Minor fixes --- frontend/src/Components/EditWeeklyReport.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/frontend/src/Components/EditWeeklyReport.tsx b/frontend/src/Components/EditWeeklyReport.tsx index e824b19..76c2b94 100644 --- a/frontend/src/Components/EditWeeklyReport.tsx +++ b/frontend/src/Components/EditWeeklyReport.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from "react"; -import { NewWeeklyReport } from "../Types/goTypes"; +import { WeeklyReport, NewWeeklyReport } from "../Types/goTypes"; import { api } from "../API/API"; import { useNavigate, useParams } from "react-router-dom"; import Button from "./Button"; @@ -31,8 +31,10 @@ export default function GetWeeklyReport(): JSX.Element { ); if (response.success) { - const report: NewWeeklyReport = response.data ?? { - projectName: "", + const report: WeeklyReport = response.data ?? { + reportId: 0, + userId: 0, + projectId: 0, week: 0, developmentTime: 0, meetingTime: 0, @@ -41,7 +43,6 @@ export default function GetWeeklyReport(): JSX.Element { studyTime: 0, testingTime: 0, }; - setWeek(report.week); setDevelopmentTime(report.developmentTime); setMeetingTime(report.meetingTime); From 14b68e446319a8dfe079f825ced6b087298935b5 Mon Sep 17 00:00:00 2001 From: Mattias Date: Wed, 20 Mar 2024 14:20:09 +0100 Subject: [PATCH 498/818] Formatting --- frontend/src/Components/ChangeUsername.tsx | 24 +++++++++------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/frontend/src/Components/ChangeUsername.tsx b/frontend/src/Components/ChangeUsername.tsx index 71d5e57..3c35e94 100644 --- a/frontend/src/Components/ChangeUsername.tsx +++ b/frontend/src/Components/ChangeUsername.tsx @@ -1,9 +1,5 @@ import React, { useState } from "react"; -import { api } from "../API/API"; import InputField from "./InputField"; -import BackButton from "./BackButton"; -import Button from "./Button"; - function ChangeUsername(): JSX.Element { const [newUsername, setNewUsername] = useState(""); @@ -12,16 +8,16 @@ function ChangeUsername(): JSX.Element { setNewUsername(e.target.value); }; - const handleSubmit = async (): Promise => { - try { - // Call the API function to update the username - await api.updateUsername(newUsername); - // Optionally, add a success message or redirect the user - } catch (error) { - console.error("Error updating username:", error); - // Optionally, handle the error - } - }; + // const handleSubmit = async (): Promise => { + // try { + // // Call the API function to update the username + // await api.updateUsername(newUsername); + // // Optionally, add a success message or redirect the user + // } catch (error) { + // console.error("Error updating username:", error); + // // Optionally, handle the error + // } + // }; return (
From a84c2c6129ed6d1daca8369030a3ddd81b101c0f Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 20 Mar 2024 14:54:22 +0100 Subject: [PATCH 499/818] Rename GetWeeklyReportsForProject -> GetWeeklyReportsForUser --- frontend/src/API/API.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index 1e90174..a3c4e92 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -49,7 +49,7 @@ interface API { week: string, token: string, ): Promise>; - getWeeklyReportsForProject( + getWeeklyReportsForUser( username: string, projectName: string, token: string, @@ -274,7 +274,7 @@ export const api: API = { } }, - async getWeeklyReportsForProject( + async getWeeklyReportsForUser( username: string, projectName: string, token: string, From 7799eb09e9c0e857e97531b7aa7057b8ab9331d7 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 20 Mar 2024 15:11:25 +0100 Subject: [PATCH 500/818] Delete unused handler prototype --- backend/internal/handlers/global_state.go | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go index a3e43ce..b88bdcd 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -22,19 +22,7 @@ type GlobalState interface { PromoteToAdmin(c *fiber.Ctx) error GetWeeklyReportsUserHandler(c *fiber.Ctx) error IsProjectManagerHandler(c *fiber.Ctx) error - // UpdateProject(c *fiber.Ctx) error // To update a project // WIP - DeleteProject(c *fiber.Ctx) error // To delete a project // WIP - // CreateTask(c *fiber.Ctx) error // To create a new task // WIP - // GetTasks(c *fiber.Ctx) error // To get all tasks // WIP - // GetTask(c *fiber.Ctx) error // To get a specific task // WIP - // UpdateTask(c *fiber.Ctx) error // To update a task // WIP - // DeleteTask(c *fiber.Ctx) error // To delete a task // WIP - // CreateCollection(c *fiber.Ctx) error // To create a new collection // WIP - // GetCollections(c *fiber.Ctx) error // To get all collections // WIP - // GetCollection(c *fiber.Ctx) error // To get a specific collection // WIP - // UpdateCollection(c *fiber.Ctx) error // To update a collection // WIP - // DeleteCollection(c *fiber.Ctx) error // To delete a collection // WIP - // SignCollection(c *fiber.Ctx) error // To sign a collection // WIP + DeleteProject(c *fiber.Ctx) error // To delete a project // WIP ListAllUsers(c *fiber.Ctx) error // To get a list of all users in the application database ListAllUsersProject(c *fiber.Ctx) error // To get a list of all users for a specific project ProjectRoleChange(c *fiber.Ctx) error // To change a users role in a project From 771118f85ead09aac4619f7870593b7456da804c Mon Sep 17 00:00:00 2001 From: al8763be Date: Wed, 20 Mar 2024 15:13:00 +0100 Subject: [PATCH 501/818] Update API.ts --- frontend/src/API/API.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index a3c4e92..d6a4b23 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -286,7 +286,7 @@ export const api: API = { "Content-Type": "application/json", Authorization: "Bearer " + token, }, - body: JSON.stringify({ username, projectName }), + body: JSON.stringify({ username: username, projectName: projectName}), }); if (!response.ok) { From af5813681d11500c0cfe723af1b8d2522bf277d2 Mon Sep 17 00:00:00 2001 From: al8763be Date: Wed, 20 Mar 2024 15:17:00 +0100 Subject: [PATCH 502/818] Jag avskyr denna handlern --- frontend/src/API/API.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index d6a4b23..6a508b5 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -280,13 +280,12 @@ export const api: API = { token: string, ): Promise> { try { - const response = await fetch("/api/getWeeklyReportsUser", { + const response = await fetch(`/api/getWeeklyReportsUser?username=${username}&projectName=${projectName}`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: "Bearer " + token, }, - body: JSON.stringify({ username: username, projectName: projectName}), }); if (!response.ok) { From 54e42cd2a8939647cc1f9837c76ccc0433f0bb9f Mon Sep 17 00:00:00 2001 From: Davenludd Date: Wed, 20 Mar 2024 15:19:09 +0100 Subject: [PATCH 503/818] Add support for week input in Chrome and Edge browsers --- frontend/src/Components/NewWeeklyReport.tsx | 58 +++++++++++++++------ 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/frontend/src/Components/NewWeeklyReport.tsx b/frontend/src/Components/NewWeeklyReport.tsx index 292ddf5..2593782 100644 --- a/frontend/src/Components/NewWeeklyReport.tsx +++ b/frontend/src/Components/NewWeeklyReport.tsx @@ -38,6 +38,7 @@ export default function NewWeeklyReport(): JSX.Element { }; const navigate = useNavigate(); + const isChromeOrEdge = /Chrome|Edg/.test(navigator.userAgent); return ( <> @@ -55,23 +56,48 @@ export default function NewWeeklyReport(): JSX.Element { }} >
- { - const weekNumber = parseInt(e.target.value.split("-W")[1]); - setWeek(weekNumber); - }} - onKeyDown={(event) => { - const keyValue = event.key; - if (!/\d/.test(keyValue) && keyValue !== "Backspace") + {isChromeOrEdge ? ( + { + const weekNumber = parseInt(e.target.value.split("-W")[1]); + setWeek(weekNumber); + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); + }} + onPaste={(event) => { event.preventDefault(); - }} - onPaste={(event) => { - event.preventDefault(); - }} - /> + }} + /> + ) : ( + { + const weekNumber = parseInt(e.target.value); + if (isNaN(weekNumber) || weekNumber < 1 || weekNumber > 53) { + setWeek(0); + return; + } else { + setWeek(weekNumber); + } + }} + onKeyDown={(event) => { + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); + }} + onPaste={(event) => { + event.preventDefault(); + }} + /> + )} From 0076a9f4bb7f16808d6e9752f0e57300f3a64cc4 Mon Sep 17 00:00:00 2001 From: pavel Hamawand Date: Wed, 20 Mar 2024 15:30:16 +0100 Subject: [PATCH 504/818] backButton --- frontend/src/Pages/AdminPages/AdminProjectAddMember.tsx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/frontend/src/Pages/AdminPages/AdminProjectAddMember.tsx b/frontend/src/Pages/AdminPages/AdminProjectAddMember.tsx index 96167cb..712df86 100644 --- a/frontend/src/Pages/AdminPages/AdminProjectAddMember.tsx +++ b/frontend/src/Pages/AdminPages/AdminProjectAddMember.tsx @@ -1,3 +1,4 @@ +import BackButton from "../../Components/BackButton"; import BasicWindow from "../../Components/BasicWindow"; import Button from "../../Components/Button"; @@ -13,13 +14,7 @@ function AdminProjectAddMember(): JSX.Element { }} type="button" /> -
+ + + + + + + + + + + + + + + + + + + + + + + + +
Role + Total Time (min) +
Project Manager + { + event.preventDefault(); + }} + /> +
System Manager + { + event.preventDefault(); + }} + /> +
Administration + { + event.preventDefault(); + }} + /> +
Own Work + { + event.preventDefault(); + }} + /> +
+
+
+ + ); +} diff --git a/frontend/src/Pages/ProjectManagerPages/PMTotalTimeRole.tsx b/frontend/src/Pages/ProjectManagerPages/PMTotalTimeRole.tsx index c0161f8..7caea04 100644 --- a/frontend/src/Pages/ProjectManagerPages/PMTotalTimeRole.tsx +++ b/frontend/src/Pages/ProjectManagerPages/PMTotalTimeRole.tsx @@ -1,8 +1,13 @@ import BasicWindow from "../../Components/BasicWindow"; import BackButton from "../../Components/BackButton"; +import TimePerRole from "../../Components/TimePerRole"; function PMTotalTimeRole(): JSX.Element { - const content = <>; + const content = ( + <> + + + ); const buttons = ( <> From 4030031ce9c1c936e31ac5b8a13f0764e3a33a22 Mon Sep 17 00:00:00 2001 From: Mattias Date: Wed, 20 Mar 2024 16:36:24 +0100 Subject: [PATCH 517/818] New comp TimePerActivity --- frontend/src/Components/TimePerActivity.tsx | 175 ++++++++++++++++++ .../PMTotalTimeActivity.tsx | 7 +- 2 files changed, 177 insertions(+), 5 deletions(-) create mode 100644 frontend/src/Components/TimePerActivity.tsx diff --git a/frontend/src/Components/TimePerActivity.tsx b/frontend/src/Components/TimePerActivity.tsx new file mode 100644 index 0000000..3dc1a6b --- /dev/null +++ b/frontend/src/Components/TimePerActivity.tsx @@ -0,0 +1,175 @@ +import { useState, useEffect } from "react"; +import { useParams } from "react-router-dom"; + +/** + * Renders the component for showing total time per role in a project. + * @returns JSX.Element + */ +export default function TimePerRole(): JSX.Element { + const [developmentTime, setDevelopmentTime] = useState(); + const [meetingTime, setMeetingTime] = useState(); + const [adminTime, setAdminTime] = useState(); + const [ownWorkTime, setOwnWorkTime] = useState(); + const [studyTime, setStudyTime] = useState(); + const [testingTime, setTestingTime] = useState(); + + // const token = localStorage.getItem("accessToken") ?? ""; + // const username = localStorage.getItem("username") ?? ""; + const { projectName } = useParams(); + + // const fetchTimePerRole = async (): Promise => { + // const response = await api.getTimePerRole( + // username, + // projectName ?? "", + // token, + // ); + // { + // if (response.success) { + // const report: TimePerRole = response.data ?? { + // PManagerTime: 0, + // SManagerTime: 0, + // DeveloperTime: 0, + // TesterTime: 0, + // }; + // } else { + // console.error("Failed to fetch weekly report:", response.message); + // } + // } + + interface TimePerActivity { + developmentTime: number; + meetingTime: number; + adminTime: number; + ownWorkTime: number; + studyTime: number; + testingTime: number; + } + + const fetchTimePerActivity = async (): Promise => { + // Use mock data + const report: TimePerActivity = { + developmentTime: 100, + meetingTime: 200, + adminTime: 300, + ownWorkTime: 50, + studyTime: 75, + testingTime: 110, + }; + + // Set the state with the mock data + setDevelopmentTime(report.developmentTime); + setMeetingTime(report.meetingTime); + setAdminTime(report.adminTime); + setOwnWorkTime(report.ownWorkTime); + setStudyTime(report.studyTime); + setTestingTime(report.testingTime); + + await Promise.resolve(); + }; + + useEffect(() => { + void fetchTimePerActivity(); + }); + + return ( + <> +

+ Total Time Per Activity In: {projectName}{" "} +

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Activity + Total Time (min) +
Development + { + event.preventDefault(); + }} + /> +
Meeting + { + event.preventDefault(); + }} + /> +
Administration + { + event.preventDefault(); + }} + /> +
Own Work + { + event.preventDefault(); + }} + /> +
Studies + { + event.preventDefault(); + }} + /> +
Testing + { + event.preventDefault(); + }} + /> +
+
+
+ + ); +} diff --git a/frontend/src/Pages/ProjectManagerPages/PMTotalTimeActivity.tsx b/frontend/src/Pages/ProjectManagerPages/PMTotalTimeActivity.tsx index 676ea28..257a89c 100644 --- a/frontend/src/Pages/ProjectManagerPages/PMTotalTimeActivity.tsx +++ b/frontend/src/Pages/ProjectManagerPages/PMTotalTimeActivity.tsx @@ -1,14 +1,11 @@ import BackButton from "../../Components/BackButton"; import BasicWindow from "../../Components/BasicWindow"; -import TimeReport from "../../Components/NewWeeklyReport"; +import TimePerActivity from "../../Components/TimePerActivity"; function PMTotalTimeActivity(): JSX.Element { const content = ( <> -

- Total Time Per Activity -

- + ); From a3fc71f4bfc0b6131deef705312a45fb2355f912 Mon Sep 17 00:00:00 2001 From: Mattias Date: Wed, 20 Mar 2024 16:36:32 +0100 Subject: [PATCH 518/818] Changed path in main --- frontend/src/main.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 643eb62..c023660 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -80,7 +80,7 @@ const router = createBrowserRouter([ element: , }, { - path: "/PMTimeActivity", + path: "/PMTimeActivity/:projectName", element: , }, { From 0befc4c7d18ae4ff11fb080d8a60b1868da5cf10 Mon Sep 17 00:00:00 2001 From: Mattias Date: Wed, 20 Mar 2024 16:36:39 +0100 Subject: [PATCH 519/818] Minor fix --- frontend/src/Components/TimePerRole.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/Components/TimePerRole.tsx b/frontend/src/Components/TimePerRole.tsx index a43e732..f62d83a 100644 --- a/frontend/src/Components/TimePerRole.tsx +++ b/frontend/src/Components/TimePerRole.tsx @@ -41,7 +41,7 @@ export default function TimePerRole(): JSX.Element { Tester: number; } - const fetchWeeklyReport = async (): Promise => { + const fetchTimePerRole = async (): Promise => { // Use mock data const report: TimePerRole = { PManager: 120, @@ -60,7 +60,7 @@ export default function TimePerRole(): JSX.Element { }; useEffect(() => { - void fetchWeeklyReport(); + void fetchTimePerRole(); }); return ( From b99de71c38fa7f030bc43b6678f18f4d24c4c98b Mon Sep 17 00:00:00 2001 From: Mattias Date: Wed, 20 Mar 2024 16:41:32 +0100 Subject: [PATCH 520/818] Update links in PMProjectMembers component --- frontend/src/Pages/ProjectManagerPages/PMProjectMembers.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/Pages/ProjectManagerPages/PMProjectMembers.tsx b/frontend/src/Pages/ProjectManagerPages/PMProjectMembers.tsx index 11b8636..f7c902e 100644 --- a/frontend/src/Pages/ProjectManagerPages/PMProjectMembers.tsx +++ b/frontend/src/Pages/ProjectManagerPages/PMProjectMembers.tsx @@ -17,7 +17,7 @@ function PMProjectMembers(): JSX.Element { const buttons = ( <> - + - - ); } export default ChangeUsername; From 3981190c7a61ef5c1b925e896e5ba80aca27d675 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Mon, 1 Apr 2024 02:16:06 +0200 Subject: [PATCH 668/818] Can now change username in this modal + moved some stuff to a separate modal --- frontend/src/Components/UserInfoModal.tsx | 73 ++++++++++++++++------- 1 file changed, 52 insertions(+), 21 deletions(-) diff --git a/frontend/src/Components/UserInfoModal.tsx b/frontend/src/Components/UserInfoModal.tsx index 9d8dc11..7888c6b 100644 --- a/frontend/src/Components/UserInfoModal.tsx +++ b/frontend/src/Components/UserInfoModal.tsx @@ -1,34 +1,38 @@ -import { Link } from "react-router-dom"; import Button from "./Button"; import DeleteUser from "./DeleteUser"; import UserProjectListAdmin from "./UserProjectListAdmin"; +import { useState } from "react"; +import InputField from "./InputField"; +import ChangeUsername from "./ChangeUsername"; +import { StrNameChange } from "../Types/goTypes"; function UserInfoModal(props: { isVisible: boolean; - manageMember: boolean; username: string; onClose: () => void; - onDelete: (username: string) => void; }): JSX.Element { - if (!props.isVisible) return <>; - const ManageUserOrMember = (check: boolean): JSX.Element => { - if (check) { - return ( - -

- (Change Role) -

- - ); + const [showInput, setShowInput] = useState(false); + const [newUsername, setNewUsername] = useState(""); + if (!props.isVisible) { + return <>; + } + + const handleChangeNameView = (): void => { + if (showInput) { + setShowInput(false); + } else { + setShowInput(true); } - return ( - -

- (Change Username) -

- - ); }; + + const handleClickChangeName = (): void => { + const nameChange: StrNameChange = { + prevName: props.username, + newName: newUsername, + }; + ChangeUsername({ nameChange: nameChange }); + }; + return (

{props.username}

- {ManageUserOrMember(props.manageMember)} +

+ (Change Username) +

+ {showInput && ( +
+ +
+ )}

Member of these projects: @@ -62,6 +91,8 @@ function UserInfoModal(props: {

+
+ + ); +} + +export default MemberInfoModal; From 58deef400a9d448059bad1dfb75fea9fdd9b4d1e Mon Sep 17 00:00:00 2001 From: Peter KW Date: Mon, 1 Apr 2024 02:17:02 +0200 Subject: [PATCH 670/818] Removed unused code --- frontend/src/Components/ProjectListAdmin.tsx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/frontend/src/Components/ProjectListAdmin.tsx b/frontend/src/Components/ProjectListAdmin.tsx index f25ee47..7305ea4 100644 --- a/frontend/src/Components/ProjectListAdmin.tsx +++ b/frontend/src/Components/ProjectListAdmin.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import { NewProject } from "../Types/goTypes"; import ProjectInfoModal from "./ProjectInfoModal"; -import UserInfoModal from "./UserInfoModal"; +import MemberInfoModal from "./MemberInfoModal"; /** * A list of projects for admin manage projects page, that sets an onClick @@ -51,13 +51,8 @@ export function ProjectListAdmin(props: { isVisible={projectModalVisible} projectname={projectname} /> - { - return; - }} isVisible={userModalVisible} username={username} /> From dc98fb510e896259169b658b0453c60ceb9627f6 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Mon, 1 Apr 2024 02:17:57 +0200 Subject: [PATCH 671/818] Clears username+password fields on successful register --- frontend/src/Components/Register.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/Components/Register.tsx b/frontend/src/Components/Register.tsx index 6192637..8a22806 100644 --- a/frontend/src/Components/Register.tsx +++ b/frontend/src/Components/Register.tsx @@ -22,6 +22,8 @@ export default function Register(): JSX.Element { const response = await api.registerUser(newUser); if (response.success) { alert("User added!"); + setPassword(""); + setUsername(""); } else { alert("User not added"); setErrMessage(response.message ?? "Unknown error"); From 1212b3c5efb301e895edee48e08a5063afdcf06c Mon Sep 17 00:00:00 2001 From: Peter KW Date: Mon, 1 Apr 2024 02:20:48 +0200 Subject: [PATCH 672/818] Removed some stuff --- frontend/src/Components/UserListAdmin.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/frontend/src/Components/UserListAdmin.tsx b/frontend/src/Components/UserListAdmin.tsx index c08b05c..76cae9f 100644 --- a/frontend/src/Components/UserListAdmin.tsx +++ b/frontend/src/Components/UserListAdmin.tsx @@ -1,6 +1,5 @@ import { useState } from "react"; import UserInfoModal from "./UserInfoModal"; -import DeleteUser from "./DeleteUser"; /** * A list of users for admin manage users page, that sets an onClick @@ -30,9 +29,7 @@ export function UserListAdmin(props: { users: string[] }): JSX.Element { return ( <> DeleteUser} isVisible={modalVisible} username={username} /> From f3466854c7e0664d4e4045f6617c8382589c2089 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Mon, 1 Apr 2024 02:24:26 +0200 Subject: [PATCH 673/818] Removed unused pages and paths to them in main --- .../Pages/AdminPages/AdminChangeUsername.tsx | 28 ------------------- .../AdminPages/AdminProjectChangeUserRole.tsx | 23 --------------- frontend/src/main.tsx | 11 -------- 3 files changed, 62 deletions(-) delete mode 100644 frontend/src/Pages/AdminPages/AdminChangeUsername.tsx delete mode 100644 frontend/src/Pages/AdminPages/AdminProjectChangeUserRole.tsx diff --git a/frontend/src/Pages/AdminPages/AdminChangeUsername.tsx b/frontend/src/Pages/AdminPages/AdminChangeUsername.tsx deleted file mode 100644 index b130fae..0000000 --- a/frontend/src/Pages/AdminPages/AdminChangeUsername.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import BackButton from "../../Components/BackButton"; -import BasicWindow from "../../Components/BasicWindow"; -import Button from "../../Components/Button"; -import ChangeUsername from "../../Components/ChangeUsername"; - -function AdminChangeUsername(): JSX.Element { - const content = ( - <> - - - ); - - const buttons = ( - <> - + ); +} From bcac9c020eeee96e9ffce70ac6252375811d4bef Mon Sep 17 00:00:00 2001 From: Peter KW Date: Sat, 13 Apr 2024 21:09:07 +0200 Subject: [PATCH 782/818] Checks if new project name meets requirements --- frontend/src/Components/ProjectInfoModal.tsx | 27 ++++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/frontend/src/Components/ProjectInfoModal.tsx b/frontend/src/Components/ProjectInfoModal.tsx index 66cffc2..71a72fb 100644 --- a/frontend/src/Components/ProjectInfoModal.tsx +++ b/frontend/src/Components/ProjectInfoModal.tsx @@ -5,11 +5,14 @@ import { Link } from "react-router-dom"; import GetProjectTimes, { projectTimes } from "./GetProjectTimes"; import DeleteProject from "./DeleteProject"; import InputField from "./InputField"; +import ProjectNameInput from "./Inputs/ProjectNameInput"; +import { alphanumeric } from "../Data/regex"; +import { projNameHighLimit, projNameLowLimit } from "../Data/constants"; function ProjectInfoModal(props: { projectname: string; onClose: () => void; - onClick: (username: string) => void; + onClick: (username: string, userRole: string) => void; }): JSX.Element { const [showInput, setShowInput] = useState(false); const [users, setUsers] = useState([]); @@ -23,6 +26,7 @@ function ProjectInfoModal(props: { const handleChangeNameView = (): void => { if (showInput) { + setNewProjName(""); setShowInput(false); } else { setShowInput(true); @@ -30,7 +34,16 @@ function ProjectInfoModal(props: { }; const handleClickChangeName = (): void => { - if (newProjName === "") return; + if ( + newProjName.length > projNameHighLimit || + newProjName.length < projNameLowLimit || + !alphanumeric.test(newProjName) + ) { + alert( + "Please provide valid project name: \n-Between 10-99 characters \n-No special characters (.-!?/*)", + ); + return; + } if ( confirm( @@ -68,14 +81,12 @@ function ProjectInfoModal(props: { {showInput && ( <>

Change name:

-
- +
+ )} + {showPwordInput && ( +
+ { + setNewPassword(e.target.value); + }} + /> +