diff --git a/flake.lock b/flake.lock
index 83909bb..203568a 100644
--- a/flake.lock
+++ b/flake.lock
@@ -2,16 +2,16 @@
"nodes": {
"nixpkgs": {
"locked": {
- "lastModified": 1721497942,
- "narHash": "sha256-EDPL9qJfklXoowl3nEBmjDIqcvXKUZInt5n6CCc1Hn4=",
+ "lastModified": 1726463316,
+ "narHash": "sha256-gI9kkaH0ZjakJOKrdjaI/VbaMEo9qBbSUl93DnU7f4c=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "d43f0636fc9492e83be8bbb41f9595d7a87106b8",
+ "rev": "99dc8785f6a0adac95f5e2ab05cc2e1bf666d172",
"type": "github"
},
"original": {
"owner": "NixOS",
- "ref": "nixpkgs-unstable",
+ "ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
diff --git a/flake.nix b/flake.nix
index e3503a2..929608e 100644
--- a/flake.nix
+++ b/flake.nix
@@ -1,6 +1,6 @@
{
inputs = {
- nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
+ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs =
diff --git a/nix/deploy/qs/default.nix b/nix/deploy/qs/default.nix
index dabd468..22e14dc 100644
--- a/nix/deploy/qs/default.nix
+++ b/nix/deploy/qs/default.nix
@@ -15,5 +15,6 @@
acmeEmail = "thilo.hohlt@tutanota.com";
dnsProvider = "porkbun";
dnsEnvironmentFile = /var/lib/porkbun.env;
+ disableRegistration = true;
};
}
diff --git a/nix/module.nix b/nix/module.nix
index d939861..dcf73b1 100644
--- a/nix/module.nix
+++ b/nix/module.nix
@@ -75,6 +75,12 @@ in
default = null;
description = "API secrets for the DNS-01 challenge (required for wildcard domains).";
};
+
+ disableRegistration = mkOption {
+ type = types.bool;
+ default = false;
+ description = "By default any user can create an account. That behavior can be disabled by using this option.";
+ };
};
config = mkIf cfg.enable {
@@ -108,7 +114,7 @@ in
${pkgs.dbmate}/bin/dbmate --url postgres://postgres@localhost:5432/archtika?sslmode=disable --migrations-dir ${cfg.package}/rest-api/db/migrations up
- PGRST_ADMIN_SERVER_PORT=${toString cfg.apiAdminPort} PGRST_SERVER_PORT=${toString cfg.apiPort} PGRST_DB_SCHEMAS="api" PGRST_DB_ANON_ROLE="anon" PGRST_OPENAPI_MODE="ignore-privileges" PGRST_DB_URI="postgres://authenticator@localhost:5432/${cfg.databaseName}" PGRST_JWT_SECRET="$JWT_SECRET" ${pkgs.postgrest}/bin/postgrest
+ PGRST_SERVER_CORS_ALLOWED_ORIGINS="https://${cfg.domain}" PGRST_ADMIN_SERVER_PORT=${toString cfg.apiAdminPort} PGRST_SERVER_PORT=${toString cfg.apiPort} PGRST_DB_SCHEMAS="api" PGRST_DB_ANON_ROLE="anon" PGRST_OPENAPI_MODE="ignore-privileges" PGRST_DB_URI="postgres://authenticator@localhost:5432/${cfg.databaseName}" PGRST_JWT_SECRET="$JWT_SECRET" ${pkgs.postgrest}/bin/postgrest
'';
};
@@ -125,7 +131,7 @@ in
};
script = ''
- BODY_SIZE_LIMIT=Infinity ORIGIN=https://${cfg.domain} PORT=${toString cfg.webAppPort} ${pkgs.nodejs_22}/bin/node ${cfg.package}/web-app
+ REGISTRATION_IS_DISABLED=${toString cfg.disableRegistration} BODY_SIZE_LIMIT=10M ORIGIN=https://${cfg.domain} PORT=${toString cfg.webAppPort} ${pkgs.nodejs_22}/bin/node ${cfg.package}/web-app
'';
};
@@ -148,6 +154,20 @@ in
enable = true;
recommendedProxySettings = true;
recommendedTlsSettings = true;
+ recommendedZstdSettings = true;
+ recommendedOptimisation = true;
+
+ appendHttpConfig = ''
+ limit_req_zone $binary_remote_addr zone=requestLimit:10m rate=5r/s;
+ limit_req_status 429;
+ limit_req zone=requestLimit burst=20 nodelay;
+
+ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
+ add_header X-Frame-Options "SAMEORIGIN" always;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
+ add_header Permissions-Policy "accelerometer=(),autoplay=(),camera=(),cross-origin-isolated=(),display-capture=(),encrypted-media=(),fullscreen=(self),geolocation=(),gyroscope=(),keyboard-map=(),magnetometer=(),microphone=(),midi=(),payment=(),picture-in-picture=(self),publickey-credentials-get=(),screen-wake-lock=(),sync-xhr=(self),usb=(),xr-spatial-tracking=(),clipboard-read=(self),clipboard-write=(self),gamepad=(),hid=(),idle-detection=(),interest-cohort=(),serial=(),unload=()" always;
+ '';
virtualHosts = {
"${cfg.domain}" = {
@@ -165,14 +185,17 @@ in
"/api/" = {
proxyPass = "http://localhost:${toString cfg.apiPort}/";
extraConfig = ''
- default_type application/json;
- proxy_set_header Connection "";
- proxy_http_version 1.1;
+ default_type application/json;
+ '';
+ };
+ "/api/rpc/register" = mkIf cfg.disableRegistration {
+ extraConfig = ''
+ deny all;
'';
};
};
};
- "~^(?.+)\\.${lib.strings.escapeRegex cfg.domain}$" = {
+ "~^(?.+)\\.${cfg.domain}$" = {
useACMEHost = cfg.domain;
forceSSL = true;
locations = {
diff --git a/rest-api/db/migrations/20240719071602_main_tables.sql b/rest-api/db/migrations/20240719071602_main_tables.sql
index 0a1ca79..2c023ab 100644
--- a/rest-api/db/migrations/20240719071602_main_tables.sql
+++ b/rest-api/db/migrations/20240719071602_main_tables.sql
@@ -25,7 +25,8 @@ CREATE TABLE internal.user (
id UUID PRIMARY KEY DEFAULT gen_random_uuid (),
username VARCHAR(16) UNIQUE NOT NULL CHECK (LENGTH(username) >= 3),
password_hash CHAR(60) NOT NULL,
- role NAME NOT NULL DEFAULT 'authenticated_user'
+ role NAME NOT NULL DEFAULT 'authenticated_user',
+ created_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP()
);
CREATE TABLE internal.website (
@@ -33,8 +34,8 @@ CREATE TABLE internal.website (
user_id UUID REFERENCES internal.user (id) ON DELETE CASCADE NOT NULL DEFAULT (CURRENT_SETTING('request.jwt.claims', TRUE)::JSON ->> 'user_id') ::UUID,
content_type VARCHAR(10) CHECK (content_type IN ('Blog', 'Docs')) NOT NULL,
title VARCHAR(50) NOT NULL CHECK (TRIM(title) != ''),
- created_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
is_published BOOLEAN NOT NULL DEFAULT FALSE,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
last_modified_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
last_modified_by UUID REFERENCES internal.user (id) ON DELETE SET NULL,
title_search TSVECTOR GENERATED ALWAYS AS (TO_TSVECTOR('english', title)) STORED
@@ -52,8 +53,10 @@ CREATE TABLE internal.media (
CREATE TABLE internal.settings (
website_id UUID PRIMARY KEY REFERENCES internal.website (id) ON DELETE CASCADE,
- accent_color_light_theme CHAR(7) CHECK (accent_color_light_theme ~ '^#[a-fA-F0-9]{6}$') NOT NULL DEFAULT '#a5d8ff',
- accent_color_dark_theme CHAR(7) CHECK (accent_color_dark_theme ~ '^#[a-fA-F0-9]{6}$') NOT NULL DEFAULT '#114678',
+ accent_color_dark_theme CHAR(7) CHECK (accent_color_light_theme ~ '^#[a-fA-F0-9]{6}$') NOT NULL DEFAULT '#a5d8ff',
+ accent_color_light_theme CHAR(7) CHECK (accent_color_dark_theme ~ '^#[a-fA-F0-9]{6}$') NOT NULL DEFAULT '#114678',
+ background_color_dark_theme CHAR(7) CHECK (accent_color_light_theme ~ '^#[a-fA-F0-9]{6}$') NOT NULL DEFAULT '#262626',
+ background_color_light_theme CHAR(7) CHECK (accent_color_dark_theme ~ '^#[a-fA-F0-9]{6}$') NOT NULL DEFAULT '#ffffff',
favicon_image UUID REFERENCES internal.media (id) ON DELETE SET NULL,
last_modified_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
last_modified_by UUID REFERENCES internal.user (id) ON DELETE SET NULL
@@ -82,6 +85,7 @@ CREATE TABLE internal.docs_category (
user_id UUID REFERENCES internal.user (id) ON DELETE SET NULL DEFAULT (CURRENT_SETTING('request.jwt.claims', TRUE)::JSON ->> 'user_id') ::UUID,
category_name VARCHAR(50) NOT NULL CHECK (TRIM(category_name) != ''),
category_weight INTEGER CHECK (category_weight >= 0) NOT NULL,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
last_modified_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
last_modified_by UUID REFERENCES internal.user (id) ON DELETE SET NULL,
UNIQUE (website_id, category_name),
@@ -117,6 +121,7 @@ CREATE TABLE internal.footer (
CREATE TABLE internal.legal_information (
website_id UUID PRIMARY KEY REFERENCES internal.website (id) ON DELETE CASCADE,
main_content TEXT NOT NULL CHECK (TRIM(main_content) != ''),
+ created_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
last_modified_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
last_modified_by UUID REFERENCES internal.user (id) ON DELETE SET NULL
);
diff --git a/rest-api/db/migrations/20240720132802_exposed_views_functions.sql b/rest-api/db/migrations/20240720132802_exposed_views_functions.sql
index 1611e91..0e7e4aa 100644
--- a/rest-api/db/migrations/20240720132802_exposed_views_functions.sql
+++ b/rest-api/db/migrations/20240720132802_exposed_views_functions.sql
@@ -127,39 +127,39 @@ GRANT SELECT ON api.account TO authenticated_user;
GRANT SELECT ON api.user TO authenticated_user;
-GRANT SELECT, UPDATE, DELETE ON internal.website TO authenticated_user;
+GRANT SELECT, UPDATE (title, is_published), DELETE ON internal.website TO authenticated_user;
GRANT SELECT, UPDATE, DELETE ON api.website TO authenticated_user;
-GRANT SELECT, UPDATE ON internal.settings TO authenticated_user;
+GRANT SELECT, UPDATE (accent_color_dark_theme, accent_color_light_theme, background_color_dark_theme, background_color_light_theme, favicon_image) ON internal.settings TO authenticated_user;
GRANT SELECT, UPDATE ON api.settings TO authenticated_user;
-GRANT SELECT, UPDATE ON internal.header TO authenticated_user;
+GRANT SELECT, UPDATE (logo_type, logo_text, logo_image) ON internal.header TO authenticated_user;
GRANT SELECT, UPDATE ON api.header TO authenticated_user;
-GRANT SELECT, UPDATE ON internal.home TO authenticated_user;
+GRANT SELECT, UPDATE (main_content) ON internal.home TO authenticated_user;
GRANT SELECT, UPDATE ON api.home TO authenticated_user;
-GRANT SELECT, INSERT, UPDATE, DELETE ON internal.article TO authenticated_user;
+GRANT SELECT, INSERT (website_id, title, meta_description, meta_author, cover_image, publication_date, main_content, category, article_weight), UPDATE (title, meta_description, meta_author, cover_image, publication_date, main_content, category, article_weight), DELETE ON internal.article TO authenticated_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON api.article TO authenticated_user;
-GRANT SELECT, INSERT, UPDATE, DELETE ON internal.docs_category TO authenticated_user;
+GRANT SELECT, INSERT (website_id, category_name, category_weight), UPDATE (category_name, category_weight), DELETE ON internal.docs_category TO authenticated_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON api.docs_category TO authenticated_user;
-GRANT SELECT, UPDATE ON internal.footer TO authenticated_user;
+GRANT SELECT, UPDATE (additional_text) ON internal.footer TO authenticated_user;
GRANT SELECT, UPDATE ON api.footer TO authenticated_user;
-GRANT SELECT, INSERT, UPDATE, DELETE ON internal.legal_information TO authenticated_user;
+GRANT SELECT, INSERT (website_id, main_content), UPDATE (website_id, main_content), DELETE ON internal.legal_information TO authenticated_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON api.legal_information TO authenticated_user;
-GRANT SELECT, INSERT, UPDATE, DELETE ON internal.collab TO authenticated_user;
+GRANT SELECT, INSERT (website_id, user_id, permission_level), UPDATE (permission_level), DELETE ON internal.collab TO authenticated_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON api.collab TO authenticated_user;
diff --git a/rest-api/db/migrations/20240805132306_last_modified_triggers.sql b/rest-api/db/migrations/20240805132306_last_modified_triggers.sql
index ea8794b..bcc55e1 100644
--- a/rest-api/db/migrations/20240805132306_last_modified_triggers.sql
+++ b/rest-api/db/migrations/20240805132306_last_modified_triggers.sql
@@ -30,7 +30,8 @@ BEGIN
RETURN COALESCE(NEW, OLD);
END;
$$
-LANGUAGE plpgsql;
+LANGUAGE plpgsql
+SECURITY DEFINER;
CREATE TRIGGER update_website_last_modified
BEFORE UPDATE ON internal.website
@@ -68,7 +69,7 @@ CREATE TRIGGER update_footer_last_modified
EXECUTE FUNCTION internal.update_last_modified ();
CREATE TRIGGER update_legal_information_last_modified
- BEFORE INSERT OR DELETE ON internal.legal_information
+ BEFORE INSERT OR UPDATE OR DELETE ON internal.legal_information
FOR EACH ROW
EXECUTE FUNCTION internal.update_last_modified ();
diff --git a/rest-api/db/migrations/20240810115846_image_upload_function.sql b/rest-api/db/migrations/20240810115846_image_upload_function.sql
index e94d73a..dd9af74 100644
--- a/rest-api/db/migrations/20240810115846_image_upload_function.sql
+++ b/rest-api/db/migrations/20240810115846_image_upload_function.sql
@@ -8,7 +8,7 @@ DECLARE
_website_id UUID := (_headers ->> 'x-website-id')::UUID;
_mimetype TEXT := _headers ->> 'x-mimetype';
_original_filename TEXT := _headers ->> 'x-original-filename';
- _allowed_mimetypes TEXT[] := ARRAY['image/png', 'image/jpeg', 'image/webp'];
+ _allowed_mimetypes TEXT[] := ARRAY['image/png', 'image/jpeg', 'image/webp', 'image/avif', 'image/gif', 'image/svg+xml'];
_max_file_size INT := 5 * 1024 * 1024;
_has_access BOOLEAN;
BEGIN
diff --git a/rest-api/db/migrations/20240920090915_custom_domain_prefix.sql b/rest-api/db/migrations/20240920090915_custom_domain_prefix.sql
new file mode 100644
index 0000000..ba1dece
--- /dev/null
+++ b/rest-api/db/migrations/20240920090915_custom_domain_prefix.sql
@@ -0,0 +1,57 @@
+-- migrate:up
+CREATE TABLE internal.domain_prefix (
+ website_id UUID PRIMARY KEY REFERENCES internal.website (id) ON DELETE CASCADE,
+ prefix VARCHAR(16) UNIQUE NOT NULL CHECK (LENGTH(prefix) >= 3 AND prefix ~ '^[a-z]+(-[a-z]+)*$'),
+ created_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
+ last_modified_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
+ last_modified_by UUID REFERENCES internal.user (id) ON DELETE SET NULL
+);
+
+CREATE VIEW api.domain_prefix WITH ( security_invoker = ON
+) AS
+SELECT
+ *
+FROM
+ internal.domain_prefix;
+
+GRANT SELECT, INSERT (website_id, prefix), UPDATE (website_id, prefix), DELETE ON internal.domain_prefix TO authenticated_user;
+
+GRANT SELECT, INSERT, UPDATE, DELETE ON api.domain_prefix TO authenticated_user;
+
+ALTER TABLE internal.domain_prefix ENABLE ROW LEVEL SECURITY;
+
+CREATE POLICY view_domain_prefix ON internal.domain_prefix
+ FOR SELECT
+ USING (internal.user_has_website_access (website_id, 10));
+
+CREATE POLICY update_domain_prefix ON internal.domain_prefix
+ FOR UPDATE
+ USING (internal.user_has_website_access (website_id, 30));
+
+CREATE POLICY delete_domain_prefix ON internal.domain_prefix
+ FOR DELETE
+ USING (internal.user_has_website_access (website_id, 30));
+
+CREATE POLICY insert_domain_prefix ON internal.domain_prefix
+ FOR INSERT
+ WITH CHECK (internal.user_has_website_access (website_id, 30));
+
+CREATE TRIGGER update_domain_prefix_last_modified
+ BEFORE INSERT OR UPDATE OR DELETE ON internal.domain_prefix
+ FOR EACH ROW
+ EXECUTE FUNCTION internal.update_last_modified ();
+
+CREATE TRIGGER domain_prefix_track_changes
+ AFTER INSERT OR UPDATE OR DELETE ON internal.domain_prefix
+ FOR EACH ROW
+ EXECUTE FUNCTION internal.track_changes ();
+
+-- migrate:down
+DROP TRIGGER domain_prefix_track_changes ON internal.domain_prefix;
+
+DROP TRIGGER update_domain_prefix_last_modified ON internal.domain_prefix;
+
+DROP VIEW api.domain_prefix;
+
+DROP TABLE internal.domain_prefix;
+
diff --git a/web-app/package-lock.json b/web-app/package-lock.json
index 97b6ede..12e611a 100644
--- a/web-app/package-lock.json
+++ b/web-app/package-lock.json
@@ -10,32 +10,32 @@
"dependencies": {
"fast-diff": "1.3.0",
"highlight.js": "11.10.0",
- "isomorphic-dompurify": "2.14.0",
- "marked": "14.0.0",
+ "isomorphic-dompurify": "2.15.0",
+ "marked": "14.1.2",
"marked-highlight": "2.1.4"
},
"devDependencies": {
- "@playwright/test": "1.40.0",
- "@sveltejs/adapter-auto": "3.2.4",
- "@sveltejs/adapter-node": "5.2.2",
- "@sveltejs/kit": "2.5.22",
- "@sveltejs/vite-plugin-svelte": "3.1.1",
+ "@playwright/test": "1.46.0",
+ "@sveltejs/adapter-auto": "3.2.5",
+ "@sveltejs/adapter-node": "5.2.3",
+ "@sveltejs/kit": "2.5.28",
+ "@sveltejs/vite-plugin-svelte": "4.0.0-next.6",
"@types/eslint": "9.6.1",
"@types/eslint__js": "8.42.3",
"@types/eslint-config-prettier": "6.11.3",
- "@types/node": "22.2.0",
+ "@types/node": "22.5.5",
"eslint": "9.10.0",
"eslint-config-prettier": "9.1.0",
- "eslint-plugin-svelte": "2.43.0",
+ "eslint-plugin-svelte": "2.44.0",
"globals": "15.9.0",
"pg-to-ts": "4.1.1",
"prettier": "3.3.3",
"prettier-plugin-svelte": "3.2.6",
- "svelte": "5.0.0-next.220",
- "svelte-check": "3.8.5",
- "typescript": "5.5.4",
- "typescript-eslint": "8.4.0",
- "vite": "5.4.0"
+ "svelte": "5.0.0-next.253",
+ "svelte-check": "4.0.2",
+ "typescript": "5.6.2",
+ "typescript-eslint": "8.6.0",
+ "vite": "5.4.6"
}
},
"node_modules/@ampproject/remapping": {
@@ -764,19 +764,19 @@
}
},
"node_modules/@playwright/test": {
- "version": "1.40.0",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.40.0.tgz",
- "integrity": "sha512-PdW+kn4eV99iP5gxWNSDQCbhMaDVej+RXL5xr6t04nbKLCBwYtA046t7ofoczHOm8u6c+45hpDKQVZqtqwkeQg==",
+ "version": "1.46.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.46.0.tgz",
+ "integrity": "sha512-/QYft5VArOrGRP5pgkrfKksqsKA6CEFyGQ/gjNe6q0y4tZ1aaPfq4gIjudr1s3D+pXyrPRdsy4opKDrjBabE5w==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "playwright": "1.40.0"
+ "playwright": "1.46.0"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
- "node": ">=16"
+ "node": ">=18"
}
},
"node_modules/@polka/url": {
@@ -883,9 +883,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.20.0.tgz",
- "integrity": "sha512-TSpWzflCc4VGAUJZlPpgAJE1+V60MePDQnBd7PPkpuEmOy8i87aL6tinFGKBFKuEDikYpig72QzdT3QPYIi+oA==",
+ "version": "4.22.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz",
+ "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==",
"cpu": [
"arm"
],
@@ -897,9 +897,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.20.0.tgz",
- "integrity": "sha512-u00Ro/nok7oGzVuh/FMYfNoGqxU5CPWz1mxV85S2w9LxHR8OoMQBuSk+3BKVIDYgkpeOET5yXkx90OYFc+ytpQ==",
+ "version": "4.22.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz",
+ "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==",
"cpu": [
"arm64"
],
@@ -911,9 +911,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.20.0.tgz",
- "integrity": "sha512-uFVfvzvsdGtlSLuL0ZlvPJvl6ZmrH4CBwLGEFPe7hUmf7htGAN+aXo43R/V6LATyxlKVC/m6UsLb7jbG+LG39Q==",
+ "version": "4.22.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz",
+ "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==",
"cpu": [
"arm64"
],
@@ -925,9 +925,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.20.0.tgz",
- "integrity": "sha512-xbrMDdlev53vNXexEa6l0LffojxhqDTBeL+VUxuuIXys4x6xyvbKq5XqTXBCEUA8ty8iEJblHvFaWRJTk/icAQ==",
+ "version": "4.22.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz",
+ "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==",
"cpu": [
"x64"
],
@@ -939,9 +939,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.20.0.tgz",
- "integrity": "sha512-jMYvxZwGmoHFBTbr12Xc6wOdc2xA5tF5F2q6t7Rcfab68TT0n+r7dgawD4qhPEvasDsVpQi+MgDzj2faOLsZjA==",
+ "version": "4.22.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz",
+ "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==",
"cpu": [
"arm"
],
@@ -953,9 +953,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.20.0.tgz",
- "integrity": "sha512-1asSTl4HKuIHIB1GcdFHNNZhxAYEdqML/MW4QmPS4G0ivbEcBr1JKlFLKsIRqjSwOBkdItn3/ZDlyvZ/N6KPlw==",
+ "version": "4.22.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz",
+ "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==",
"cpu": [
"arm"
],
@@ -967,9 +967,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.20.0.tgz",
- "integrity": "sha512-COBb8Bkx56KldOYJfMf6wKeYJrtJ9vEgBRAOkfw6Ens0tnmzPqvlpjZiLgkhg6cA3DGzCmLmmd319pmHvKWWlQ==",
+ "version": "4.22.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz",
+ "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==",
"cpu": [
"arm64"
],
@@ -981,9 +981,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.20.0.tgz",
- "integrity": "sha512-+it+mBSyMslVQa8wSPvBx53fYuZK/oLTu5RJoXogjk6x7Q7sz1GNRsXWjn6SwyJm8E/oMjNVwPhmNdIjwP135Q==",
+ "version": "4.22.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz",
+ "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==",
"cpu": [
"arm64"
],
@@ -995,9 +995,9 @@
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.20.0.tgz",
- "integrity": "sha512-yAMvqhPfGKsAxHN8I4+jE0CpLWD8cv4z7CK7BMmhjDuz606Q2tFKkWRY8bHR9JQXYcoLfopo5TTqzxgPUjUMfw==",
+ "version": "4.22.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz",
+ "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==",
"cpu": [
"ppc64"
],
@@ -1009,9 +1009,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.20.0.tgz",
- "integrity": "sha512-qmuxFpfmi/2SUkAw95TtNq/w/I7Gpjurx609OOOV7U4vhvUhBcftcmXwl3rqAek+ADBwSjIC4IVNLiszoj3dPA==",
+ "version": "4.22.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz",
+ "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==",
"cpu": [
"riscv64"
],
@@ -1023,9 +1023,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.20.0.tgz",
- "integrity": "sha512-I0BtGXddHSHjV1mqTNkgUZLnS3WtsqebAXv11D5BZE/gfw5KoyXSAXVqyJximQXNvNzUo4GKlCK/dIwXlz+jlg==",
+ "version": "4.22.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz",
+ "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==",
"cpu": [
"s390x"
],
@@ -1037,9 +1037,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.20.0.tgz",
- "integrity": "sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==",
+ "version": "4.22.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz",
+ "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==",
"cpu": [
"x64"
],
@@ -1051,9 +1051,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.20.0.tgz",
- "integrity": "sha512-hM3nhW40kBNYUkZb/r9k2FKK+/MnKglX7UYd4ZUy5DJs8/sMsIbqWK2piZtVGE3kcXVNj3B2IrUYROJMMCikNg==",
+ "version": "4.22.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz",
+ "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==",
"cpu": [
"x64"
],
@@ -1065,9 +1065,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.20.0.tgz",
- "integrity": "sha512-psegMvP+Ik/Bg7QRJbv8w8PAytPA7Uo8fpFjXyCRHWm6Nt42L+JtoqH8eDQ5hRP7/XW2UiIriy1Z46jf0Oa1kA==",
+ "version": "4.22.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz",
+ "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==",
"cpu": [
"arm64"
],
@@ -1079,9 +1079,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.20.0.tgz",
- "integrity": "sha512-GabekH3w4lgAJpVxkk7hUzUf2hICSQO0a/BLFA11/RMxQT92MabKAqyubzDZmMOC/hcJNlc+rrypzNzYl4Dx7A==",
+ "version": "4.22.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz",
+ "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==",
"cpu": [
"ia32"
],
@@ -1093,9 +1093,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.20.0.tgz",
- "integrity": "sha512-aJ1EJSuTdGnM6qbVC4B5DSmozPTqIag9fSzXRNNo+humQLG89XpPgdt16Ia56ORD7s+H8Pmyx44uczDQ0yDzpg==",
+ "version": "4.22.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz",
+ "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==",
"cpu": [
"x64"
],
@@ -1107,9 +1107,9 @@
]
},
"node_modules/@sveltejs/adapter-auto": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-3.2.4.tgz",
- "integrity": "sha512-a64AKYbfTUrVwU0xslzv1Jf3M8bj0IwhptaXmhgIkjXspBXhD0od9JiItQHchijpLMGdEDcYBlvqySkEawv6mQ==",
+ "version": "3.2.5",
+ "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-3.2.5.tgz",
+ "integrity": "sha512-27LR+uKccZ62lgq4N/hvyU2G+hTP9fxWEAfnZcl70HnyfAjMSsGk1z/SjAPXNCD1mVJIE7IFu3TQ8cQ/UH3c0A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1120,9 +1120,9 @@
}
},
"node_modules/@sveltejs/adapter-node": {
- "version": "5.2.2",
- "resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.2.2.tgz",
- "integrity": "sha512-BCX4zP0cf86TXpmvLQTnnT/tp7P12UMezf+5LwljP1MJC1fFzn9XOXpAHQCyP+pyHGy2K7p5gY0LyLcZFAL02w==",
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.2.3.tgz",
+ "integrity": "sha512-0KNrTc9NiEhB1vyVL0HiqZaW2P5JWNJgTYT5PnUZCLO9Oydx8G+6PNtJPJ/NNPyeGrn+6LwR5L8GNRvA4b5Bpw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1136,9 +1136,9 @@
}
},
"node_modules/@sveltejs/kit": {
- "version": "2.5.22",
- "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.22.tgz",
- "integrity": "sha512-PQ98baF2WzvG5yiO4cZKJZJG60XjHTZD1jyho3u9Kmthx2ytdGYyVPPvKXgKXpKSq4wwctD9dl0d2blSbJMcOg==",
+ "version": "2.5.28",
+ "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.28.tgz",
+ "integrity": "sha512-/O7pvFGBsQPcFa9UrW8eUC5uHTOXLsUp3SN0dY6YmRAL9nfPSrJsSJk//j5vMpinSshzUjteAFcfQTU+04Ka1w==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -1163,65 +1163,51 @@
"node": ">=18.13"
},
"peerDependencies": {
- "@sveltejs/vite-plugin-svelte": "^3.0.0",
+ "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1",
"svelte": "^4.0.0 || ^5.0.0-next.0",
"vite": "^5.0.3"
}
},
"node_modules/@sveltejs/vite-plugin-svelte": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.1.tgz",
- "integrity": "sha512-rimpFEAboBBHIlzISibg94iP09k/KYdHgVhJlcsTfn7KMBhc70jFX/GRWkRdFCc2fdnk+4+Bdfej23cMDnJS6A==",
+ "version": "4.0.0-next.6",
+ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-4.0.0-next.6.tgz",
+ "integrity": "sha512-7+bEFN5F9pthG6nOEHNz9yioHxNXK6yl+0GnTy9WOfxN/SvPykkH/Hs6MqTGjo47a9G2q3QXQnzuxG5WXNX4Tg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0",
- "debug": "^4.3.4",
+ "@sveltejs/vite-plugin-svelte-inspector": "^3.0.0-next.0||^3.0.0",
+ "debug": "^4.3.6",
"deepmerge": "^4.3.1",
"kleur": "^4.1.5",
- "magic-string": "^0.30.10",
- "svelte-hmr": "^0.16.0",
+ "magic-string": "^0.30.11",
"vitefu": "^0.2.5"
},
"engines": {
- "node": "^18.0.0 || >=20"
+ "node": "^18.0.0 || ^20.0.0 || >=22"
},
"peerDependencies": {
- "svelte": "^4.0.0 || ^5.0.0-next.0",
+ "svelte": "^5.0.0-next.96 || ^5.0.0",
"vite": "^5.0.0"
}
},
"node_modules/@sveltejs/vite-plugin-svelte-inspector": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz",
- "integrity": "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==",
+ "version": "3.0.0-next.3",
+ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-3.0.0-next.3.tgz",
+ "integrity": "sha512-kuGJ2CZ5lAw3gKF8Kw0AfKtUJWbwdlDHY14K413B0MCyrzvQvsKTorwmwZcky0+QqY6RnVIZ/5FttB9bQmkLXg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "debug": "^4.3.4"
+ "debug": "^4.3.5"
},
"engines": {
- "node": "^18.0.0 || >=20"
+ "node": "^18.0.0 || ^20.0.0 || >=22"
},
"peerDependencies": {
- "@sveltejs/vite-plugin-svelte": "^3.0.0",
- "svelte": "^4.0.0 || ^5.0.0-next.0",
+ "@sveltejs/vite-plugin-svelte": "^4.0.0-next.0||^4.0.0",
+ "svelte": "^5.0.0-next.96 || ^5.0.0",
"vite": "^5.0.0"
}
},
- "node_modules/@sveltejs/vite-plugin-svelte/node_modules/svelte-hmr": {
- "version": "0.16.0",
- "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz",
- "integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": "^12.20 || ^14.13.1 || >= 16"
- },
- "peerDependencies": {
- "svelte": "^3.19.0 || ^4.0.0"
- }
- },
"node_modules/@types/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
@@ -1281,22 +1267,15 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "22.2.0",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.2.0.tgz",
- "integrity": "sha512-bm6EG6/pCpkxDf/0gDNDdtDILMOHgaQBVOJGdwsqClnxA3xL6jtMv76rLBc006RVMWbmaf0xbmom4Z/5o2nRkQ==",
+ "version": "22.5.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz",
+ "integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "undici-types": "~6.13.0"
+ "undici-types": "~6.19.2"
}
},
- "node_modules/@types/pug": {
- "version": "2.0.10",
- "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz",
- "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@types/resolve": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
@@ -1311,17 +1290,17 @@
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.4.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.4.0.tgz",
- "integrity": "sha512-rg8LGdv7ri3oAlenMACk9e+AR4wUV0yrrG+XKsGKOK0EVgeEDqurkXMPILG2836fW4ibokTB5v4b6Z9+GYQDEw==",
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.6.0.tgz",
+ "integrity": "sha512-UOaz/wFowmoh2G6Mr9gw60B1mm0MzUtm6Ic8G2yM1Le6gyj5Loi/N+O5mocugRGY+8OeeKmkMmbxNqUCq3B4Sg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.4.0",
- "@typescript-eslint/type-utils": "8.4.0",
- "@typescript-eslint/utils": "8.4.0",
- "@typescript-eslint/visitor-keys": "8.4.0",
+ "@typescript-eslint/scope-manager": "8.6.0",
+ "@typescript-eslint/type-utils": "8.6.0",
+ "@typescript-eslint/utils": "8.6.0",
+ "@typescript-eslint/visitor-keys": "8.6.0",
"graphemer": "^1.4.0",
"ignore": "^5.3.1",
"natural-compare": "^1.4.0",
@@ -1345,16 +1324,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "8.4.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.4.0.tgz",
- "integrity": "sha512-NHgWmKSgJk5K9N16GIhQ4jSobBoJwrmURaLErad0qlLjrpP5bECYg+wxVTGlGZmJbU03jj/dfnb6V9bw+5icsA==",
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.6.0.tgz",
+ "integrity": "sha512-eQcbCuA2Vmw45iGfcyG4y6rS7BhWfz9MQuk409WD47qMM+bKCGQWXxvoOs1DUp+T7UBMTtRTVT+kXr7Sh4O9Ow==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.4.0",
- "@typescript-eslint/types": "8.4.0",
- "@typescript-eslint/typescript-estree": "8.4.0",
- "@typescript-eslint/visitor-keys": "8.4.0",
+ "@typescript-eslint/scope-manager": "8.6.0",
+ "@typescript-eslint/types": "8.6.0",
+ "@typescript-eslint/typescript-estree": "8.6.0",
+ "@typescript-eslint/visitor-keys": "8.6.0",
"debug": "^4.3.4"
},
"engines": {
@@ -1374,14 +1353,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "8.4.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.4.0.tgz",
- "integrity": "sha512-n2jFxLeY0JmKfUqy3P70rs6vdoPjHK8P/w+zJcV3fk0b0BwRXC/zxRTEnAsgYT7MwdQDt/ZEbtdzdVC+hcpF0A==",
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.6.0.tgz",
+ "integrity": "sha512-ZuoutoS5y9UOxKvpc/GkvF4cuEmpokda4wRg64JEia27wX+PysIE9q+lzDtlHHgblwUWwo5/Qn+/WyTUvDwBHw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.4.0",
- "@typescript-eslint/visitor-keys": "8.4.0"
+ "@typescript-eslint/types": "8.6.0",
+ "@typescript-eslint/visitor-keys": "8.6.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1392,14 +1371,14 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.4.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.4.0.tgz",
- "integrity": "sha512-pu2PAmNrl9KX6TtirVOrbLPLwDmASpZhK/XU7WvoKoCUkdtq9zF7qQ7gna0GBZFN0hci0vHaSusiL2WpsQk37A==",
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.6.0.tgz",
+ "integrity": "sha512-dtePl4gsuenXVwC7dVNlb4mGDcKjDT/Ropsk4za/ouMBPplCLyznIaR+W65mvCvsyS97dymoBRrioEXI7k0XIg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/typescript-estree": "8.4.0",
- "@typescript-eslint/utils": "8.4.0",
+ "@typescript-eslint/typescript-estree": "8.6.0",
+ "@typescript-eslint/utils": "8.6.0",
"debug": "^4.3.4",
"ts-api-utils": "^1.3.0"
},
@@ -1417,9 +1396,9 @@
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.4.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.4.0.tgz",
- "integrity": "sha512-T1RB3KQdskh9t3v/qv7niK6P8yvn7ja1mS7QK7XfRVL6wtZ8/mFs/FHf4fKvTA0rKnqnYxl/uHFNbnEt0phgbw==",
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.6.0.tgz",
+ "integrity": "sha512-rojqFZGd4MQxw33SrOy09qIDS8WEldM8JWtKQLAjf/X5mGSeEFh5ixQlxssMNyPslVIk9yzWqXCsV2eFhYrYUw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1431,14 +1410,14 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.4.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.4.0.tgz",
- "integrity": "sha512-kJ2OIP4dQw5gdI4uXsaxUZHRwWAGpREJ9Zq6D5L0BweyOrWsL6Sz0YcAZGWhvKnH7fm1J5YFE1JrQL0c9dd53A==",
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.6.0.tgz",
+ "integrity": "sha512-MOVAzsKJIPIlLK239l5s06YXjNqpKTVhBVDnqUumQJja5+Y94V3+4VUFRA0G60y2jNnTVwRCkhyGQpavfsbq/g==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
- "@typescript-eslint/types": "8.4.0",
- "@typescript-eslint/visitor-keys": "8.4.0",
+ "@typescript-eslint/types": "8.6.0",
+ "@typescript-eslint/visitor-keys": "8.6.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@@ -1460,16 +1439,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "8.4.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.4.0.tgz",
- "integrity": "sha512-swULW8n1IKLjRAgciCkTCafyTHHfwVQFt8DovmaF69sKbOxTSFMmIZaSHjqO9i/RV0wIblaawhzvtva8Nmm7lQ==",
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.6.0.tgz",
+ "integrity": "sha512-eNp9cWnYf36NaOVjkEUznf6fEgVy1TWpE0o52e4wtojjBx7D1UV2WAWGzR+8Y5lVFtpMLPwNbC67T83DWSph4A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
- "@typescript-eslint/scope-manager": "8.4.0",
- "@typescript-eslint/types": "8.4.0",
- "@typescript-eslint/typescript-estree": "8.4.0"
+ "@typescript-eslint/scope-manager": "8.6.0",
+ "@typescript-eslint/types": "8.6.0",
+ "@typescript-eslint/typescript-estree": "8.6.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1483,13 +1462,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.4.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.4.0.tgz",
- "integrity": "sha512-zTQD6WLNTre1hj5wp09nBIDiOc2U5r/qmzo7wxPn4ZgAjHql09EofqhF9WF+fZHzL5aCyaIpPcT2hyxl73kr9A==",
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.6.0.tgz",
+ "integrity": "sha512-wapVFfZg9H0qOYh4grNVQiMklJGluQrOUiOhYRrQWhx7BY/+I1IYb8BczWNbbUpO+pqy0rDciv3lQH5E1bCLrg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.4.0",
+ "@typescript-eslint/types": "8.6.0",
"eslint-visitor-keys": "^3.4.3"
},
"engines": {
@@ -1623,13 +1602,13 @@
"license": "Python-2.0"
},
"node_modules/aria-query": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
- "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz",
+ "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==",
"dev": true,
"license": "Apache-2.0",
- "dependencies": {
- "dequal": "^2.0.3"
+ "engines": {
+ "node": ">= 0.4"
}
},
"node_modules/assert-options": {
@@ -1701,16 +1680,6 @@
"node": ">=8"
}
},
- "node_modules/buffer-crc32": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz",
- "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8.0.0"
- }
- },
"node_modules/buffer-writer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
@@ -1995,23 +1964,17 @@
}
},
"node_modules/cssstyle": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.0.1.tgz",
- "integrity": "sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz",
+ "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==",
"license": "MIT",
"dependencies": {
- "rrweb-cssom": "^0.6.0"
+ "rrweb-cssom": "^0.7.1"
},
"engines": {
"node": ">=18"
}
},
- "node_modules/cssstyle/node_modules/rrweb-cssom": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz",
- "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==",
- "license": "MIT"
- },
"node_modules/data-urls": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
@@ -2074,26 +2037,6 @@
"node": ">=0.4.0"
}
},
- "node_modules/dequal": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
- "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/detect-indent": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
- "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/devalue": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.0.0.tgz",
@@ -2170,13 +2113,6 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
- "node_modules/es6-promise": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
- "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/esbuild": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
@@ -2329,9 +2265,9 @@
}
},
"node_modules/eslint-plugin-svelte": {
- "version": "2.43.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.43.0.tgz",
- "integrity": "sha512-REkxQWvg2pp7QVLxQNa+dJ97xUqRe7Y2JJbSWkHSuszu0VcblZtXkPBPckkivk99y5CdLw4slqfPylL2d/X4jQ==",
+ "version": "2.44.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.44.0.tgz",
+ "integrity": "sha512-wav4MOs02vBb1WjvTCYItwJCxMkuk2Z4p+K/eyjL0N/z7ahXLP+0LtQQjiKc2ezuif7GnZLbD1F3o1VHzSvdVg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2345,7 +2281,7 @@
"postcss-safe-parser": "^6.0.0",
"postcss-selector-parser": "^6.1.0",
"semver": "^7.6.2",
- "svelte-eslint-parser": "^0.41.0"
+ "svelte-eslint-parser": "^0.41.1"
},
"engines": {
"node": "^14.17.0 || >=16.0.0"
@@ -2691,13 +2627,6 @@
"node": ">= 6"
}
},
- "node_modules/fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -2794,13 +2723,6 @@
"dev": true,
"license": "MIT"
},
- "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,
- "license": "ISC"
- },
"node_modules/graphemer": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
@@ -2938,25 +2860,6 @@
"node": ">=0.8.19"
}
},
- "node_modules/inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
- "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "node_modules/inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -3086,14 +2989,14 @@
"license": "ISC"
},
"node_modules/isomorphic-dompurify": {
- "version": "2.14.0",
- "resolved": "https://registry.npmjs.org/isomorphic-dompurify/-/isomorphic-dompurify-2.14.0.tgz",
- "integrity": "sha512-7xyjuzBf3P/HBt0PbOpmv5LuV38TmfvidBFvgyuSWVMLwCGDITBPHWsBZ/L1a8DpcGz5PEintBeGdlrKzUqt5A==",
+ "version": "2.15.0",
+ "resolved": "https://registry.npmjs.org/isomorphic-dompurify/-/isomorphic-dompurify-2.15.0.tgz",
+ "integrity": "sha512-RDHlyeVmwEDAPZuX1VaaBzSn9RrsfvswxH7faEQK9cTHC1dXeNuK6ElUeSr7locFyeLguut8ASfhQWxHB4Ttug==",
"license": "MIT",
"dependencies": {
"@types/dompurify": "^3.0.5",
"dompurify": "^3.1.6",
- "jsdom": "^24.1.1"
+ "jsdom": "^25.0.0"
},
"engines": {
"node": ">=18"
@@ -3129,9 +3032,9 @@
}
},
"node_modules/jsdom": {
- "version": "24.1.3",
- "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.1.3.tgz",
- "integrity": "sha512-MyL55p3Ut3cXbeBEG7Hcv0mVM8pp8PBNWxRqchZnSfAiES1v1mRnMeFfaHWIPULpwsYfvO+ZmMZz5tGCnjzDUQ==",
+ "version": "25.0.0",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.0.tgz",
+ "integrity": "sha512-OhoFVT59T7aEq75TVw9xxEfkXgacpqAhQaYgP9y/fDqWQCMB/b1H66RfmPm/MaeaAIU9nDwMOVTlPN51+ao6CQ==",
"license": "MIT",
"dependencies": {
"cssstyle": "^4.0.1",
@@ -3295,9 +3198,9 @@
}
},
"node_modules/marked": {
- "version": "14.0.0",
- "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz",
- "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==",
+ "version": "14.1.2",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-14.1.2.tgz",
+ "integrity": "sha512-f3r0yqpz31VXiDB/wj9GaOB0a2PRLQl6vJmXiFrniNwjkKdvakqJRULhjFKJpxOchlCRiG5fcacoUZY5Xa6PEQ==",
"license": "MIT",
"bin": {
"marked": "bin/marked.js"
@@ -3360,16 +3263,6 @@
"node": ">= 0.6"
}
},
- "node_modules/min-indent": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
- "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
@@ -3386,16 +3279,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/minimist": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
- "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/minipass": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
@@ -3406,19 +3289,6 @@
"node": ">=16 || 14 >=14.17"
}
},
- "node_modules/mkdirp": {
- "version": "0.5.6",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
- "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "minimist": "^1.2.6"
- },
- "bin": {
- "mkdirp": "bin/cmd.js"
- }
- },
"node_modules/mri": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
@@ -3487,16 +3357,6 @@
"integrity": "sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w==",
"license": "MIT"
},
- "node_modules/once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "wrappy": "1"
- }
- },
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -3596,16 +3456,6 @@
"node": ">=8"
}
},
- "node_modules/path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -3777,9 +3627,9 @@
}
},
"node_modules/picocolors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
- "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
+ "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==",
"dev": true,
"license": "ISC"
},
@@ -3797,35 +3647,35 @@
}
},
"node_modules/playwright": {
- "version": "1.40.0",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.40.0.tgz",
- "integrity": "sha512-gyHAgQjiDf1m34Xpwzaqb76KgfzYrhK7iih+2IzcOCoZWr/8ZqmdBw+t0RU85ZmfJMgtgAiNtBQ/KS2325INXw==",
+ "version": "1.46.0",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.0.tgz",
+ "integrity": "sha512-XYJ5WvfefWONh1uPAUAi0H2xXV5S3vrtcnXe6uAOgdGi3aSpqOSXX08IAjXW34xitfuOJsvXU5anXZxPSEQiJw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "playwright-core": "1.40.0"
+ "playwright-core": "1.46.0"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
- "node": ">=16"
+ "node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
- "version": "1.40.0",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.0.tgz",
- "integrity": "sha512-fvKewVJpGeca8t0ipM56jkVSU6Eo0RmFvQ/MaCQNDYm+sdvKkMBBWTE1FdeMqIdumRaXXjZChWHvIzCGM/tA/Q==",
+ "version": "1.46.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.0.tgz",
+ "integrity": "sha512-9Y/d5UIwuJk8t3+lhmMSAJyNP1BUC/DqP3cQJDQQL/oWqAiuPTLgy7Q5dzglmTLwcBRdetzgNM/gni7ckfTr6A==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
- "node": ">=16"
+ "node": ">=18"
}
},
"node_modules/playwright/node_modules/fsevents": {
@@ -3844,9 +3694,9 @@
}
},
"node_modules/postcss": {
- "version": "8.4.40",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.40.tgz",
- "integrity": "sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==",
+ "version": "8.4.47",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
+ "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
"dev": true,
"funding": [
{
@@ -3865,8 +3715,8 @@
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.7",
- "picocolors": "^1.0.1",
- "source-map-js": "^1.2.0"
+ "picocolors": "^1.1.0",
+ "source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
@@ -4157,70 +4007,10 @@
"node": ">=0.10.0"
}
},
- "node_modules/rimraf": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
- "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
- "deprecated": "Rimraf versions prior to v4 are no longer supported",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "glob": "^7.1.3"
- },
- "bin": {
- "rimraf": "bin.js"
- }
- },
- "node_modules/rimraf/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,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/rimraf/node_modules/glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "deprecated": "Glob versions prior to v9 are no longer supported",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.1.1",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- },
- "engines": {
- "node": "*"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/rimraf/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,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/rollup": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.20.0.tgz",
- "integrity": "sha512-6rbWBChcnSGzIlXeIdNIZTopKYad8ZG8ajhl78lGRLsI2rX8IkaotQhVas2Ma+GPxJav19wrSzvRvuiv0YKzWw==",
+ "version": "4.22.4",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz",
+ "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4234,22 +4024,22 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.20.0",
- "@rollup/rollup-android-arm64": "4.20.0",
- "@rollup/rollup-darwin-arm64": "4.20.0",
- "@rollup/rollup-darwin-x64": "4.20.0",
- "@rollup/rollup-linux-arm-gnueabihf": "4.20.0",
- "@rollup/rollup-linux-arm-musleabihf": "4.20.0",
- "@rollup/rollup-linux-arm64-gnu": "4.20.0",
- "@rollup/rollup-linux-arm64-musl": "4.20.0",
- "@rollup/rollup-linux-powerpc64le-gnu": "4.20.0",
- "@rollup/rollup-linux-riscv64-gnu": "4.20.0",
- "@rollup/rollup-linux-s390x-gnu": "4.20.0",
- "@rollup/rollup-linux-x64-gnu": "4.20.0",
- "@rollup/rollup-linux-x64-musl": "4.20.0",
- "@rollup/rollup-win32-arm64-msvc": "4.20.0",
- "@rollup/rollup-win32-ia32-msvc": "4.20.0",
- "@rollup/rollup-win32-x64-msvc": "4.20.0",
+ "@rollup/rollup-android-arm-eabi": "4.22.4",
+ "@rollup/rollup-android-arm64": "4.22.4",
+ "@rollup/rollup-darwin-arm64": "4.22.4",
+ "@rollup/rollup-darwin-x64": "4.22.4",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.22.4",
+ "@rollup/rollup-linux-arm-musleabihf": "4.22.4",
+ "@rollup/rollup-linux-arm64-gnu": "4.22.4",
+ "@rollup/rollup-linux-arm64-musl": "4.22.4",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4",
+ "@rollup/rollup-linux-riscv64-gnu": "4.22.4",
+ "@rollup/rollup-linux-s390x-gnu": "4.22.4",
+ "@rollup/rollup-linux-x64-gnu": "4.22.4",
+ "@rollup/rollup-linux-x64-musl": "4.22.4",
+ "@rollup/rollup-win32-arm64-msvc": "4.22.4",
+ "@rollup/rollup-win32-ia32-msvc": "4.22.4",
+ "@rollup/rollup-win32-x64-msvc": "4.22.4",
"fsevents": "~2.3.2"
}
},
@@ -4302,19 +4092,6 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT"
},
- "node_modules/sander": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz",
- "integrity": "sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es6-promise": "^3.1.2",
- "graceful-fs": "^4.1.3",
- "mkdirp": "^0.5.1",
- "rimraf": "^2.5.2"
- }
- },
"node_modules/saxes": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
@@ -4405,26 +4182,10 @@
"node": ">= 10"
}
},
- "node_modules/sorcery": {
- "version": "0.11.1",
- "resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.1.tgz",
- "integrity": "sha512-o7npfeJE6wi6J9l0/5LKshFzZ2rMatRiCDwYeDQaOzqdzRJwALhX7mk/A/ecg6wjMu7wdZbmXfD2S/vpOg0bdQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/sourcemap-codec": "^1.4.14",
- "buffer-crc32": "^1.0.0",
- "minimist": "^1.2.0",
- "sander": "^0.5.0"
- },
- "bin": {
- "sorcery": "bin/sorcery"
- }
- },
"node_modules/source-map-js": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
- "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
@@ -4555,19 +4316,6 @@
"node": ">=8"
}
},
- "node_modules/strip-indent": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
- "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "min-indent": "^1.0.0"
- },
- "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",
@@ -4608,24 +4356,24 @@
}
},
"node_modules/svelte": {
- "version": "5.0.0-next.220",
- "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.0.0-next.220.tgz",
- "integrity": "sha512-ETTqNWJXr2RNnKjDZpHmVghBeD8lfFy1OO1BHQvNQXwmwu1rGIGre75vSVsLW35FYRXb/tJ7G225w9vrhY9eAg==",
+ "version": "5.0.0-next.253",
+ "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.0.0-next.253.tgz",
+ "integrity": "sha512-s32X0g/yJJcada3+ZdQiirospMUSl4f8h8/hrMeJ7Oim6+bWccP4pZaKQY+x+LCqyzmBNMxVm3j7+SU5xOaAzg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@ampproject/remapping": "^2.2.1",
- "@jridgewell/sourcemap-codec": "^1.4.15",
+ "@ampproject/remapping": "^2.3.0",
+ "@jridgewell/sourcemap-codec": "^1.5.0",
"@types/estree": "^1.0.5",
- "acorn": "^8.11.3",
+ "acorn": "^8.12.1",
"acorn-typescript": "^1.4.13",
- "aria-query": "^5.3.0",
- "axobject-query": "^4.0.0",
+ "aria-query": "^5.3.1",
+ "axobject-query": "^4.1.0",
"esm-env": "^1.0.0",
"esrap": "^1.2.2",
"is-reference": "^3.0.2",
"locate-character": "^3.0.0",
- "magic-string": "^0.30.5",
+ "magic-string": "^0.30.11",
"zimmerframe": "^1.1.2"
},
"engines": {
@@ -4633,30 +4381,63 @@
}
},
"node_modules/svelte-check": {
- "version": "3.8.5",
- "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.8.5.tgz",
- "integrity": "sha512-3OGGgr9+bJ/+1nbPgsvulkLC48xBsqsgtc8Wam281H4G9F5v3mYGa2bHRsPuwHC5brKl4AxJH95QF73kmfihGQ==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.0.2.tgz",
+ "integrity": "sha512-w2yqcG9ELJe2RJCnAvB7v0OgkHhL3czzz/tVoxGFfO6y4mOrF6QHCDhXijeXzsU7LVKEwWS3Qd9tza4JBuDxqA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jridgewell/trace-mapping": "^0.3.17",
+ "@jridgewell/trace-mapping": "^0.3.25",
"chokidar": "^3.4.1",
+ "fdir": "^6.2.0",
"picocolors": "^1.0.0",
- "sade": "^1.7.4",
- "svelte-preprocess": "^5.1.3",
- "typescript": "^5.0.3"
+ "sade": "^1.7.4"
},
"bin": {
"svelte-check": "bin/svelte-check"
},
+ "engines": {
+ "node": ">= 18.0.0"
+ },
"peerDependencies": {
- "svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0"
+ "svelte": "^4.0.0 || ^5.0.0-next.0",
+ "typescript": ">=5.0.0"
+ }
+ },
+ "node_modules/svelte-check/node_modules/fdir": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.3.0.tgz",
+ "integrity": "sha512-QOnuT+BOtivR77wYvCWHfGt9s4Pz1VIMbD463vegT5MLqNXy8rYFT/lPVEqf/bhYeT6qmqrNHhsX+rWwe3rOCQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/svelte-check/node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/svelte-eslint-parser": {
- "version": "0.41.0",
- "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.41.0.tgz",
- "integrity": "sha512-L6f4hOL+AbgfBIB52Z310pg1d2QjRqm7wy3kI1W6hhdhX5bvu7+f0R6w4ykp5HoDdzq+vGhIJmsisaiJDGmVfA==",
+ "version": "0.41.1",
+ "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.41.1.tgz",
+ "integrity": "sha512-08ndI6zTghzI8SuJAFpvMbA/haPSGn3xz19pjre19yYMw8Nw/wQJ2PrZBI/L8ijGTgtkWCQQiLLy+Z1tfaCwNA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4729,69 +4510,6 @@
"url": "https://opencollective.com/eslint"
}
},
- "node_modules/svelte-preprocess": {
- "version": "5.1.4",
- "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.4.tgz",
- "integrity": "sha512-IvnbQ6D6Ao3Gg6ftiM5tdbR6aAETwjhHV+UKGf5bHGYR69RQvF1ho0JKPcbUON4vy4R7zom13jPjgdOWCQ5hDA==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "dependencies": {
- "@types/pug": "^2.0.6",
- "detect-indent": "^6.1.0",
- "magic-string": "^0.30.5",
- "sorcery": "^0.11.0",
- "strip-indent": "^3.0.0"
- },
- "engines": {
- "node": ">= 16.0.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.10.2",
- "coffeescript": "^2.5.1",
- "less": "^3.11.3 || ^4.0.0",
- "postcss": "^7 || ^8",
- "postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0",
- "pug": "^3.0.0",
- "sass": "^1.26.8",
- "stylus": "^0.55.0",
- "sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0",
- "svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0",
- "typescript": ">=3.9.5 || ^4.0.0 || ^5.0.0"
- },
- "peerDependenciesMeta": {
- "@babel/core": {
- "optional": true
- },
- "coffeescript": {
- "optional": true
- },
- "less": {
- "optional": true
- },
- "postcss": {
- "optional": true
- },
- "postcss-load-config": {
- "optional": true
- },
- "pug": {
- "optional": true
- },
- "sass": {
- "optional": true
- },
- "stylus": {
- "optional": true
- },
- "sugarss": {
- "optional": true
- },
- "typescript": {
- "optional": true
- }
- }
- },
"node_modules/svelte/node_modules/is-reference": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz",
@@ -4903,9 +4621,9 @@
}
},
"node_modules/typescript": {
- "version": "5.5.4",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
- "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
+ "version": "5.6.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz",
+ "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -4917,15 +4635,15 @@
}
},
"node_modules/typescript-eslint": {
- "version": "8.4.0",
- "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.4.0.tgz",
- "integrity": "sha512-67qoc3zQZe3CAkO0ua17+7aCLI0dU+sSQd1eKPGq06QE4rfQjstVXR6woHO5qQvGUa550NfGckT4tzh3b3c8Pw==",
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.6.0.tgz",
+ "integrity": "sha512-eEhhlxCEpCd4helh3AO1hk0UP2MvbRi9CtIAJTVPQjuSXOOO2jsEacNi4UdcJzZJbeuVg1gMhtZ8UYb+NFYPrA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/eslint-plugin": "8.4.0",
- "@typescript-eslint/parser": "8.4.0",
- "@typescript-eslint/utils": "8.4.0"
+ "@typescript-eslint/eslint-plugin": "8.6.0",
+ "@typescript-eslint/parser": "8.6.0",
+ "@typescript-eslint/utils": "8.6.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -4961,9 +4679,9 @@
}
},
"node_modules/undici-types": {
- "version": "6.13.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz",
- "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==",
+ "version": "6.19.8",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
+ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"dev": true,
"license": "MIT"
},
@@ -5004,15 +4722,15 @@
"license": "MIT"
},
"node_modules/vite": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.0.tgz",
- "integrity": "sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==",
+ "version": "5.4.6",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.6.tgz",
+ "integrity": "sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.21.3",
- "postcss": "^8.4.40",
- "rollup": "^4.13.0"
+ "postcss": "^8.4.43",
+ "rollup": "^4.20.0"
},
"bin": {
"vite": "bin/vite.js"
@@ -5257,13 +4975,6 @@
"node": ">=8"
}
},
- "node_modules/wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/ws": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
diff --git a/web-app/package.json b/web-app/package.json
index 44c0c95..79e6551 100644
--- a/web-app/package.json
+++ b/web-app/package.json
@@ -14,34 +14,34 @@
"gents": "pg-to-ts generate -c postgres://postgres@localhost:15432/archtika -o src/lib/db-schema.ts -s internal"
},
"devDependencies": {
- "@playwright/test": "1.40.0",
- "@sveltejs/adapter-auto": "3.2.4",
- "@sveltejs/adapter-node": "5.2.2",
- "@sveltejs/kit": "2.5.22",
- "@sveltejs/vite-plugin-svelte": "3.1.1",
+ "@playwright/test": "1.46.0",
+ "@sveltejs/adapter-auto": "3.2.5",
+ "@sveltejs/adapter-node": "5.2.3",
+ "@sveltejs/kit": "2.5.28",
+ "@sveltejs/vite-plugin-svelte": "4.0.0-next.6",
"@types/eslint": "9.6.1",
"@types/eslint__js": "8.42.3",
"@types/eslint-config-prettier": "6.11.3",
- "@types/node": "22.2.0",
+ "@types/node": "22.5.5",
"eslint": "9.10.0",
"eslint-config-prettier": "9.1.0",
- "eslint-plugin-svelte": "2.43.0",
+ "eslint-plugin-svelte": "2.44.0",
"globals": "15.9.0",
"pg-to-ts": "4.1.1",
"prettier": "3.3.3",
"prettier-plugin-svelte": "3.2.6",
- "svelte": "5.0.0-next.220",
- "svelte-check": "3.8.5",
- "typescript": "5.5.4",
- "typescript-eslint": "8.4.0",
- "vite": "5.4.0"
+ "svelte": "5.0.0-next.253",
+ "svelte-check": "4.0.2",
+ "typescript": "5.6.2",
+ "typescript-eslint": "8.6.0",
+ "vite": "5.4.6"
},
"type": "module",
"dependencies": {
"fast-diff": "1.3.0",
"highlight.js": "11.10.0",
- "isomorphic-dompurify": "2.14.0",
- "marked": "14.0.0",
+ "isomorphic-dompurify": "2.15.0",
+ "marked": "14.1.2",
"marked-highlight": "2.1.4"
}
}
diff --git a/web-app/src/hooks.server.ts b/web-app/src/hooks.server.ts
index 51a677c..73cf58b 100644
--- a/web-app/src/hooks.server.ts
+++ b/web-app/src/hooks.server.ts
@@ -1,32 +1,36 @@
import { redirect } from "@sveltejs/kit";
-import { API_BASE_PREFIX } from "$lib/server/utils";
-import type { User } from "$lib/db-schema";
+import { API_BASE_PREFIX, apiRequest } from "$lib/server/utils";
export const handle = async ({ event, resolve }) => {
if (!event.url.pathname.startsWith("/api/")) {
- const userData = await event.fetch(`${API_BASE_PREFIX}/account`, {
- method: "GET",
+ const userData = await apiRequest(event.fetch, `${API_BASE_PREFIX}/account`, "GET", {
headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${event.cookies.get("session_token")}`,
- Accept: "application/vnd.pgrst.object+json"
- }
+ Accept: "application/vnd.pgrst.object+json",
+ Authorization: `Bearer ${event.cookies.get("session_token")}`
+ },
+ returnData: true
});
- if (!userData.ok && !["/login", "/register"].includes(event.url.pathname)) {
+ if (!userData.success && !["/login", "/register"].includes(event.url.pathname)) {
throw redirect(303, "/login");
}
- if (userData.ok) {
+ if (userData.success) {
if (["/login", "/register"].includes(event.url.pathname)) {
throw redirect(303, "/");
}
- const user: User = await userData.json();
-
- event.locals.user = user;
+ event.locals.user = userData.data;
}
}
return await resolve(event);
};
+
+export const handleFetch = async ({ event, request, fetch }) => {
+ if (event.locals.user) {
+ request.headers.set("Authorization", `Bearer ${event.cookies.get("session_token")}`);
+ }
+
+ return fetch(request);
+};
diff --git a/web-app/src/lib/components/LoadingSpinner.svelte b/web-app/src/lib/components/LoadingSpinner.svelte
index 6eb9339..12ee598 100644
--- a/web-app/src/lib/components/LoadingSpinner.svelte
+++ b/web-app/src/lib/components/LoadingSpinner.svelte
@@ -10,7 +10,7 @@
.spinner {
position: fixed;
inset: 0;
- background-color: rgba(0, 0, 0, 0.5);
+ background-color: var(--bg-blurred);
z-index: 40;
}
@@ -27,6 +27,6 @@
border: var(--border-primary);
border-width: 0.125rem;
border-block-start-color: var(--color-accent);
- animation: spinner 0.6s linear infinite;
+ animation: spinner 500ms linear infinite;
}
diff --git a/web-app/src/lib/components/MarkdownEditor.svelte b/web-app/src/lib/components/MarkdownEditor.svelte
new file mode 100644
index 0000000..cbe47c6
--- /dev/null
+++ b/web-app/src/lib/components/MarkdownEditor.svelte
@@ -0,0 +1,72 @@
+
+
+
diff --git a/web-app/src/lib/components/Modal.svelte b/web-app/src/lib/components/Modal.svelte
index 0f446c4..2a01eef 100644
--- a/web-app/src/lib/components/Modal.svelte
+++ b/web-app/src/lib/components/Modal.svelte
@@ -39,7 +39,7 @@
position: fixed;
inset: 0;
z-index: 10;
- background-color: rgba(0, 0, 0, 0.5);
+ background-color: var(--bg-blurred);
}
.modal__content {
diff --git a/web-app/src/lib/components/WebsiteEditor.svelte b/web-app/src/lib/components/WebsiteEditor.svelte
index 0038b73..267831d 100644
--- a/web-app/src/lib/components/WebsiteEditor.svelte
+++ b/web-app/src/lib/components/WebsiteEditor.svelte
@@ -2,30 +2,27 @@
import type { Snippet } from "svelte";
import { md } from "$lib/utils";
import { page } from "$app/stores";
+ import { previewContent, textareaScrollTop } from "$lib/runes.svelte";
const {
id,
contentType,
title,
children,
- fullPreview = false,
- previewContent,
- previewScrollTop = 0
+ fullPreview = false
}: {
id: string;
contentType: string;
title: string;
children: Snippet;
fullPreview?: boolean;
- previewContent: string;
- previewScrollTop?: number;
} = $props();
let previewElement: HTMLDivElement;
$effect(() => {
const scrollHeight = previewElement.scrollHeight - previewElement.clientHeight;
- previewElement.scrollTop = (previewScrollTop / 100) * scrollHeight;
+ previewElement.scrollTop = (textareaScrollTop.value / 100) * scrollHeight;
});
@@ -66,9 +63,12 @@
{#if fullPreview}
-
+
{:else}
- {@html md(previewContent, Object.keys($page.params).length > 1 ? true : false)}
+ {@html md(
+ previewContent.value || "Write some markdown content to see a live preview here",
+ Object.keys($page.params).length > 1 ? true : false
+ )}
{/if}
diff --git a/web-app/src/lib/db-schema.ts b/web-app/src/lib/db-schema.ts
index 1e45e9d..a02fb53 100644
--- a/web-app/src/lib/db-schema.ts
+++ b/web-app/src/lib/db-schema.ts
@@ -5,7 +5,7 @@
* AUTO-GENERATED FILE - DO NOT EDIT!
*
* This file was automatically generated by pg-to-ts v.4.1.1
- * $ pg-to-ts generate -c postgres://username:password@localhost:15432/archtika -t article -t change_log -t collab -t docs_category -t footer -t header -t home -t legal_information -t media -t settings -t user -t website -s internal
+ * $ pg-to-ts generate -c postgres://username:password@localhost:15432/archtika -t article -t change_log -t collab -t docs_category -t domain_prefix -t footer -t header -t home -t legal_information -t media -t settings -t user -t website -s internal
*
*/
@@ -169,6 +169,7 @@ export interface DocsCategory {
user_id: string | null;
category_name: string;
category_weight: number;
+ created_at: Date;
last_modified_at: Date;
last_modified_by: string | null;
}
@@ -178,6 +179,7 @@ export interface DocsCategoryInput {
user_id?: string | null;
category_name: string;
category_weight: number;
+ created_at?: Date;
last_modified_at?: Date;
last_modified_by?: string | null;
}
@@ -189,6 +191,7 @@ const docs_category = {
"user_id",
"category_name",
"category_weight",
+ "created_at",
"last_modified_at",
"last_modified_by"
],
@@ -203,6 +206,34 @@ const docs_category = {
$input: null as unknown as DocsCategoryInput
} as const;
+// Table domain_prefix
+export interface DomainPrefix {
+ website_id: string;
+ prefix: string;
+ created_at: Date;
+ last_modified_at: Date;
+ last_modified_by: string | null;
+}
+export interface DomainPrefixInput {
+ website_id: string;
+ prefix: string;
+ created_at?: Date;
+ last_modified_at?: Date;
+ last_modified_by?: string | null;
+}
+const domain_prefix = {
+ tableName: "domain_prefix",
+ columns: ["website_id", "prefix", "created_at", "last_modified_at", "last_modified_by"],
+ requiredForInsert: ["website_id", "prefix"],
+ primaryKey: "website_id",
+ foreignKeys: {
+ website_id: { table: "website", column: "id", $type: null as unknown as Website },
+ last_modified_by: { table: "user", column: "id", $type: null as unknown as User }
+ },
+ $type: null as unknown as DomainPrefix,
+ $input: null as unknown as DomainPrefixInput
+} as const;
+
// Table footer
export interface Footer {
website_id: string;
@@ -297,18 +328,20 @@ const home = {
export interface LegalInformation {
website_id: string;
main_content: string;
+ created_at: Date;
last_modified_at: Date;
last_modified_by: string | null;
}
export interface LegalInformationInput {
website_id: string;
main_content: string;
+ created_at?: Date;
last_modified_at?: Date;
last_modified_by?: string | null;
}
const legal_information = {
tableName: "legal_information",
- columns: ["website_id", "main_content", "last_modified_at", "last_modified_by"],
+ columns: ["website_id", "main_content", "created_at", "last_modified_at", "last_modified_by"],
requiredForInsert: ["website_id", "main_content"],
primaryKey: "website_id",
foreignKeys: {
@@ -354,16 +387,20 @@ const media = {
// Table settings
export interface Settings {
website_id: string;
- accent_color_light_theme: string;
accent_color_dark_theme: string;
+ accent_color_light_theme: string;
+ background_color_dark_theme: string;
+ background_color_light_theme: string;
favicon_image: string | null;
last_modified_at: Date;
last_modified_by: string | null;
}
export interface SettingsInput {
website_id: string;
- accent_color_light_theme?: string;
accent_color_dark_theme?: string;
+ accent_color_light_theme?: string;
+ background_color_dark_theme?: string;
+ background_color_light_theme?: string;
favicon_image?: string | null;
last_modified_at?: Date;
last_modified_by?: string | null;
@@ -372,8 +409,10 @@ const settings = {
tableName: "settings",
columns: [
"website_id",
- "accent_color_light_theme",
"accent_color_dark_theme",
+ "accent_color_light_theme",
+ "background_color_dark_theme",
+ "background_color_light_theme",
"favicon_image",
"last_modified_at",
"last_modified_by"
@@ -395,16 +434,18 @@ export interface User {
username: string;
password_hash: string;
role: string;
+ created_at: Date;
}
export interface UserInput {
id?: string;
username: string;
password_hash: string;
role?: string;
+ created_at?: Date;
}
const user = {
tableName: "user",
- columns: ["id", "username", "password_hash", "role"],
+ columns: ["id", "username", "password_hash", "role", "created_at"],
requiredForInsert: ["username", "password_hash"],
primaryKey: "id",
foreignKeys: {},
@@ -418,8 +459,8 @@ export interface Website {
user_id: string;
content_type: string;
title: string;
- created_at: Date;
is_published: boolean;
+ created_at: Date;
last_modified_at: Date;
last_modified_by: string | null;
title_search: any | null;
@@ -429,8 +470,8 @@ export interface WebsiteInput {
user_id?: string;
content_type: string;
title: string;
- created_at?: Date;
is_published?: boolean;
+ created_at?: Date;
last_modified_at?: Date;
last_modified_by?: string | null;
title_search?: any | null;
@@ -442,8 +483,8 @@ const website = {
"user_id",
"content_type",
"title",
- "created_at",
"is_published",
+ "created_at",
"last_modified_at",
"last_modified_by",
"title_search"
@@ -475,6 +516,10 @@ export interface TableTypes {
select: DocsCategory;
input: DocsCategoryInput;
};
+ domain_prefix: {
+ select: DomainPrefix;
+ input: DomainPrefixInput;
+ };
footer: {
select: Footer;
input: FooterInput;
@@ -514,6 +559,7 @@ export const tables = {
change_log,
collab,
docs_category,
+ domain_prefix,
footer,
header,
home,
diff --git a/web-app/src/lib/runes.svelte.ts b/web-app/src/lib/runes.svelte.ts
new file mode 100644
index 0000000..fab051e
--- /dev/null
+++ b/web-app/src/lib/runes.svelte.ts
@@ -0,0 +1,30 @@
+let sendingState = $state(false);
+let previewContentState = $state("");
+let textareaScrollTopState = $state(0);
+
+export const sending = {
+ get value() {
+ return sendingState;
+ },
+ set value(val) {
+ sendingState = val;
+ }
+};
+
+export const previewContent = {
+ get value() {
+ return previewContentState;
+ },
+ set value(val) {
+ previewContentState = val;
+ }
+};
+
+export const textareaScrollTop = {
+ get value() {
+ return textareaScrollTopState;
+ },
+ set value(val) {
+ textareaScrollTopState = val;
+ }
+};
diff --git a/web-app/src/lib/server/utils.ts b/web-app/src/lib/server/utils.ts
index e745aeb..134430c 100644
--- a/web-app/src/lib/server/utils.ts
+++ b/web-app/src/lib/server/utils.ts
@@ -3,3 +3,54 @@ import { dev } from "$app/environment";
export const API_BASE_PREFIX = dev
? "http://localhost:3000"
: `${process.env.ORIGIN ? `${process.env.ORIGIN}/api` : "http://localhost:3000"}`;
+
+export const REGISTRATION_IS_DISABLED = dev
+ ? false
+ : process.env.REGISTRATION_IS_DISABLED
+ ? JSON.parse(process.env.REGISTRATION_IS_DISABLED)
+ : false;
+
+export const apiRequest = async (
+ customFetch: typeof fetch,
+ url: string,
+ method: "HEAD" | "GET" | "POST" | "PATCH" | "DELETE",
+ options: {
+ headers?: Record;
+ body?: any;
+ successMessage?: string;
+ returnData?: boolean;
+ } = {
+ headers: {},
+ body: undefined,
+ successMessage: "Operation was successful",
+ returnData: false
+ }
+) => {
+ const headers = {
+ "Content-Type": "application/json",
+ ...options.headers
+ };
+
+ const response = await customFetch(url, {
+ method,
+ headers,
+ ...(!["HEAD", "GET", "DELETE"].includes(method) && {
+ body: options.body instanceof ArrayBuffer ? options.body : JSON.stringify(options.body)
+ })
+ });
+
+ if (!response.ok) {
+ const errorData = await response.json();
+ return { success: false, message: errorData.message };
+ }
+
+ if (options.returnData) {
+ return {
+ success: true,
+ message: options.successMessage,
+ data: method === "HEAD" ? response : await response.json()
+ };
+ }
+
+ return { success: true, message: options.successMessage };
+};
diff --git a/web-app/src/lib/templates/common/Footer.svelte b/web-app/src/lib/templates/common/Footer.svelte
index da65909..d18594d 100644
--- a/web-app/src/lib/templates/common/Footer.svelte
+++ b/web-app/src/lib/templates/common/Footer.svelte
@@ -1,5 +1,5 @@
-
-
-
- {title}
-
-
- {#if websiteOverview.settings.favicon_image}
-
- {/if}
-
+
+
+ {title}
+
+
+ {#if websiteOverview.settings.favicon_image}
+
+ {/if}
diff --git a/web-app/src/lib/utils.ts b/web-app/src/lib/utils.ts
index 9307bb9..9a03ad3 100644
--- a/web-app/src/lib/utils.ts
+++ b/web-app/src/lib/utils.ts
@@ -12,10 +12,20 @@ import type {
Footer,
Article,
DocsCategory,
- LegalInformation
+ LegalInformation,
+ DomainPrefix
} from "$lib/db-schema";
+import type { SubmitFunction } from "@sveltejs/kit";
+import { sending } from "./runes.svelte";
-export const ALLOWED_MIME_TYPES = ["image/jpeg", "image/png", "image/webp"];
+export const ALLOWED_MIME_TYPES = [
+ "image/jpeg",
+ "image/png",
+ "image/webp",
+ "image/avif",
+ "image/gif",
+ "image/svg+xml"
+];
export const slugify = (string: string) => {
return string
@@ -24,8 +34,8 @@ export const slugify = (string: string) => {
.toLowerCase() // Convert to lowercase
.trim() // Trim leading and trailing whitespace
.replace(/\s+/g, "-") // Replace spaces with hyphens
- .replace(/[^\w\-]+/g, "") // Remove non-word characters (except hyphens)
- .replace(/\-\-+/g, "-") // Replace multiple hyphens with single hyphen
+ .replace(/[^\w-]+/g, "") // Remove non-word characters (except hyphens)
+ .replace(/-+/g, "-") // Replace multiple hyphens with single hyphen
.replace(/^-+/, "") // Remove leading hyphens
.replace(/-+$/, ""); // Remove trailing hyphens
};
@@ -51,8 +61,8 @@ const createMarkdownParser = (showToc = true) => {
);
const gfmHeadingId = ({ prefix = "", showToc = true } = {}) => {
- let headings: { text: string; level: number; id: string }[] = [];
- let sectionStack: { level: number; id: string }[] = [];
+ const headings: { text: string; level: number; id: string }[] = [];
+ const sectionStack: { level: number; id: string }[] = [];
return {
renderer: {
@@ -143,45 +153,59 @@ export const md = (markdownContent: string, showToc = true) => {
return html;
};
-export const handleImagePaste = async (event: ClipboardEvent, API_BASE_PREFIX: string) => {
- const clipboardItems = Array.from(event.clipboardData?.items ?? []);
- const file = clipboardItems.find((item) => item.type.startsWith("image/"));
+export const LOADING_DELAY = 500;
+let loadingDelay: number;
- if (!file) return null;
+export const enhanceForm = (options?: {
+ reset?: boolean;
+ closeModal?: boolean;
+}): SubmitFunction => {
+ return () => {
+ loadingDelay = window.setTimeout(() => (sending.value = true), LOADING_DELAY);
- event.preventDefault();
+ return async ({ update }) => {
+ await update({ reset: options?.reset ?? true });
+ window.clearTimeout(loadingDelay);
+ if (options?.closeModal) {
+ window.location.hash = "!";
+ }
+ sending.value = false;
+ };
+ };
+};
- const fileObject = file.getAsFile();
+export const hexToHSL = (hex: string) => {
+ const r = parseInt(hex.slice(1, 3), 16) / 255;
+ const g = parseInt(hex.slice(3, 5), 16) / 255;
+ const b = parseInt(hex.slice(5, 7), 16) / 255;
- if (!fileObject) return;
+ const max = Math.max(r, g, b);
+ const min = Math.min(r, g, b);
+ const d = max - min;
- const formData = new FormData();
- formData.append("file", fileObject);
+ let h = 0;
+ const l = (max + min) / 2;
+ const s = d === 0 ? 0 : d / (1 - Math.abs(2 * l - 1));
- const request = await fetch("?/pasteImage", {
- method: "POST",
- body: formData
- });
-
- const result = deserialize(await request.clone().text());
- applyAction(result);
-
- const response = await request.json();
-
- if (JSON.parse(response.data)[1]) {
- const fileId = JSON.parse(response.data)[3];
- const fileUrl = `${API_BASE_PREFIX}/rpc/retrieve_file?id=${fileId}`;
-
- const target = event.target as HTMLTextAreaElement;
- const newContent =
- target.value.slice(0, target.selectionStart) +
- `` +
- target.value.slice(target.selectionStart);
-
- return newContent;
- } else {
- return "";
+ if (d !== 0) {
+ switch (max) {
+ case r:
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
+ break;
+ case g:
+ h = ((b - r) / d + 2) / 6;
+ break;
+ case b:
+ h = ((r - g) / d + 4) / 6;
+ break;
+ }
}
+
+ return {
+ h: Math.round(h * 360),
+ s: Math.round(s * 100),
+ l: Math.round(l * 100)
+ };
};
export interface WebsiteOverview extends Website {
@@ -191,4 +215,5 @@ export interface WebsiteOverview extends Website {
footer: Footer;
article: (Article & { docs_category: DocsCategory | null })[];
legal_information?: LegalInformation;
+ domain_prefix?: DomainPrefix;
}
diff --git a/web-app/src/routes/(anonymous)/login/+page.server.ts b/web-app/src/routes/(anonymous)/login/+page.server.ts
index ce747c8..747b6a7 100644
--- a/web-app/src/routes/(anonymous)/login/+page.server.ts
+++ b/web-app/src/routes/(anonymous)/login/+page.server.ts
@@ -1,26 +1,24 @@
import type { Actions } from "./$types";
-import { API_BASE_PREFIX } from "$lib/server/utils";
+import { API_BASE_PREFIX, apiRequest } from "$lib/server/utils";
export const actions: Actions = {
default: async ({ request, cookies, fetch }) => {
const data = await request.formData();
- const res = await fetch(`${API_BASE_PREFIX}/rpc/login`, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({
+ const response = await apiRequest(fetch, `${API_BASE_PREFIX}/rpc/login`, "POST", {
+ body: {
username: data.get("username"),
pass: data.get("password")
- })
+ },
+ returnData: true,
+ successMessage: "Successfully logged in, you can refresh the page"
});
- const response = await res.json();
-
- if (!res.ok) {
- return { success: false, message: response.message };
+ if (!response.success) {
+ return response;
}
- cookies.set("session_token", response.token, { path: "/" });
- return { success: true, message: "Successfully logged in" };
+ cookies.set("session_token", response.data.token, { path: "/" });
+ return response;
}
};
diff --git a/web-app/src/routes/(anonymous)/login/+page.svelte b/web-app/src/routes/(anonymous)/login/+page.svelte
index 30bcd57..bb9d470 100644
--- a/web-app/src/routes/(anonymous)/login/+page.svelte
+++ b/web-app/src/routes/(anonymous)/login/+page.svelte
@@ -3,28 +3,19 @@
import SuccessOrError from "$lib/components/SuccessOrError.svelte";
import type { ActionData } from "./$types";
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte";
+ import { sending } from "$lib/runes.svelte";
+ import { enhanceForm } from "$lib/utils";
const { form }: { form: ActionData } = $props();
-
- let sending = $state(false);
-{#if sending}
+{#if sending.value}
{/if}
-
+{/if}
+
+
diff --git a/web-app/src/routes/(authenticated)/+page.server.ts b/web-app/src/routes/(authenticated)/+page.server.ts
index 2f37835..fe26335 100644
--- a/web-app/src/routes/(authenticated)/+page.server.ts
+++ b/web-app/src/routes/(authenticated)/+page.server.ts
@@ -1,10 +1,11 @@
import type { Actions, PageServerLoad } from "./$types";
+import { apiRequest } from "$lib/server/utils";
import { API_BASE_PREFIX } from "$lib/server/utils";
import { rm } from "node:fs/promises";
import { join } from "node:path";
-import type { Website, WebsiteInput } from "$lib/db-schema";
+import type { Website } from "$lib/db-schema";
-export const load: PageServerLoad = async ({ fetch, cookies, url, locals }) => {
+export const load: PageServerLoad = async ({ fetch, url, locals }) => {
const searchQuery = url.searchParams.get("website_search_query");
const filterBy = url.searchParams.get("website_filter");
@@ -27,28 +28,22 @@ export const load: PageServerLoad = async ({ fetch, cookies, url, locals }) => {
const constructedFetchUrl = `${baseFetchUrl}&${params.toString()}`;
- const totalWebsitesData = await fetch(baseFetchUrl, {
- method: "HEAD",
+ const totalWebsites = await apiRequest(fetch, baseFetchUrl, "HEAD", {
headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`,
Prefer: "count=exact"
- }
+ },
+ returnData: true
});
const totalWebsiteCount = Number(
- totalWebsitesData.headers.get("content-range")?.split("/").at(-1)
+ totalWebsites.data.headers.get("content-range")?.split("/").at(-1)
);
- const websiteData = await fetch(constructedFetchUrl, {
- method: "GET",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`
- }
- });
-
- const websites: Website[] = await websiteData.json();
+ const websites: Website[] = (
+ await apiRequest(fetch, constructedFetchUrl, "GET", {
+ returnData: true
+ })
+ ).data;
return {
totalWebsiteCount,
@@ -57,70 +52,63 @@ export const load: PageServerLoad = async ({ fetch, cookies, url, locals }) => {
};
export const actions: Actions = {
- createWebsite: async ({ request, fetch, cookies }) => {
+ createWebsite: async ({ request, fetch }) => {
const data = await request.formData();
- const res = await fetch(`${API_BASE_PREFIX}/rpc/create_website`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`
- },
- body: JSON.stringify({
- content_type: data.get("content-type") as string,
- title: data.get("title") as string
- } satisfies WebsiteInput)
- });
-
- if (!res.ok) {
- const response = await res.json();
- return { success: false, message: response.message };
- }
-
- return { success: true, message: "Successfully created website" };
- },
- updateWebsite: async ({ request, cookies, fetch }) => {
- const data = await request.formData();
-
- const res = await fetch(`${API_BASE_PREFIX}/website?id=eq.${data.get("id")}`, {
- method: "PATCH",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`
- },
- body: JSON.stringify({
+ return await apiRequest(fetch, `${API_BASE_PREFIX}/rpc/create_website`, "POST", {
+ body: {
+ content_type: data.get("content-type"),
title: data.get("title")
- })
+ },
+ successMessage: "Successfully created website"
});
-
- if (!res.ok) {
- const response = await res.json();
- return { success: false, message: response.message };
- }
-
- return { success: true, message: "Successfully updated website" };
},
- deleteWebsite: async ({ request, cookies, fetch }) => {
+ updateWebsite: async ({ request, fetch }) => {
const data = await request.formData();
- const res = await fetch(`${API_BASE_PREFIX}/website?id=eq.${data.get("id")}`, {
- method: "DELETE",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`
- }
+ return await apiRequest(fetch, `${API_BASE_PREFIX}/website?id=eq.${data.get("id")}`, "PATCH", {
+ body: {
+ title: data.get("title")
+ },
+ successMessage: "Successfully updated website"
});
+ },
+ deleteWebsite: async ({ request, fetch }) => {
+ const data = await request.formData();
+ const id = data.get("id");
- if (!res.ok) {
- const response = await res.json();
- return { success: false, message: response.message };
+ const oldDomainPrefix = (
+ await apiRequest(fetch, `${API_BASE_PREFIX}/domain_prefix?website_id=eq.${id}`, "GET", {
+ headers: {
+ Accept: "application/vnd.pgrst.object+json"
+ },
+ returnData: true
+ })
+ ).data;
+
+ const deleteWebsite = await apiRequest(
+ fetch,
+ `${API_BASE_PREFIX}/website?id=eq.${id}`,
+ "DELETE",
+ {
+ successMessage: "Successfully deleted website"
+ }
+ );
+
+ if (!deleteWebsite.success) {
+ return deleteWebsite;
}
- await rm(join("/", "var", "www", "archtika-websites", data.get("id") as string), {
+ await rm(join("/", "var", "www", "archtika-websites", "previews", id as string), {
recursive: true,
force: true
});
- return { success: true, message: "Successfully deleted website" };
+ await rm(join("/", "var", "www", "archtika-websites", oldDomainPrefix?.prefix ?? id), {
+ recursive: true,
+ force: true
+ });
+
+ return deleteWebsite;
}
};
diff --git a/web-app/src/routes/(authenticated)/+page.svelte b/web-app/src/routes/(authenticated)/+page.svelte
index 592780a..11c1359 100644
--- a/web-app/src/routes/(authenticated)/+page.svelte
+++ b/web-app/src/routes/(authenticated)/+page.svelte
@@ -6,15 +6,15 @@
import SuccessOrError from "$lib/components/SuccessOrError.svelte";
import type { ActionData, PageServerData } from "./$types";
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte";
+ import { enhanceForm } from "$lib/utils";
+ import { sending } from "$lib/runes.svelte";
const { form, data }: { form: ActionData; data: PageServerData } = $props();
-
- let sending = $state(false);
-{#if sending}
+{#if sending.value}
{/if}
@@ -26,18 +26,7 @@
Create website
-
- {
- sending = true;
- return async ({ update }) => {
- await update();
- window.location.hash = "!";
- sending = false;
- };
- }}
- >
+
+
+
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/+layout.server.ts b/web-app/src/routes/(authenticated)/website/[websiteId]/+layout.server.ts
index 560b904..9271b14 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/+layout.server.ts
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/+layout.server.ts
@@ -1,36 +1,35 @@
import type { LayoutServerLoad } from "./$types";
-import { API_BASE_PREFIX } from "$lib/server/utils";
+import { API_BASE_PREFIX, apiRequest } from "$lib/server/utils";
import { error } from "@sveltejs/kit";
import type { Website, Home, User } from "$lib/db-schema";
-export const load: LayoutServerLoad = async ({ params, fetch, cookies }) => {
- const websiteData = await fetch(
+export const load: LayoutServerLoad = async ({ params, fetch }) => {
+ const websiteData = await apiRequest(
+ fetch,
`${API_BASE_PREFIX}/website?id=eq.${params.websiteId}&select=*,user!user_id(username)`,
+ "GET",
{
- method: "GET",
headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`,
Accept: "application/vnd.pgrst.object+json"
- }
+ },
+ returnData: true
}
);
- if (!websiteData.ok) {
+ const website: Website & { user: { username: User["username"] } } = websiteData.data;
+
+ if (!websiteData.success) {
throw error(404, "Website not found");
}
- const homeData = await fetch(`${API_BASE_PREFIX}/home?website_id=eq.${params.websiteId}`, {
- method: "GET",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`,
- Accept: "application/vnd.pgrst.object+json"
- }
- });
-
- const website: Website & { user: { username: User["username"] } } = await websiteData.json();
- const home: Home = await homeData.json();
+ const home: Home = (
+ await apiRequest(fetch, `${API_BASE_PREFIX}/home?website_id=eq.${params.websiteId}`, "GET", {
+ headers: {
+ Accept: "application/vnd.pgrst.object+json"
+ },
+ returnData: true
+ })
+ ).data;
return {
website,
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/+page.server.ts b/web-app/src/routes/(authenticated)/website/[websiteId]/+page.server.ts
index 8d61ee6..e932ba8 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/+page.server.ts
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/+page.server.ts
@@ -1,41 +1,40 @@
import type { Actions, PageServerLoad } from "./$types";
import { API_BASE_PREFIX } from "$lib/server/utils";
+import { apiRequest } from "$lib/server/utils";
import type { Settings, Header, Footer } from "$lib/db-schema";
-export const load: PageServerLoad = async ({ params, fetch, cookies }) => {
- const globalSettingsData = await fetch(
- `${API_BASE_PREFIX}/settings?website_id=eq.${params.websiteId}`,
- {
- method: "GET",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`,
- Accept: "application/vnd.pgrst.object+json"
+export const load: PageServerLoad = async ({ params, fetch }) => {
+ const globalSettings: Settings = (
+ await apiRequest(
+ fetch,
+ `${API_BASE_PREFIX}/settings?website_id=eq.${params.websiteId}`,
+ "GET",
+ {
+ headers: {
+ Accept: "application/vnd.pgrst.object+json"
+ },
+ returnData: true
}
- }
- );
+ )
+ ).data;
- const headerData = await fetch(`${API_BASE_PREFIX}/header?website_id=eq.${params.websiteId}`, {
- method: "GET",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`,
- Accept: "application/vnd.pgrst.object+json"
- }
- });
+ const header: Header = (
+ await apiRequest(fetch, `${API_BASE_PREFIX}/header?website_id=eq.${params.websiteId}`, "GET", {
+ headers: {
+ Accept: "application/vnd.pgrst.object+json"
+ },
+ returnData: true
+ })
+ ).data;
- const footerData = await fetch(`${API_BASE_PREFIX}/footer?website_id=eq.${params.websiteId}`, {
- method: "GET",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`,
- Accept: "application/vnd.pgrst.object+json"
- }
- });
-
- const globalSettings: Settings = await globalSettingsData.json();
- const header: Header = await headerData.json();
- const footer: Footer = await footerData.json();
+ const footer: Footer = (
+ await apiRequest(fetch, `${API_BASE_PREFIX}/footer?website_id=eq.${params.websiteId}`, "GET", {
+ headers: {
+ Accept: "application/vnd.pgrst.object+json"
+ },
+ returnData: true
+ })
+ ).data;
return {
globalSettings,
@@ -46,13 +45,12 @@ export const load: PageServerLoad = async ({ params, fetch, cookies }) => {
};
export const actions: Actions = {
- updateGlobal: async ({ request, fetch, cookies, params }) => {
+ updateGlobal: async ({ request, fetch, params }) => {
const data = await request.formData();
const faviconFile = data.get("favicon") as File;
const headers: Record = {
"Content-Type": "application/octet-stream",
- Authorization: `Bearer ${cookies.get("session_token")}`,
Accept: "application/vnd.pgrst.object+json",
"X-Website-Id": params.websiteId
};
@@ -62,48 +60,38 @@ export const actions: Actions = {
headers["X-Original-Filename"] = faviconFile.name;
}
- const uploadedImageData = await fetch(`${API_BASE_PREFIX}/rpc/upload_file`, {
- method: "POST",
+ const uploadedImage = await apiRequest(fetch, `${API_BASE_PREFIX}/rpc/upload_file`, "POST", {
headers,
- body: faviconFile ? await faviconFile.arrayBuffer() : null
+ body: faviconFile ? await faviconFile.arrayBuffer() : null,
+ returnData: true
});
- const uploadedImage = await uploadedImageData.json();
-
- if (!uploadedImageData.ok && (faviconFile?.size ?? 0 > 0)) {
- return { success: false, message: uploadedImage.message };
+ if (!uploadedImage.success && (faviconFile?.size ?? 0 > 0)) {
+ return uploadedImage;
}
- const res = await fetch(`${API_BASE_PREFIX}/settings?website_id=eq.${params.websiteId}`, {
- method: "PATCH",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`
- },
- body: JSON.stringify({
- accent_color_light_theme: data.get("accent-color-light"),
- accent_color_dark_theme: data.get("accent-color-dark"),
- favicon_image: uploadedImage.file_id
- })
- });
-
- if (!res.ok) {
- const response = await res.json();
- return { success: false, message: response.message };
- }
-
- return {
- success: true,
- message: "Successfully updated global settings"
- };
+ return await apiRequest(
+ fetch,
+ `${API_BASE_PREFIX}/settings?website_id=eq.${params.websiteId}`,
+ "PATCH",
+ {
+ body: {
+ accent_color_dark_theme: data.get("accent-color-dark"),
+ accent_color_light_theme: data.get("accent-color-light"),
+ background_color_dark_theme: data.get("background-color-dark"),
+ background_color_light_theme: data.get("background-color-light"),
+ favicon_image: uploadedImage.data?.file_id
+ },
+ successMessage: "Successfully updated global settings"
+ }
+ );
},
- updateHeader: async ({ request, fetch, cookies, params }) => {
+ updateHeader: async ({ request, fetch, params }) => {
const data = await request.formData();
const logoImage = data.get("logo-image") as File;
const headers: Record = {
"Content-Type": "application/octet-stream",
- Authorization: `Bearer ${cookies.get("session_token")}`,
Accept: "application/vnd.pgrst.object+json",
"X-Website-Id": params.websiteId
};
@@ -113,109 +101,75 @@ export const actions: Actions = {
headers["X-Original-Filename"] = logoImage.name;
}
- const uploadedImageData = await fetch(`${API_BASE_PREFIX}/rpc/upload_file`, {
- method: "POST",
+ const uploadedImage = await apiRequest(fetch, `${API_BASE_PREFIX}/rpc/upload_file`, "POST", {
headers,
- body: logoImage ? await logoImage.arrayBuffer() : null
+ body: logoImage ? await logoImage.arrayBuffer() : null,
+ returnData: true
});
- const uploadedImage = await uploadedImageData.json();
-
- if (!uploadedImageData.ok && (logoImage?.size ?? 0 > 0)) {
+ if (!uploadedImage.success && (logoImage?.size ?? 0 > 0)) {
return { success: false, message: uploadedImage.message };
}
- const res = await fetch(`${API_BASE_PREFIX}/header?website_id=eq.${params.websiteId}`, {
- method: "PATCH",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`
- },
- body: JSON.stringify({
- logo_type: data.get("logo-type"),
- logo_text: data.get("logo-text"),
- logo_image: uploadedImage.file_id
- })
- });
-
- if (!res.ok) {
- const response = await res.json();
- return { success: false, message: response.message };
- }
-
- return {
- success: true,
- message: "Successfully updated header"
- };
+ return await apiRequest(
+ fetch,
+ `${API_BASE_PREFIX}/header?website_id=eq.${params.websiteId}`,
+ "PATCH",
+ {
+ body: {
+ logo_type: data.get("logo-type"),
+ logo_text: data.get("logo-text"),
+ logo_image: uploadedImage.data?.file_id
+ },
+ successMessage: "Successfully updated header"
+ }
+ );
},
- updateHome: async ({ request, fetch, cookies, params }) => {
+ updateHome: async ({ request, fetch, params }) => {
const data = await request.formData();
- const res = await fetch(`${API_BASE_PREFIX}/home?website_id=eq.${params.websiteId}`, {
- method: "PATCH",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`
- },
- body: JSON.stringify({
- main_content: data.get("main-content")
- })
- });
-
- if (!res.ok) {
- const response = await res.json();
- return { success: false, message: response.message };
- }
-
- return { success: true, message: "Successfully updated home" };
+ return await apiRequest(
+ fetch,
+ `${API_BASE_PREFIX}/home?website_id=eq.${params.websiteId}`,
+ "PATCH",
+ {
+ body: {
+ main_content: data.get("main-content")
+ },
+ successMessage: "Successfully updated home"
+ }
+ );
},
- updateFooter: async ({ request, fetch, cookies, params }) => {
+ updateFooter: async ({ request, fetch, params }) => {
const data = await request.formData();
- const res = await fetch(`${API_BASE_PREFIX}/footer?website_id=eq.${params.websiteId}`, {
- method: "PATCH",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`
- },
- body: JSON.stringify({
- additional_text: data.get("additional-text")
- })
- });
-
- if (!res.ok) {
- const response = await res.json();
- return { success: false, message: response.message };
- }
-
- return {
- success: true,
- message: "Successfully updated footer"
- };
+ return await apiRequest(
+ fetch,
+ `${API_BASE_PREFIX}/footer?website_id=eq.${params.websiteId}`,
+ "PATCH",
+ {
+ body: {
+ additional_text: data.get("additional-text")
+ },
+ successMessage: "Successfully updated footer"
+ }
+ );
},
- pasteImage: async ({ request, fetch, cookies, params }) => {
+ pasteImage: async ({ request, fetch, params }) => {
const data = await request.formData();
const file = data.get("file") as File;
- const fileData = await fetch(`${API_BASE_PREFIX}/rpc/upload_file`, {
- method: "POST",
+ return await apiRequest(fetch, `${API_BASE_PREFIX}/rpc/upload_file`, "POST", {
headers: {
"Content-Type": "application/octet-stream",
- Authorization: `Bearer ${cookies.get("session_token")}`,
Accept: "application/vnd.pgrst.object+json",
"X-Website-Id": params.websiteId,
"X-Mimetype": file.type,
"X-Original-Filename": file.name
},
- body: await file.arrayBuffer()
+ body: await file.arrayBuffer(),
+ successMessage: "Successfully uploaded image",
+ returnData: true
});
-
- const fileJSON = await fileData.json();
-
- if (!fileData.ok) {
- return { success: false, message: fileJSON.message };
- }
-
- return { success: true, message: "Successfully uploaded image", fileId: fileJSON.file_id };
}
};
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/+page.svelte b/web-app/src/routes/(authenticated)/website/[websiteId]/+page.svelte
index 3d3f91f..a537691 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/+page.svelte
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/+page.svelte
@@ -1,37 +1,24 @@
-{#if sending}
+{#if sending.value}
{/if}
@@ -39,9 +26,6 @@
id={data.website.id}
contentType={data.website.content_type}
title={data.website.title}
- previewContent={previewContent ||
- "Put some markdown content in main content to see a live preview here"}
- previewScrollTop={textareaScrollTop}
>
@@ -51,26 +35,30 @@
action="?/updateGlobal"
method="POST"
enctype="multipart/form-data"
- use:enhance={() => {
- sending = true;
- return async ({ update }) => {
- await update({ reset: false });
- sending = false;
- };
- }}
+ use:enhance={enhanceForm({ reset: false })}
>
+
+
{/if}
-
- Main content:
-
-
+
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/categories/+page.server.ts b/web-app/src/routes/(authenticated)/website/[websiteId]/categories/+page.server.ts
index f16941f..0254e87 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/categories/+page.server.ts
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/categories/+page.server.ts
@@ -1,20 +1,19 @@
import type { Actions, PageServerLoad } from "./$types";
-import { API_BASE_PREFIX } from "$lib/server/utils";
-import type { DocsCategory, DocsCategoryInput } from "$lib/db-schema";
+import { API_BASE_PREFIX, apiRequest } from "$lib/server/utils";
+import type { DocsCategory } from "$lib/db-schema";
-export const load: PageServerLoad = async ({ parent, params, cookies, fetch }) => {
- const categoryData = await fetch(
- `${API_BASE_PREFIX}/docs_category?website_id=eq.${params.websiteId}&order=category_weight.desc`,
- {
- method: "GET",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`
+export const load: PageServerLoad = async ({ parent, params, fetch }) => {
+ const categories: DocsCategory[] = (
+ await apiRequest(
+ fetch,
+ `${API_BASE_PREFIX}/docs_category?website_id=eq.${params.websiteId}&order=category_weight.desc`,
+ "GET",
+ {
+ returnData: true
}
- }
- );
+ )
+ ).data;
- const categories: DocsCategory[] = await categoryData.json();
const { website, home } = await parent();
return {
@@ -25,72 +24,44 @@ export const load: PageServerLoad = async ({ parent, params, cookies, fetch }) =
};
export const actions: Actions = {
- createCategory: async ({ request, fetch, cookies, params }) => {
+ createCategory: async ({ request, fetch, params }) => {
const data = await request.formData();
- const res = await fetch(`${API_BASE_PREFIX}/docs_category`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`
- },
- body: JSON.stringify({
+ return await apiRequest(fetch, `${API_BASE_PREFIX}/docs_category`, "POST", {
+ body: {
website_id: params.websiteId,
- category_name: data.get("category-name") as string,
- category_weight: data.get("category-weight") as unknown as number
- } satisfies DocsCategoryInput)
+ category_name: data.get("category-name"),
+ category_weight: data.get("category-weight")
+ },
+ successMessage: "Successfully created category"
});
-
- if (!res.ok) {
- const response = await res.json();
- return { success: false, message: response.message };
- }
-
- return { success: true, message: "Successfully created category" };
},
- updateCategory: async ({ request, fetch, cookies, params }) => {
+ updateCategory: async ({ request, fetch }) => {
const data = await request.formData();
- const res = await fetch(
- `${API_BASE_PREFIX}/docs_category?website_id=eq.${params.websiteId}&id=eq.${data.get("category-id")}`,
+ return await apiRequest(
+ fetch,
+ `${API_BASE_PREFIX}/docs_category?id=eq.${data.get("category-id")}`,
+ "PATCH",
{
- method: "PATCH",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`
- },
- body: JSON.stringify({
+ body: {
+ category_name: data.get("category-name"),
category_weight: data.get("category-weight")
- })
+ },
+ successMessage: "Successfully updated category"
}
);
-
- if (!res.ok) {
- const response = await res.json();
- return { success: false, message: response.message };
- }
-
- return { success: true, message: "Successfully updated category" };
},
- deleteCategory: async ({ request, fetch, cookies, params }) => {
+ deleteCategory: async ({ request, fetch }) => {
const data = await request.formData();
- const res = await fetch(
- `${API_BASE_PREFIX}/docs_category?website_id=eq.${params.websiteId}&id=eq.${data.get("category-id")}`,
+ return await apiRequest(
+ fetch,
+ `${API_BASE_PREFIX}/docs_category?id=eq.${data.get("category-id")}`,
+ "DELETE",
{
- method: "DELETE",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`
- }
+ successMessage: "Successfully deleted category"
}
);
-
- if (!res.ok) {
- const response = await res.json();
- return { success: false, message: response.message };
- }
-
- return { success: true, message: "Successfully deleted category" };
}
};
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/categories/+page.svelte b/web-app/src/routes/(authenticated)/website/[websiteId]/categories/+page.svelte
index 71100d9..d6f43e0 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/categories/+page.svelte
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/categories/+page.svelte
@@ -5,15 +5,18 @@
import Modal from "$lib/components/Modal.svelte";
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte";
import type { ActionData, PageServerData } from "./$types";
+ import { enhanceForm } from "$lib/utils";
+ import { sending } from "$lib/runes.svelte";
+ import { previewContent } from "$lib/runes.svelte";
const { data, form }: { data: PageServerData; form: ActionData } = $props();
- let sending = $state(false);
+ previewContent.value = data.home.main_content;
-{#if sending}
+{#if sending.value}
{/if}
@@ -21,7 +24,6 @@
id={data.website.id}
contentType={data.website.content_type}
title={data.website.title}
- previewContent={data.home.main_content}
>
@@ -31,18 +33,7 @@
Create category
- {
- sending = true;
- return async ({ update }) => {
- await update();
- window.location.hash = "!";
- sending = false;
- };
- }}
- >
+
Name:
@@ -78,17 +69,21 @@
{
- sending = true;
- return async ({ update }) => {
- await update({ reset: false });
- window.location.hash = "!";
- sending = false;
- };
- }}
+ use:enhance={enhanceForm({ reset: false, closeModal: true })}
>
+
+ Name:
+
+
+
Weight:
@@ -105,14 +100,7 @@
{
- sending = true;
- return async ({ update }) => {
- await update();
- window.location.hash = "!";
- sending = false;
- };
- }}
+ use:enhance={enhanceForm({ closeModal: true })}
>
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/collaborators/+page.server.ts b/web-app/src/routes/(authenticated)/website/[websiteId]/collaborators/+page.server.ts
index 89842a1..0854363 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/collaborators/+page.server.ts
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/collaborators/+page.server.ts
@@ -1,22 +1,20 @@
import type { Actions, PageServerLoad } from "./$types";
-import { API_BASE_PREFIX } from "$lib/server/utils";
-import type { Collab, CollabInput, User } from "$lib/db-schema";
+import { API_BASE_PREFIX, apiRequest } from "$lib/server/utils";
+import type { Collab, User } from "$lib/db-schema";
-export const load: PageServerLoad = async ({ parent, params, fetch, cookies }) => {
- const { website, home } = await parent();
-
- const collabData = await fetch(
- `${API_BASE_PREFIX}/collab?website_id=eq.${params.websiteId}&select=*,user!user_id(*)&order=last_modified_at.desc,added_at.desc`,
- {
- method: "GET",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`
+export const load: PageServerLoad = async ({ parent, params, fetch }) => {
+ const collaborators: (Collab & { user: User })[] = (
+ await apiRequest(
+ fetch,
+ `${API_BASE_PREFIX}/collab?website_id=eq.${params.websiteId}&select=*,user!user_id(*)&order=last_modified_at.desc,added_at.desc`,
+ "GET",
+ {
+ returnData: true
}
- }
- );
+ )
+ ).data;
- const collaborators: (Collab & { user: User })[] = await collabData.json();
+ const { website, home } = await parent();
return {
website,
@@ -26,83 +24,61 @@ export const load: PageServerLoad = async ({ parent, params, fetch, cookies }) =
};
export const actions: Actions = {
- addCollaborator: async ({ request, fetch, cookies, params }) => {
+ addCollaborator: async ({ request, fetch, params }) => {
const data = await request.formData();
- const userData = await fetch(`${API_BASE_PREFIX}/user?username=eq.${data.get("username")}`, {
- method: "GET",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`,
- Accept: "application/vnd.pgrst.object+json"
- }
- });
+ const user: User = (
+ await apiRequest(
+ fetch,
+ `${API_BASE_PREFIX}/user?username=eq.${data.get("username")}`,
+ "GET",
+ {
+ headers: {
+ Accept: "application/vnd.pgrst.object+json"
+ },
+ returnData: true
+ }
+ )
+ ).data;
- const user: User = await userData.json();
+ if (!user) {
+ return { success: false, message: "Specified user could not be found" };
+ }
- const res = await fetch(`${API_BASE_PREFIX}/collab`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`
- },
- body: JSON.stringify({
+ return await apiRequest(fetch, `${API_BASE_PREFIX}/collab`, "POST", {
+ body: {
website_id: params.websiteId,
user_id: user.id,
- permission_level: data.get("permission-level") as unknown as number
- } satisfies CollabInput)
+ permission_level: data.get("permission-level")
+ },
+ successMessage: "Successfully added collaborator"
});
-
- if (!res.ok) {
- const response = await res.json();
- return { success: false, message: response.message };
- }
-
- return { success: true, message: "Successfully added collaborator" };
},
- updateCollaborator: async ({ request, fetch, cookies, params }) => {
+ updateCollaborator: async ({ request, fetch, params }) => {
const data = await request.formData();
- const res = await fetch(
+ return await apiRequest(
+ fetch,
`${API_BASE_PREFIX}/collab?website_id=eq.${params.websiteId}&user_id=eq.${data.get("user-id")}`,
+ "PATCH",
{
- method: "PATCH",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`
- },
- body: JSON.stringify({
+ body: {
permission_level: data.get("permission-level")
- })
+ },
+ successMessage: "Successfully updated collaborator"
}
);
-
- if (!res.ok) {
- const response = await res.json();
- return { success: false, message: response.message };
- }
-
- return { success: true, message: "Successfully updated collaborator" };
},
- removeCollaborator: async ({ request, fetch, cookies, params }) => {
+ removeCollaborator: async ({ request, fetch, params }) => {
const data = await request.formData();
- const res = await fetch(
+ return await apiRequest(
+ fetch,
`${API_BASE_PREFIX}/collab?website_id=eq.${params.websiteId}&user_id=eq.${data.get("user-id")}`,
+ "DELETE",
{
- method: "DELETE",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`
- }
+ successMessage: "Successfully removed collaborator"
}
);
-
- if (!res.ok) {
- const response = await res.json();
- return { success: false, message: response.message };
- }
-
- return { success: true, message: "Successfully removed collaborator" };
}
};
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/collaborators/+page.svelte b/web-app/src/routes/(authenticated)/website/[websiteId]/collaborators/+page.svelte
index 1edb9f4..f2efd8e 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/collaborators/+page.svelte
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/collaborators/+page.svelte
@@ -4,16 +4,18 @@
import SuccessOrError from "$lib/components/SuccessOrError.svelte";
import Modal from "$lib/components/Modal.svelte";
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte";
+ import { enhanceForm } from "$lib/utils";
+ import { previewContent, sending } from "$lib/runes.svelte";
import type { ActionData, PageServerData } from "./$types";
const { data, form }: { data: PageServerData; form: ActionData } = $props();
- let sending = $state(false);
+ previewContent.value = data.home.main_content;
-{#if sending}
+{#if sending.value}
{/if}
@@ -21,7 +23,6 @@
id={data.website.id}
contentType={data.website.content_type}
title={data.website.title}
- previewContent={data.home.main_content}
>
@@ -34,14 +35,7 @@
{
- sending = true;
- return async ({ update }) => {
- await update();
- window.location.hash = "!";
- sending = false;
- };
- }}
+ use:enhance={enhanceForm({ closeModal: true })}
>
Username:
@@ -82,14 +76,7 @@
{
- sending = true;
- return async ({ update }) => {
- await update({ reset: false });
- window.location.hash = "!";
- sending = false;
- };
- }}
+ use:enhance={enhanceForm({ reset: false, closeModal: true })}
>
@@ -113,14 +100,7 @@
{
- sending = true;
- return async ({ update }) => {
- await update();
- window.location.hash = "!";
- sending = false;
- };
- }}
+ use:enhance={enhanceForm({ closeModal: true })}
>
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/legal-information/+page.server.ts b/web-app/src/routes/(authenticated)/website/[websiteId]/legal-information/+page.server.ts
index 86ac858..7188599 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/legal-information/+page.server.ts
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/legal-information/+page.server.ts
@@ -1,74 +1,61 @@
import type { Actions, PageServerLoad } from "./$types";
-import { API_BASE_PREFIX } from "$lib/server/utils";
+import { API_BASE_PREFIX, apiRequest } from "$lib/server/utils";
import { rm } from "node:fs/promises";
import { join } from "node:path";
-import type { LegalInformation, LegalInformationInput } from "$lib/db-schema";
+import type { LegalInformation } from "$lib/db-schema";
-export const load: PageServerLoad = async ({ parent, fetch, params, cookies }) => {
- const legalInformationData = await fetch(
- `${API_BASE_PREFIX}/legal_information?website_id=eq.${params.websiteId}`,
- {
- method: "GET",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`,
- Accept: "application/vnd.pgrst.object+json"
+export const load: PageServerLoad = async ({ parent, fetch, params }) => {
+ const legalInformation: LegalInformation = (
+ await apiRequest(
+ fetch,
+ `${API_BASE_PREFIX}/legal_information?website_id=eq.${params.websiteId}`,
+ "GET",
+ {
+ headers: {
+ Accept: "application/vnd.pgrst.object+json"
+ },
+ returnData: true
}
- }
- );
+ )
+ ).data;
- const legalInformation: LegalInformation = await legalInformationData.json();
const { website } = await parent();
return {
legalInformation,
- website
+ website,
+ API_BASE_PREFIX
};
};
export const actions: Actions = {
- createUpdateLegalInformation: async ({ request, fetch, cookies, params }) => {
+ createUpdateLegalInformation: async ({ request, fetch, params }) => {
const data = await request.formData();
- const res = await fetch(`${API_BASE_PREFIX}/legal_information`, {
- method: "POST",
+ return await apiRequest(fetch, `${API_BASE_PREFIX}/legal_information`, "POST", {
headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`,
Prefer: "resolution=merge-duplicates",
Accept: "application/vnd.pgrst.object+json"
},
- body: JSON.stringify({
+ body: {
website_id: params.websiteId,
- main_content: data.get("main-content") as string
- } satisfies LegalInformationInput)
+ main_content: data.get("main-content")
+ },
+ successMessage: "Successfully created/updated legal information"
});
-
- if (!res.ok) {
- const response = await res.json();
- return { success: false, message: response.message };
- }
-
- return {
- success: true,
- message: `Successfully ${res.status === 201 ? "created" : "updated"} legal information`
- };
},
- deleteLegalInformation: async ({ fetch, cookies, params }) => {
- const res = await fetch(
+ deleteLegalInformation: async ({ fetch, params }) => {
+ const deleteLegalInformation = await apiRequest(
+ fetch,
`${API_BASE_PREFIX}/legal_information?website_id=eq.${params.websiteId}`,
+ "DELETE",
{
- method: "DELETE",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`
- }
+ successMessage: "Successfully deleted legal information"
}
);
- if (!res.ok) {
- const response = await res.json();
- return { success: false, message: response.message };
+ if (!deleteLegalInformation.success) {
+ return deleteLegalInformation;
}
await rm(
@@ -76,6 +63,6 @@ export const actions: Actions = {
{ force: true }
);
- return { success: true, message: `Successfully deleted legal information` };
+ return deleteLegalInformation;
}
};
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/legal-information/+page.svelte b/web-app/src/routes/(authenticated)/website/[websiteId]/legal-information/+page.svelte
index ea23367..b00f2d0 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/legal-information/+page.svelte
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/legal-information/+page.svelte
@@ -4,25 +4,19 @@
import SuccessOrError from "$lib/components/SuccessOrError.svelte";
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte";
import Modal from "$lib/components/Modal.svelte";
+ import { enhanceForm } from "$lib/utils";
+ import { sending, previewContent } from "$lib/runes.svelte";
import type { ActionData, PageServerData } from "./$types";
+ import MarkdownEditor from "$lib/components/MarkdownEditor.svelte";
const { data, form }: { data: PageServerData; form: ActionData } = $props();
- let previewContent = $state(data.legalInformation.main_content);
- let mainContentTextarea: HTMLTextAreaElement;
- let textareaScrollTop = $state(0);
-
- const updateScrollPercentage = () => {
- const { scrollTop, scrollHeight, clientHeight } = mainContentTextarea;
- textareaScrollTop = (scrollTop / (scrollHeight - clientHeight)) * 100;
- };
-
- let sending = $state(false);
+ previewContent.value = data.legalInformation?.main_content ?? "";
-{#if sending}
+{#if sending.value}
{/if}
@@ -30,9 +24,6 @@
id={data.website.id}
contentType={data.website.content_type}
title={data.website.title}
- previewContent={previewContent ||
- "Put some markdown content in main content to see a live preview here"}
- previewScrollTop={textareaScrollTop}
>
@@ -245,8 +251,6 @@
.pagination {
display: flex;
align-items: center;
- margin-inline: var(--space-2xs);
- margin-block: var(--space-s);
flex-wrap: wrap;
gap: var(--space-xs);
justify-content: end;
@@ -256,8 +260,8 @@
margin-inline-start: auto;
}
- button[disabled] {
- opacity: 0.5;
+ button:disabled {
pointer-events: none;
+ color: hsl(0 0% 50%);
}
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.server.ts b/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.server.ts
index 674e6e7..ea8a1a3 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.server.ts
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.server.ts
@@ -1,29 +1,29 @@
-import { readFile, mkdir, writeFile } from "node:fs/promises";
-import { join } from "node:path";
-import { type WebsiteOverview, slugify } from "$lib/utils";
-import type { Actions, PageServerLoad } from "./$types";
-import { API_BASE_PREFIX } from "$lib/server/utils";
-import { render } from "svelte/server";
-import BlogIndex from "$lib/templates/blog/BlogIndex.svelte";
-import BlogArticle from "$lib/templates/blog/BlogArticle.svelte";
-import DocsIndex from "$lib/templates/docs/DocsIndex.svelte";
-import DocsArticle from "$lib/templates/docs/DocsArticle.svelte";
import { dev } from "$app/environment";
+import { API_BASE_PREFIX, apiRequest } from "$lib/server/utils";
+import BlogArticle from "$lib/templates/blog/BlogArticle.svelte";
+import BlogIndex from "$lib/templates/blog/BlogIndex.svelte";
+import DocsArticle from "$lib/templates/docs/DocsArticle.svelte";
+import DocsIndex from "$lib/templates/docs/DocsIndex.svelte";
+import { type WebsiteOverview, hexToHSL, slugify } from "$lib/utils";
+import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
+import { join } from "node:path";
+import { render } from "svelte/server";
+import type { Actions, PageServerLoad } from "./$types";
-export const load: PageServerLoad = async ({ params, fetch, cookies }) => {
- const websiteOverviewData = await fetch(
- `${API_BASE_PREFIX}/website?id=eq.${params.websiteId}&select=*,settings(*),header(*),home(*),footer(*),article(*,docs_category(*)),legal_information(*)`,
- {
- method: "GET",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`,
- Accept: "application/vnd.pgrst.object+json"
+export const load: PageServerLoad = async ({ params, fetch }) => {
+ const websiteOverview: WebsiteOverview = (
+ await apiRequest(
+ fetch,
+ `${API_BASE_PREFIX}/website?id=eq.${params.websiteId}&select=*,settings(*),header(*),home(*),footer(*),article(*,docs_category(*)),legal_information(*),domain_prefix(*)`,
+ "GET",
+ {
+ headers: {
+ Accept: "application/vnd.pgrst.object+json"
+ },
+ returnData: true
}
- }
- );
-
- const websiteOverview: WebsiteOverview = await websiteOverviewData.json();
+ )
+ ).data;
generateStaticFiles(websiteOverview);
@@ -36,10 +36,13 @@ export const load: PageServerLoad = async ({ params, fetch, cookies }) => {
}/previews/${websiteOverview.id}/`;
const websiteProdUrl = dev
- ? `http://localhost:18000/${websiteOverview.id}/`
+ ? `http://localhost:18000/${websiteOverview.domain_prefix?.prefix ?? websiteOverview.id}/`
: process.env.ORIGIN
- ? process.env.ORIGIN.replace("//", `//${websiteOverview.id}.`)
- : `http://localhost:18000/${websiteOverview.id}/`;
+ ? process.env.ORIGIN.replace(
+ "//",
+ `//${websiteOverview.domain_prefix?.prefix ?? websiteOverview.id}.`
+ )
+ : `http://localhost:18000/${websiteOverview.domain_prefix?.prefix ?? websiteOverview.id}/`;
return {
websiteOverview,
@@ -49,43 +52,110 @@ export const load: PageServerLoad = async ({ params, fetch, cookies }) => {
};
export const actions: Actions = {
- publishWebsite: async ({ fetch, params, cookies }) => {
- const websiteOverviewData = await fetch(
- `${API_BASE_PREFIX}/website?id=eq.${params.websiteId}&select=*,settings(*),header(*),home(*),footer(*),article(*,docs_category(*)),legal_information(*)`,
- {
- method: "GET",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`,
- Accept: "application/vnd.pgrst.object+json"
+ publishWebsite: async ({ fetch, params }) => {
+ const websiteOverview: WebsiteOverview = (
+ await apiRequest(
+ fetch,
+ `${API_BASE_PREFIX}/website?id=eq.${params.websiteId}&select=*,settings(*),header(*),home(*),footer(*),article(*,docs_category(*)),legal_information(*),domain_prefix(*)`,
+ "GET",
+ {
+ headers: {
+ Accept: "application/vnd.pgrst.object+json"
+ },
+ returnData: true
}
+ )
+ ).data;
+
+ generateStaticFiles(websiteOverview, false);
+
+ return await apiRequest(
+ fetch,
+ `${API_BASE_PREFIX}/website?id=eq.${params.websiteId}`,
+ "PATCH",
+ {
+ body: {
+ is_published: true
+ },
+ successMessage: "Successfully published website"
+ }
+ );
+ },
+ createUpdateCustomDomainPrefix: async ({ request, fetch, params }) => {
+ const data = await request.formData();
+
+ const oldDomainPrefix = (
+ await apiRequest(
+ fetch,
+ `${API_BASE_PREFIX}/domain_prefix?website_id=eq.${params.websiteId}`,
+ "GET",
+ {
+ headers: {
+ Accept: "application/vnd.pgrst.object+json"
+ },
+ returnData: true
+ }
+ )
+ ).data;
+
+ const newDomainPrefix = await apiRequest(fetch, `${API_BASE_PREFIX}/domain_prefix`, "POST", {
+ headers: {
+ Prefer: "resolution=merge-duplicates",
+ Accept: "application/vnd.pgrst.object+json"
+ },
+ body: {
+ website_id: params.websiteId,
+ prefix: data.get("domain-prefix")
+ },
+ successMessage: "Successfully created/updated domain prefix"
+ });
+
+ if (!newDomainPrefix.success) {
+ return newDomainPrefix;
+ }
+
+ await rename(
+ join(
+ "/",
+ "var",
+ "www",
+ "archtika-websites",
+ oldDomainPrefix?.prefix ? oldDomainPrefix.prefix : params.websiteId
+ ),
+ join("/", "var", "www", "archtika-websites", data.get("domain-prefix") as string)
+ );
+
+ return newDomainPrefix;
+ },
+ deleteCustomDomainPrefix: async ({ fetch, params }) => {
+ const customPrefix = await apiRequest(
+ fetch,
+ `${API_BASE_PREFIX}/domain_prefix?website_id=eq.${params.websiteId}`,
+ "DELETE",
+ {
+ headers: {
+ Prefer: "return=representation",
+ Accept: "application/vnd.pgrst.object+json"
+ },
+ successMessage: "Successfully deleted domain prefix",
+ returnData: true
}
);
- const websiteOverview = await websiteOverviewData.json();
- generateStaticFiles(websiteOverview, false);
-
- const res = await fetch(`${API_BASE_PREFIX}/website?id=eq.${params.websiteId}`, {
- method: "PATCH",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${cookies.get("session_token")}`
- },
- body: JSON.stringify({
- is_published: true
- })
- });
-
- if (!res.ok) {
- const response = await res.json();
- return { success: false, message: response.message };
+ if (!customPrefix.success) {
+ return customPrefix;
}
- return { success: true, message: "Successfully published website" };
+ await rename(
+ join("/", "var", "www", "archtika-websites", customPrefix.data.prefix),
+ join("/", "var", "www", "archtika-websites", params.websiteId)
+ );
+
+ return customPrefix;
}
};
-const generateStaticFiles = async (websiteData: WebsiteOverview, isPreview: boolean = true) => {
+const generateStaticFiles = async (websiteData: WebsiteOverview, isPreview = true) => {
const fileContents = (head: string, body: string) => {
return `
@@ -112,7 +182,13 @@ const generateStaticFiles = async (websiteData: WebsiteOverview, isPreview: bool
if (isPreview) {
uploadDir = join("/", "var", "www", "archtika-websites", "previews", websiteData.id);
} else {
- uploadDir = join("/", "var", "www", "archtika-websites", websiteData.id);
+ uploadDir = join(
+ "/",
+ "var",
+ "www",
+ "archtika-websites",
+ websiteData.domain_prefix?.prefix ?? websiteData.id
+ );
}
await mkdir(uploadDir, { recursive: true });
@@ -157,21 +233,35 @@ const generateStaticFiles = async (websiteData: WebsiteOverview, isPreview: bool
encoding: "utf-8"
}
);
+
+ const {
+ h: hDark,
+ s: sDark,
+ l: lDark
+ } = hexToHSL(websiteData.settings.background_color_dark_theme);
+ const {
+ h: hLight,
+ s: sLight,
+ l: lLight
+ } = hexToHSL(websiteData.settings.background_color_light_theme);
+
await writeFile(
join(uploadDir, "styles.css"),
commonStyles
.concat(specificStyles)
+ .replace(/(?<=\/\* BACKGROUND_COLOR_DARK_THEME_H \*\/\s*).*(?=;)/, ` ${hDark}`)
+ .replace(/(?<=\/\* BACKGROUND_COLOR_DARK_THEME_S \*\/\s*).*(?=;)/, ` ${sDark}%`)
+ .replace(/(?<=\/\* BACKGROUND_COLOR_DARK_THEME_L \*\/\s*).*(?=;)/, ` ${lDark}%`)
+ .replace(/(?<=\/\* BACKGROUND_COLOR_LIGHT_THEME_H \*\/\s*).*(?=;)/, ` ${hLight}`)
+ .replace(/(?<=\/\* BACKGROUND_COLOR_LIGHT_THEME_S \*\/\s*).*(?=;)/, ` ${sLight}%`)
+ .replace(/(?<=\/\* BACKGROUND_COLOR_LIGHT_THEME_L \*\/\s*).*(?=;)/, ` ${lLight}%`)
.replace(
- /--color-accent:\s*(.*?);/,
- `--color-accent: ${websiteData.settings.accent_color_dark_theme};`
+ /(?<=\/\* ACCENT_COLOR_DARK_THEME \*\/\s*).*(?=;)/,
+ ` ${websiteData.settings.accent_color_dark_theme}`
)
.replace(
- /@media\s*\(prefers-color-scheme:\s*dark\)\s*{[^}]*--color-accent:\s*(.*?);/,
- (match) =>
- match.replace(
- /--color-accent:\s*(.*?);/,
- `--color-accent: ${websiteData.settings.accent_color_light_theme};`
- )
+ /(?<=\/\* ACCENT_COLOR_LIGHT_THEME \*\/\s*).*(?=;)/,
+ ` ${websiteData.settings.accent_color_light_theme}`
)
);
};
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.svelte b/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.svelte
index bd97a70..51d231f 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.svelte
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.svelte
@@ -4,15 +4,19 @@
import SuccessOrError from "$lib/components/SuccessOrError.svelte";
import type { ActionData, PageServerData } from "./$types";
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte";
+ import Modal from "$lib/components/Modal.svelte";
+ import { enhanceForm } from "$lib/utils";
+ import { sending } from "$lib/runes.svelte";
+ import { previewContent } from "$lib/runes.svelte";
const { data, form }: { data: PageServerData; form: ActionData } = $props();
- let sending = $state(false);
+ previewContent.value = data.websitePreviewUrl;
-{#if sending}
+{#if sending.value}
{/if}
@@ -20,7 +24,6 @@
id={data.websiteOverview.id}
contentType={data.websiteOverview.content_type}
title={data.websiteOverview.title}
- previewContent={data.websitePreviewUrl}
fullPreview={true}
>
@@ -32,31 +35,63 @@
is published. If you are happy with the results, click the button below and your website will
be published on the Internet.
- {
- sending = true;
- return async ({ update }) => {
- await update();
- sending = false;
- };
- }}
- >
+
-
- {#if data.websiteOverview.is_published}
-
- {/if}
+
+ {#if data.websiteOverview.is_published}
+
+
+
+ {/if}
diff --git a/web-app/src/routes/+layout.svelte b/web-app/src/routes/+layout.svelte
index 75a5ce9..5dac755 100644
--- a/web-app/src/routes/+layout.svelte
+++ b/web-app/src/routes/+layout.svelte
@@ -5,6 +5,7 @@
import type { Snippet } from "svelte";
import { navigating } from "$app/stores";
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte";
+ import { LOADING_DELAY } from "$lib/utils";
const { data, children }: { data: LayoutServerData; children: Snippet } = $props();
@@ -14,23 +15,43 @@
? "Dashboard"
: `${$page.url.pathname.charAt(1).toUpperCase()}${$page.url.pathname.slice(2)}`
);
+
+ let loading = $state(false);
+ let loadingDelay: number;
+
+ $effect(() => {
+ if ($navigating && ["link", "goto"].includes($navigating.type)) {
+ loadingDelay = window.setTimeout(() => (loading = true), LOADING_DELAY);
+ } else {
+ window.clearTimeout(loadingDelay);
+ loading = false;
+ }
+ });
-{#if $navigating && ["link", "goto"].includes($navigating.type)}
+{#if loading}
{/if}
archtika | {routeName.replaceAll("/", " - ")}
+