diff --git a/components/CopyButton.tsx b/components/CopyButton.tsx
new file mode 100644
index 0000000..54fe4b9
--- /dev/null
+++ b/components/CopyButton.tsx
@@ -0,0 +1,58 @@
+import { useState } from 'preact/hooks';
+
+export default function CopyButton({ value }: { value: string }) {
+ const [copied, setCopied] = useState(false);
+
+ const handleCopy = async () => {
+ try {
+ await navigator.clipboard.writeText(value);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 1500);
+ } catch (e) {
+ console.error('Failed to copy:', e);
+ }
+ };
+
+ return (
+
+ );
+}
diff --git a/components/CopyField.tsx b/components/CopyField.tsx
index b895c1c..72cd2f3 100644
--- a/components/CopyField.tsx
+++ b/components/CopyField.tsx
@@ -1,4 +1,4 @@
-import { useState } from 'preact/hooks';
+import CopyButton from './CopyButton.tsx';
type CopyFieldProps = {
label: string;
@@ -7,18 +7,6 @@ type CopyFieldProps = {
};
export default function CopyField({ label, value, title }: CopyFieldProps) {
- const [copied, setCopied] = useState(false);
-
- const handleCopy = async () => {
- try {
- await navigator.clipboard.writeText(value);
- setCopied(true);
- setTimeout(() => setCopied(false), 1500);
- } catch (e) {
- console.error('Failed to copy:', e);
- }
- };
-
return (
@@ -31,46 +19,7 @@ export default function CopyField({ label, value, title }: CopyFieldProps) {
>
{value}
-
+
diff --git a/deno.json b/deno.json
index 4c9c633..13bd56f 100644
--- a/deno.json
+++ b/deno.json
@@ -9,39 +9,24 @@
"preview": "deno task build && deno task start",
"clean": "rm -rf ./dist && rm -rf ./_fresh && rm -rf ./node_modules && rm -f ./deno.lock && rm -f ./package-lock.json"
},
- "lint": {
- "rules": {
- "tags": [
- "fresh",
- "recommended"
- ]
- }
- },
- "exclude": [
- "**/_fresh/*"
- ],
+ "lint": { "rules": { "tags": ["fresh", "recommended"] } },
+ "exclude": ["**/_fresh/*"],
"imports": {
"@/": "./",
"@deno/gfm": "jsr:@deno/gfm@^0.11.0",
- "fresh": "jsr:@fresh/core@^2.1.4",
- "preact": "npm:preact@^10.27.2",
- "@preact/signals": "npm:@preact/signals@^2.3.2",
- "@fresh/plugin-vite": "jsr:@fresh/plugin-vite@^1.0.7",
- "vite": "npm:vite@^7.1.12",
- "tailwindcss": "npm:tailwindcss@^4.1.16",
- "@tailwindcss/vite": "npm:@tailwindcss/vite@^4.1.16",
- "@valibot/valibot": "jsr:@valibot/valibot@^1.1.0",
+ "fresh": "jsr:@fresh/core@^2.2.0",
+ "preact": "npm:preact@^10.28.2",
+ "@preact/signals": "npm:@preact/signals@^2.5.1",
+ "@fresh/plugin-vite": "jsr:@fresh/plugin-vite@^1.0.8",
+ "vite": "npm:vite@^7.3.1",
+ "tailwindcss": "npm:tailwindcss@^4.1.18",
+ "@tailwindcss/vite": "npm:@tailwindcss/vite@^4.1.18",
+ "@valibot/valibot": "jsr:@valibot/valibot@^1.2.0",
"$std/": "https://deno.land/std@0.216.0/",
"bcrypt": "https://deno.land/x/bcrypt/mod.ts"
},
"compilerOptions": {
- "lib": [
- "dom",
- "dom.asynciterable",
- "dom.iterable",
- "deno.ns",
- "deno.unstable"
- ],
+ "lib": ["dom", "dom.asynciterable", "dom.iterable", "deno.ns", "deno.unstable"],
"jsx": "precompile",
"jsxImportSource": "preact",
"jsxPrecompileSkipElements": [
@@ -60,9 +45,7 @@
"noscript",
"template"
],
- "types": [
- "vite/client"
- ]
+ "types": ["vite/client"]
},
"fmt": {
"useTabs": false,
@@ -70,12 +53,7 @@
"indentWidth": 4,
"singleQuote": true,
"proseWrap": "always",
- "exclude": [
- "**/node_modules/**",
- "**/.git/**",
- "**/.vscode/**",
- "**/.github/**"
- ]
+ "exclude": ["**/node_modules/**", "**/.git/**", "**/.vscode/**", "**/.github/**"]
},
"nodeModulesDir": "auto"
}
diff --git a/deno.lock b/deno.lock
index 344f979..935ef7d 100644
--- a/deno.lock
+++ b/deno.lock
@@ -1,41 +1,42 @@
{
"version": "5",
"specifiers": {
- "jsr:@deno/esbuild-plugin@^1.2.0": "1.2.0",
+ "jsr:@deno/esbuild-plugin@^1.2.0": "1.2.1",
"jsr:@deno/gfm@0.11": "0.11.0",
- "jsr:@deno/loader@~0.3.2": "0.3.9",
- "jsr:@deno/loader@~0.3.3": "0.3.9",
+ "jsr:@deno/loader@~0.3.10": "0.3.11",
+ "jsr:@deno/loader@~0.3.2": "0.3.11",
"jsr:@denosaurs/emoji@~0.3.1": "0.3.1",
"jsr:@fresh/build-id@1": "1.0.1",
- "jsr:@fresh/core@2": "2.1.4",
- "jsr:@fresh/core@^2.1.4": "2.1.4",
- "jsr:@fresh/plugin-vite@^1.0.7": "1.0.7",
+ "jsr:@fresh/core@2": "2.2.0",
+ "jsr:@fresh/core@^2.2.0": "2.2.0",
+ "jsr:@fresh/plugin-vite@^1.0.8": "1.0.8",
"jsr:@std/bytes@^1.0.6": "1.0.6",
- "jsr:@std/dotenv@~0.225.5": "0.225.5",
+ "jsr:@std/dotenv@~0.225.5": "0.225.6",
"jsr:@std/encoding@^1.0.10": "1.0.10",
"jsr:@std/fmt@^1.0.7": "1.0.8",
"jsr:@std/fmt@^1.0.8": "1.0.8",
- "jsr:@std/fs@^1.0.19": "1.0.19",
+ "jsr:@std/fs@^1.0.19": "1.0.21",
"jsr:@std/html@^1.0.5": "1.0.5",
- "jsr:@std/http@^1.0.21": "1.0.21",
- "jsr:@std/internal@^1.0.10": "1.0.12",
+ "jsr:@std/http@^1.0.21": "1.0.23",
+ "jsr:@std/internal@^1.0.12": "1.0.12",
"jsr:@std/json@^1.0.2": "1.0.2",
"jsr:@std/jsonc@^1.0.2": "1.0.2",
"jsr:@std/media-types@^1.1.0": "1.1.0",
- "jsr:@std/path@1": "1.1.2",
- "jsr:@std/path@^1.1.1": "1.1.2",
- "jsr:@std/path@^1.1.2": "1.1.2",
- "jsr:@std/semver@^1.0.6": "1.0.6",
- "jsr:@std/uuid@^1.0.9": "1.0.9",
- "jsr:@valibot/valibot@^1.1.0": "1.1.0",
+ "jsr:@std/path@1": "1.1.4",
+ "jsr:@std/path@^1.1.1": "1.1.4",
+ "jsr:@std/path@^1.1.2": "1.1.4",
+ "jsr:@std/path@^1.1.4": "1.1.4",
+ "jsr:@std/semver@^1.0.6": "1.0.7",
+ "jsr:@std/uuid@^1.0.9": "1.1.0",
+ "jsr:@valibot/valibot@^1.2.0": "1.2.0",
"npm:@babel/core@^7.28.0": "7.28.5",
"npm:@babel/preset-react@^7.27.1": "7.28.5_@babel+core@7.28.5",
"npm:@mjackson/node-fetch-server@0.7": "0.7.0",
"npm:@opentelemetry/api@^1.9.0": "1.9.0",
- "npm:@preact/signals@^2.2.1": "2.3.2_preact@10.27.2",
- "npm:@preact/signals@^2.3.2": "2.3.2_preact@10.27.2",
- "npm:@prefresh/vite@^2.4.8": "2.4.11_preact@10.27.2_vite@7.1.12__picomatch@4.0.3_@types+node@24.2.0",
- "npm:@tailwindcss/vite@^4.1.16": "4.1.16_vite@7.1.12__picomatch@4.0.3_@types+node@24.2.0",
+ "npm:@preact/signals@^2.2.1": "2.5.1_preact@10.28.2",
+ "npm:@preact/signals@^2.5.1": "2.5.1_preact@10.28.2",
+ "npm:@prefresh/vite@^2.4.8": "2.4.11_preact@10.28.2_vite@7.3.1__@types+node@24.2.0__picomatch@4.0.3_@types+node@24.2.0",
+ "npm:@tailwindcss/vite@^4.1.18": "4.1.18_vite@7.3.1__@types+node@24.2.0__picomatch@4.0.3_@types+node@24.2.0",
"npm:@types/node@*": "24.2.0",
"npm:esbuild-wasm@~0.25.11": "0.25.12",
"npm:esbuild@0.25.7": "0.25.7",
@@ -47,22 +48,23 @@
"npm:marked-footnote@^1.2.0": "1.4.0_marked@12.0.2",
"npm:marked-gfm-heading-id@^3.1.0": "3.2.0_marked@12.0.2",
"npm:marked@12": "12.0.2",
- "npm:preact-render-to-string@^6.6.3": "6.6.3_preact@10.27.2",
- "npm:preact@^10.27.0": "10.27.2",
- "npm:preact@^10.27.2": "10.27.2",
+ "npm:preact-render-to-string@^6.6.3": "6.6.5_preact@10.28.2",
+ "npm:preact@^10.27.0": "10.28.2",
+ "npm:preact@^10.27.2": "10.28.2",
+ "npm:preact@^10.28.2": "10.28.2",
"npm:prismjs@^1.29.0": "1.30.0",
- "npm:rollup@^4.50.0": "4.52.5",
+ "npm:rollup@^4.50.0": "4.55.1",
"npm:sanitize-html@^2.13.0": "2.17.0",
- "npm:tailwindcss@^4.1.16": "4.1.16",
- "npm:vite@*": "7.1.12_picomatch@4.0.3_@types+node@24.2.0",
- "npm:vite@^7.1.12": "7.1.12_picomatch@4.0.3_@types+node@24.2.0",
- "npm:vite@^7.1.4": "7.1.12_picomatch@4.0.3_@types+node@24.2.0"
+ "npm:tailwindcss@^4.1.18": "4.1.18",
+ "npm:vite@*": "7.3.1_@types+node@24.2.0_picomatch@4.0.3",
+ "npm:vite@^7.1.4": "7.3.1_@types+node@24.2.0_picomatch@4.0.3",
+ "npm:vite@^7.3.1": "7.3.1_@types+node@24.2.0_picomatch@4.0.3"
},
"jsr": {
- "@deno/esbuild-plugin@1.2.0": {
- "integrity": "04ddd0fca9416d8a2866263928a53b9d5ed08dfca064d64504a0aaf9800c709e",
+ "@deno/esbuild-plugin@1.2.1": {
+ "integrity": "df629467913adc1f960149fdfa3a3430ba8c20381c310fba096db244e6c3c9f6",
"dependencies": [
- "jsr:@deno/loader@~0.3.3",
+ "jsr:@deno/loader@~0.3.10",
"jsr:@std/path@^1.1.1",
"npm:esbuild@~0.25.5"
]
@@ -82,8 +84,8 @@
"npm:sanitize-html"
]
},
- "@deno/loader@0.3.9": {
- "integrity": "703d44656f7da0fa4a4a7f8a5105b5b41320821286508c2967b4252a00a2506f"
+ "@deno/loader@0.3.11": {
+ "integrity": "7c62f4f09cdfc34e66ba25b5a775a1830cbb5266b3e39f67b0f620c75484df8d"
},
"@denosaurs/emoji@0.3.1": {
"integrity": "b0aed5f55dec99e83da7c9637fe0a36d1d6252b7c99deaaa3fc5dea3fcf3da8b"
@@ -94,8 +96,8 @@
"jsr:@std/encoding"
]
},
- "@fresh/core@2.1.4": {
- "integrity": "ce6321efe9ec5de38631a52a79f1691e37805c11df8b96472c788805e5e3a214",
+ "@fresh/core@2.2.0": {
+ "integrity": "b3c00f82288a2c4c8ec85e4abb67b080b366ec5971860f2f2898eb281ea1a80f",
"dependencies": [
"jsr:@deno/esbuild-plugin",
"jsr:@fresh/build-id",
@@ -118,12 +120,12 @@
"npm:preact@^10.27.2"
]
},
- "@fresh/plugin-vite@1.0.7": {
- "integrity": "0dd7048f7c5d5cf7f62b29b4653a18cfa4dc81e074c4c0ab9e8166cce639cbeb",
+ "@fresh/plugin-vite@1.0.8": {
+ "integrity": "5780d842ed82e4cbccd93dd8ba2d54bf59dff5aee65921134aab15a4cd457c56",
"dependencies": [
"jsr:@deno/loader@~0.3.2",
"jsr:@fresh/core@2",
- "jsr:@fresh/core@^2.1.4",
+ "jsr:@fresh/core@^2.2.0",
"jsr:@std/dotenv",
"jsr:@std/fmt@^1.0.7",
"jsr:@std/path@1",
@@ -138,8 +140,8 @@
"@std/bytes@1.0.6": {
"integrity": "f6ac6adbd8ccd99314045f5703e23af0a68d7f7e58364b47d2c7f408aeb5820a"
},
- "@std/dotenv@0.225.5": {
- "integrity": "9ce6f9d0ec3311f74a32535aa1b8c62ed88b1ab91b7f0815797d77a6f60c922f"
+ "@std/dotenv@0.225.6": {
+ "integrity": "1d6f9db72f565bd26790fa034c26e45ecb260b5245417be76c2279e5734c421b"
},
"@std/encoding@1.0.10": {
"integrity": "8783c6384a2d13abd5e9e87a7ae0520a30e9f56aeeaa3bdf910a3eaaf5c811a1"
@@ -147,17 +149,17 @@
"@std/fmt@1.0.8": {
"integrity": "71e1fc498787e4434d213647a6e43e794af4fd393ef8f52062246e06f7e372b7"
},
- "@std/fs@1.0.19": {
- "integrity": "051968c2b1eae4d2ea9f79a08a3845740ef6af10356aff43d3e2ef11ed09fb06",
+ "@std/fs@1.0.21": {
+ "integrity": "d720fe1056d78d43065a4d6e0eeb2b19f34adb8a0bc7caf3a4dbf1d4178252cd",
"dependencies": [
- "jsr:@std/path@^1.1.1"
+ "jsr:@std/path@^1.1.4"
]
},
"@std/html@1.0.5": {
"integrity": "4e2d693f474cae8c16a920fa5e15a3b72267b94b84667f11a50c6dd1cb18d35e"
},
- "@std/http@1.0.21": {
- "integrity": "abb5c747651ee6e3ea6139858fd9b1810d2c97f53a5e6722f3b6d27a6d263edc",
+ "@std/http@1.0.23": {
+ "integrity": "6634e9e034c589bf35101c1b5ee5bbf052a5987abca20f903e58bdba85c80dee",
"dependencies": [
"jsr:@std/encoding"
]
@@ -177,23 +179,23 @@
"@std/media-types@1.1.0": {
"integrity": "c9d093f0c05c3512932b330e3cc1fe1d627b301db33a4c2c2185c02471d6eaa4"
},
- "@std/path@1.1.2": {
- "integrity": "c0b13b97dfe06546d5e16bf3966b1cadf92e1cc83e56ba5476ad8b498d9e3038",
+ "@std/path@1.1.4": {
+ "integrity": "1d2d43f39efb1b42f0b1882a25486647cb851481862dc7313390b2bb044314b5",
"dependencies": [
"jsr:@std/internal"
]
},
- "@std/semver@1.0.6": {
- "integrity": "b7c98ae2843547cf3f7ac37f3995889e6e4cee0a97b57b57f17f62722843303c"
+ "@std/semver@1.0.7": {
+ "integrity": "7d5f65391762dc4358abde80fc3354086ddb40101f140295e60f290c138887d0"
},
- "@std/uuid@1.0.9": {
- "integrity": "44b627bf2d372fe1bd099e2ad41b2be41a777fc94e62a3151006895a037f1642",
+ "@std/uuid@1.1.0": {
+ "integrity": "6268db2ccf172849c9be80763354ca305d49ef4af41fe995623d44fcc3f7457c",
"dependencies": [
"jsr:@std/bytes"
]
},
- "@valibot/valibot@1.1.0": {
- "integrity": "2617f02b532011b8140926899d420a3e1bbb0fcb7cdf8e7b669df89e7edd7f5f"
+ "@valibot/valibot@1.2.0": {
+ "integrity": "61c118a4d027ed55912caf381c78f0a178f335f46ad0c4bcb136498dc1ef2285"
}
},
"npm": {
@@ -378,266 +380,266 @@
"@babel/helper-validator-identifier"
]
},
- "@esbuild/aix-ppc64@0.25.12": {
- "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
- "os": ["aix"],
- "cpu": ["ppc64"]
- },
"@esbuild/aix-ppc64@0.25.7": {
"integrity": "sha512-uD0kKFHh6ETr8TqEtaAcV+dn/2qnYbH/+8wGEdY70Qf7l1l/jmBUbrmQqwiPKAQE6cOQ7dTj6Xr0HzQDGHyceQ==",
"os": ["aix"],
"cpu": ["ppc64"]
},
- "@esbuild/android-arm64@0.25.12": {
- "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
- "os": ["android"],
- "cpu": ["arm64"]
+ "@esbuild/aix-ppc64@0.27.2": {
+ "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
+ "os": ["aix"],
+ "cpu": ["ppc64"]
},
"@esbuild/android-arm64@0.25.7": {
"integrity": "sha512-p0ohDnwyIbAtztHTNUTzN5EGD/HJLs1bwysrOPgSdlIA6NDnReoVfoCyxG6W1d85jr2X80Uq5KHftyYgaK9LPQ==",
"os": ["android"],
"cpu": ["arm64"]
},
- "@esbuild/android-arm@0.25.12": {
- "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "@esbuild/android-arm64@0.27.2": {
+ "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
"os": ["android"],
- "cpu": ["arm"]
+ "cpu": ["arm64"]
},
"@esbuild/android-arm@0.25.7": {
"integrity": "sha512-Jhuet0g1k9rAJHrXGIh7sFknFuT4sfytYZpZpuZl7YKDhnPByVAm5oy2LEBmMbuYf3ejWVYCc2seX81Mk+madA==",
"os": ["android"],
"cpu": ["arm"]
},
- "@esbuild/android-x64@0.25.12": {
- "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "@esbuild/android-arm@0.27.2": {
+ "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
"os": ["android"],
- "cpu": ["x64"]
+ "cpu": ["arm"]
},
"@esbuild/android-x64@0.25.7": {
"integrity": "sha512-mMxIJFlSgVK23HSsII3ZX9T2xKrBCDGyk0qiZnIW10LLFFtZLkFD6imZHu7gUo2wkNZwS9Yj3mOtZD3ZPcjCcw==",
"os": ["android"],
"cpu": ["x64"]
},
- "@esbuild/darwin-arm64@0.25.12": {
- "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
- "os": ["darwin"],
- "cpu": ["arm64"]
+ "@esbuild/android-x64@0.27.2": {
+ "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
+ "os": ["android"],
+ "cpu": ["x64"]
},
"@esbuild/darwin-arm64@0.25.7": {
"integrity": "sha512-jyOFLGP2WwRwxM8F1VpP6gcdIJc8jq2CUrURbbTouJoRO7XCkU8GdnTDFIHdcifVBT45cJlOYsZ1kSlfbKjYUQ==",
"os": ["darwin"],
"cpu": ["arm64"]
},
- "@esbuild/darwin-x64@0.25.12": {
- "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "@esbuild/darwin-arm64@0.27.2": {
+ "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
"os": ["darwin"],
- "cpu": ["x64"]
+ "cpu": ["arm64"]
},
"@esbuild/darwin-x64@0.25.7": {
"integrity": "sha512-m9bVWqZCwQ1BthruifvG64hG03zzz9gE2r/vYAhztBna1/+qXiHyP9WgnyZqHgGeXoimJPhAmxfbeU+nMng6ZA==",
"os": ["darwin"],
"cpu": ["x64"]
},
- "@esbuild/freebsd-arm64@0.25.12": {
- "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
- "os": ["freebsd"],
- "cpu": ["arm64"]
+ "@esbuild/darwin-x64@0.27.2": {
+ "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
+ "os": ["darwin"],
+ "cpu": ["x64"]
},
"@esbuild/freebsd-arm64@0.25.7": {
"integrity": "sha512-Bss7P4r6uhr3kDzRjPNEnTm/oIBdTPRNQuwaEFWT/uvt6A1YzK/yn5kcx5ZxZ9swOga7LqeYlu7bDIpDoS01bA==",
"os": ["freebsd"],
"cpu": ["arm64"]
},
- "@esbuild/freebsd-x64@0.25.12": {
- "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "@esbuild/freebsd-arm64@0.27.2": {
+ "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
"os": ["freebsd"],
- "cpu": ["x64"]
+ "cpu": ["arm64"]
},
"@esbuild/freebsd-x64@0.25.7": {
"integrity": "sha512-S3BFyjW81LXG7Vqmr37ddbThrm3A84yE7ey/ERBlK9dIiaWgrjRlre3pbG7txh1Uaxz8N7wGGQXmC9zV+LIpBQ==",
"os": ["freebsd"],
"cpu": ["x64"]
},
- "@esbuild/linux-arm64@0.25.12": {
- "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
- "os": ["linux"],
- "cpu": ["arm64"]
+ "@esbuild/freebsd-x64@0.27.2": {
+ "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
+ "os": ["freebsd"],
+ "cpu": ["x64"]
},
"@esbuild/linux-arm64@0.25.7": {
"integrity": "sha512-HfQZQqrNOfS1Okn7PcsGUqHymL1cWGBslf78dGvtrj8q7cN3FkapFgNA4l/a5lXDwr7BqP2BSO6mz9UremNPbg==",
"os": ["linux"],
"cpu": ["arm64"]
},
- "@esbuild/linux-arm@0.25.12": {
- "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "@esbuild/linux-arm64@0.27.2": {
+ "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
"os": ["linux"],
- "cpu": ["arm"]
+ "cpu": ["arm64"]
},
"@esbuild/linux-arm@0.25.7": {
"integrity": "sha512-JZMIci/1m5vfQuhKoFXogCKVYVfYQmoZJg8vSIMR4TUXbF+0aNlfXH3DGFEFMElT8hOTUF5hisdZhnrZO/bkDw==",
"os": ["linux"],
"cpu": ["arm"]
},
- "@esbuild/linux-ia32@0.25.12": {
- "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "@esbuild/linux-arm@0.27.2": {
+ "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
"os": ["linux"],
- "cpu": ["ia32"]
+ "cpu": ["arm"]
},
"@esbuild/linux-ia32@0.25.7": {
"integrity": "sha512-9Jex4uVpdeofiDxnwHRgen+j6398JlX4/6SCbbEFEXN7oMO2p0ueLN+e+9DdsdPLUdqns607HmzEFnxwr7+5wQ==",
"os": ["linux"],
"cpu": ["ia32"]
},
- "@esbuild/linux-loong64@0.25.12": {
- "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "@esbuild/linux-ia32@0.27.2": {
+ "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
"os": ["linux"],
- "cpu": ["loong64"]
+ "cpu": ["ia32"]
},
"@esbuild/linux-loong64@0.25.7": {
"integrity": "sha512-TG1KJqjBlN9IHQjKVUYDB0/mUGgokfhhatlay8aZ/MSORMubEvj/J1CL8YGY4EBcln4z7rKFbsH+HeAv0d471w==",
"os": ["linux"],
"cpu": ["loong64"]
},
- "@esbuild/linux-mips64el@0.25.12": {
- "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "@esbuild/linux-loong64@0.27.2": {
+ "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
"os": ["linux"],
- "cpu": ["mips64el"]
+ "cpu": ["loong64"]
},
"@esbuild/linux-mips64el@0.25.7": {
"integrity": "sha512-Ty9Hj/lx7ikTnhOfaP7ipEm/ICcBv94i/6/WDg0OZ3BPBHhChsUbQancoWYSO0WNkEiSW5Do4febTTy4x1qYQQ==",
"os": ["linux"],
"cpu": ["mips64el"]
},
- "@esbuild/linux-ppc64@0.25.12": {
- "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "@esbuild/linux-mips64el@0.27.2": {
+ "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
"os": ["linux"],
- "cpu": ["ppc64"]
+ "cpu": ["mips64el"]
},
"@esbuild/linux-ppc64@0.25.7": {
"integrity": "sha512-MrOjirGQWGReJl3BNQ58BLhUBPpWABnKrnq8Q/vZWWwAB1wuLXOIxS2JQ1LT3+5T+3jfPh0tyf5CpbyQHqnWIQ==",
"os": ["linux"],
"cpu": ["ppc64"]
},
- "@esbuild/linux-riscv64@0.25.12": {
- "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "@esbuild/linux-ppc64@0.27.2": {
+ "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
"os": ["linux"],
- "cpu": ["riscv64"]
+ "cpu": ["ppc64"]
},
"@esbuild/linux-riscv64@0.25.7": {
"integrity": "sha512-9pr23/pqzyqIZEZmQXnFyqp3vpa+KBk5TotfkzGMqpw089PGm0AIowkUppHB9derQzqniGn3wVXgck19+oqiOw==",
"os": ["linux"],
"cpu": ["riscv64"]
},
- "@esbuild/linux-s390x@0.25.12": {
- "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "@esbuild/linux-riscv64@0.27.2": {
+ "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
"os": ["linux"],
- "cpu": ["s390x"]
+ "cpu": ["riscv64"]
},
"@esbuild/linux-s390x@0.25.7": {
"integrity": "sha512-4dP11UVGh9O6Y47m8YvW8eoA3r8qL2toVZUbBKyGta8j6zdw1cn9F/Rt59/Mhv0OgY68pHIMjGXWOUaykCnx+w==",
"os": ["linux"],
"cpu": ["s390x"]
},
- "@esbuild/linux-x64@0.25.12": {
- "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "@esbuild/linux-s390x@0.27.2": {
+ "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
"os": ["linux"],
- "cpu": ["x64"]
+ "cpu": ["s390x"]
},
"@esbuild/linux-x64@0.25.7": {
"integrity": "sha512-ghJMAJTdw/0uhz7e7YnpdX1xVn7VqA0GrWrAO2qKMuqbvgHT2VZiBv1BQ//VcHsPir4wsL3P2oPggfKPzTKoCA==",
"os": ["linux"],
"cpu": ["x64"]
},
- "@esbuild/netbsd-arm64@0.25.12": {
- "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
- "os": ["netbsd"],
- "cpu": ["arm64"]
+ "@esbuild/linux-x64@0.27.2": {
+ "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
+ "os": ["linux"],
+ "cpu": ["x64"]
},
"@esbuild/netbsd-arm64@0.25.7": {
"integrity": "sha512-bwXGEU4ua45+u5Ci/a55B85KWaDSRS8NPOHtxy2e3etDjbz23wlry37Ffzapz69JAGGc4089TBo+dGzydQmydg==",
"os": ["netbsd"],
"cpu": ["arm64"]
},
- "@esbuild/netbsd-x64@0.25.12": {
- "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "@esbuild/netbsd-arm64@0.27.2": {
+ "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
"os": ["netbsd"],
- "cpu": ["x64"]
+ "cpu": ["arm64"]
},
"@esbuild/netbsd-x64@0.25.7": {
"integrity": "sha512-tUZRvLtgLE5OyN46sPSYlgmHoBS5bx2URSrgZdW1L1teWPYVmXh+QN/sKDqkzBo/IHGcKcHLKDhBeVVkO7teEA==",
"os": ["netbsd"],
"cpu": ["x64"]
},
- "@esbuild/openbsd-arm64@0.25.12": {
- "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
- "os": ["openbsd"],
- "cpu": ["arm64"]
+ "@esbuild/netbsd-x64@0.27.2": {
+ "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
+ "os": ["netbsd"],
+ "cpu": ["x64"]
},
"@esbuild/openbsd-arm64@0.25.7": {
"integrity": "sha512-bTJ50aoC+WDlDGBReWYiObpYvQfMjBNlKztqoNUL0iUkYtwLkBQQeEsTq/I1KyjsKA5tyov6VZaPb8UdD6ci6Q==",
"os": ["openbsd"],
"cpu": ["arm64"]
},
- "@esbuild/openbsd-x64@0.25.12": {
- "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "@esbuild/openbsd-arm64@0.27.2": {
+ "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
"os": ["openbsd"],
- "cpu": ["x64"]
+ "cpu": ["arm64"]
},
"@esbuild/openbsd-x64@0.25.7": {
"integrity": "sha512-TA9XfJrgzAipFUU895jd9j2SyDh9bbNkK2I0gHcvqb/o84UeQkBpi/XmYX3cO1q/9hZokdcDqQxIi6uLVrikxg==",
"os": ["openbsd"],
"cpu": ["x64"]
},
- "@esbuild/openharmony-arm64@0.25.12": {
- "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
- "os": ["openharmony"],
- "cpu": ["arm64"]
+ "@esbuild/openbsd-x64@0.27.2": {
+ "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
+ "os": ["openbsd"],
+ "cpu": ["x64"]
},
"@esbuild/openharmony-arm64@0.25.7": {
"integrity": "sha512-5VTtExUrWwHHEUZ/N+rPlHDwVFQ5aME7vRJES8+iQ0xC/bMYckfJ0l2n3yGIfRoXcK/wq4oXSItZAz5wslTKGw==",
"os": ["openharmony"],
"cpu": ["arm64"]
},
- "@esbuild/sunos-x64@0.25.12": {
- "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
- "os": ["sunos"],
- "cpu": ["x64"]
+ "@esbuild/openharmony-arm64@0.27.2": {
+ "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
+ "os": ["openharmony"],
+ "cpu": ["arm64"]
},
"@esbuild/sunos-x64@0.25.7": {
"integrity": "sha512-umkbn7KTxsexhv2vuuJmj9kggd4AEtL32KodkJgfhNOHMPtQ55RexsaSrMb+0+jp9XL4I4o2y91PZauVN4cH3A==",
"os": ["sunos"],
"cpu": ["x64"]
},
- "@esbuild/win32-arm64@0.25.12": {
- "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
- "os": ["win32"],
- "cpu": ["arm64"]
+ "@esbuild/sunos-x64@0.27.2": {
+ "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
+ "os": ["sunos"],
+ "cpu": ["x64"]
},
"@esbuild/win32-arm64@0.25.7": {
"integrity": "sha512-j20JQGP/gz8QDgzl5No5Gr4F6hurAZvtkFxAKhiv2X49yi/ih8ECK4Y35YnjlMogSKJk931iNMcd35BtZ4ghfw==",
"os": ["win32"],
"cpu": ["arm64"]
},
- "@esbuild/win32-ia32@0.25.12": {
- "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "@esbuild/win32-arm64@0.27.2": {
+ "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
"os": ["win32"],
- "cpu": ["ia32"]
+ "cpu": ["arm64"]
},
"@esbuild/win32-ia32@0.25.7": {
"integrity": "sha512-4qZ6NUfoiiKZfLAXRsvFkA0hoWVM+1y2bSHXHkpdLAs/+r0LgwqYohmfZCi985c6JWHhiXP30mgZawn/XrqAkQ==",
"os": ["win32"],
"cpu": ["ia32"]
},
- "@esbuild/win32-x64@0.25.12": {
- "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "@esbuild/win32-ia32@0.27.2": {
+ "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
"os": ["win32"],
- "cpu": ["x64"]
+ "cpu": ["ia32"]
},
"@esbuild/win32-x64@0.25.7": {
"integrity": "sha512-FaPsAHTwm+1Gfvn37Eg3E5HIpfR3i6x1AIcla/MkqAIupD4BW3MrSeUqfoTzwwJhk3WE2/KqUn4/eenEJC76VA==",
"os": ["win32"],
"cpu": ["x64"]
},
+ "@esbuild/win32-x64@0.27.2": {
+ "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
+ "os": ["win32"],
+ "cpu": ["x64"]
+ },
"@jridgewell/gen-mapping@0.3.13": {
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dependencies": [
@@ -674,8 +676,8 @@
"@preact/signals-core@1.12.1": {
"integrity": "sha512-BwbTXpj+9QutoZLQvbttRg5x3l5468qaV2kufh+51yha1c53ep5dY4kTuZR35+3pAZxpfQerGJiQqg34ZNZ6uA=="
},
- "@preact/signals@2.3.2_preact@10.27.2": {
- "integrity": "sha512-Q22avIn4z0BQnmFeo6Y5HCnJTo8VufN84zN51OtqeNgZOVCYgdwEOcJKVX1x/IrjRVxUnOy6Ubn7H5aVFujXaQ==",
+ "@preact/signals@2.5.1_preact@10.28.2": {
+ "integrity": "sha512-VPjk5YFt7i11Fi4UK0tzaEe5xLwfhUxXL3l89ocxQ5aPz7bRo8M5+N73LjBMPklyXKYKz6YsNo4Smp8n6nplng==",
"dependencies": [
"@preact/signals-core",
"preact"
@@ -684,8 +686,8 @@
"@prefresh/babel-plugin@0.5.2": {
"integrity": "sha512-AOl4HG6dAxWkJ5ndPHBgBa49oo/9bOiJuRDKHLSTyH+Fd9x00shTXpdiTj1W41l6oQIwUOAgJeHMn4QwIDpHkA=="
},
- "@prefresh/core@1.5.8_preact@10.27.2": {
- "integrity": "sha512-T7HMpakS1iPVCFZvfDLMGyrWAcO3toUN9/RkJUqqoRr/vNhQrZgHjidfhq3awDzAQtw1emDWH8dsOeu0DWqtgA==",
+ "@prefresh/core@1.5.9_preact@10.28.2": {
+ "integrity": "sha512-IKBKCPaz34OFVC+adiQ2qaTF5qdztO2/4ZPf4KsRTgjKosWqxVXmEbxCiUydYZRY8GVie+DQlKzQr9gt6HQ+EQ==",
"dependencies": [
"preact"
]
@@ -693,19 +695,7 @@
"@prefresh/utils@1.2.1": {
"integrity": "sha512-vq/sIuN5nYfYzvyayXI4C2QkprfNaHUQ9ZX+3xLD8nL3rWyzpxOm1+K7RtMbhd+66QcaISViK7amjnheQ/4WZw=="
},
- "@prefresh/vite@2.4.11_preact@10.27.2_vite@7.1.12__picomatch@4.0.3": {
- "integrity": "sha512-/XjURQqdRiCG3NpMmWqE9kJwrg9IchIOWHzulCfqg2sRe/8oQ1g5De7xrk9lbqPIQLn7ntBkKdqWXIj4E9YXyg==",
- "dependencies": [
- "@babel/core",
- "@prefresh/babel-plugin",
- "@prefresh/core",
- "@prefresh/utils",
- "@rollup/pluginutils",
- "preact",
- "vite@7.1.12_picomatch@4.0.3"
- ]
- },
- "@prefresh/vite@2.4.11_preact@10.27.2_vite@7.1.12__picomatch@4.0.3_@types+node@24.2.0": {
+ "@prefresh/vite@2.4.11_preact@10.28.2_vite@7.3.1__@types+node@24.2.0__picomatch@4.0.3_@types+node@24.2.0": {
"integrity": "sha512-/XjURQqdRiCG3NpMmWqE9kJwrg9IchIOWHzulCfqg2sRe/8oQ1g5De7xrk9lbqPIQLn7ntBkKdqWXIj4E9YXyg==",
"dependencies": [
"@babel/core",
@@ -714,7 +704,7 @@
"@prefresh/utils",
"@rollup/pluginutils",
"preact",
- "vite@7.1.12_picomatch@4.0.3_@types+node@24.2.0"
+ "vite"
]
},
"@rollup/pluginutils@4.2.1": {
@@ -724,118 +714,133 @@
"picomatch@2.3.1"
]
},
- "@rollup/rollup-android-arm-eabi@4.52.5": {
- "integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==",
+ "@rollup/rollup-android-arm-eabi@4.55.1": {
+ "integrity": "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==",
"os": ["android"],
"cpu": ["arm"]
},
- "@rollup/rollup-android-arm64@4.52.5": {
- "integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==",
+ "@rollup/rollup-android-arm64@4.55.1": {
+ "integrity": "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==",
"os": ["android"],
"cpu": ["arm64"]
},
- "@rollup/rollup-darwin-arm64@4.52.5": {
- "integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==",
+ "@rollup/rollup-darwin-arm64@4.55.1": {
+ "integrity": "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==",
"os": ["darwin"],
"cpu": ["arm64"]
},
- "@rollup/rollup-darwin-x64@4.52.5": {
- "integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==",
+ "@rollup/rollup-darwin-x64@4.55.1": {
+ "integrity": "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==",
"os": ["darwin"],
"cpu": ["x64"]
},
- "@rollup/rollup-freebsd-arm64@4.52.5": {
- "integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==",
+ "@rollup/rollup-freebsd-arm64@4.55.1": {
+ "integrity": "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==",
"os": ["freebsd"],
"cpu": ["arm64"]
},
- "@rollup/rollup-freebsd-x64@4.52.5": {
- "integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==",
+ "@rollup/rollup-freebsd-x64@4.55.1": {
+ "integrity": "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==",
"os": ["freebsd"],
"cpu": ["x64"]
},
- "@rollup/rollup-linux-arm-gnueabihf@4.52.5": {
- "integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==",
+ "@rollup/rollup-linux-arm-gnueabihf@4.55.1": {
+ "integrity": "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==",
"os": ["linux"],
"cpu": ["arm"]
},
- "@rollup/rollup-linux-arm-musleabihf@4.52.5": {
- "integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==",
+ "@rollup/rollup-linux-arm-musleabihf@4.55.1": {
+ "integrity": "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==",
"os": ["linux"],
"cpu": ["arm"]
},
- "@rollup/rollup-linux-arm64-gnu@4.52.5": {
- "integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==",
+ "@rollup/rollup-linux-arm64-gnu@4.55.1": {
+ "integrity": "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==",
"os": ["linux"],
"cpu": ["arm64"]
},
- "@rollup/rollup-linux-arm64-musl@4.52.5": {
- "integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==",
+ "@rollup/rollup-linux-arm64-musl@4.55.1": {
+ "integrity": "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==",
"os": ["linux"],
"cpu": ["arm64"]
},
- "@rollup/rollup-linux-loong64-gnu@4.52.5": {
- "integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==",
+ "@rollup/rollup-linux-loong64-gnu@4.55.1": {
+ "integrity": "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==",
"os": ["linux"],
"cpu": ["loong64"]
},
- "@rollup/rollup-linux-ppc64-gnu@4.52.5": {
- "integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==",
+ "@rollup/rollup-linux-loong64-musl@4.55.1": {
+ "integrity": "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==",
+ "os": ["linux"],
+ "cpu": ["loong64"]
+ },
+ "@rollup/rollup-linux-ppc64-gnu@4.55.1": {
+ "integrity": "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==",
"os": ["linux"],
"cpu": ["ppc64"]
},
- "@rollup/rollup-linux-riscv64-gnu@4.52.5": {
- "integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==",
+ "@rollup/rollup-linux-ppc64-musl@4.55.1": {
+ "integrity": "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==",
+ "os": ["linux"],
+ "cpu": ["ppc64"]
+ },
+ "@rollup/rollup-linux-riscv64-gnu@4.55.1": {
+ "integrity": "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==",
"os": ["linux"],
"cpu": ["riscv64"]
},
- "@rollup/rollup-linux-riscv64-musl@4.52.5": {
- "integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==",
+ "@rollup/rollup-linux-riscv64-musl@4.55.1": {
+ "integrity": "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==",
"os": ["linux"],
"cpu": ["riscv64"]
},
- "@rollup/rollup-linux-s390x-gnu@4.52.5": {
- "integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==",
+ "@rollup/rollup-linux-s390x-gnu@4.55.1": {
+ "integrity": "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==",
"os": ["linux"],
"cpu": ["s390x"]
},
- "@rollup/rollup-linux-x64-gnu@4.52.5": {
- "integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==",
+ "@rollup/rollup-linux-x64-gnu@4.55.1": {
+ "integrity": "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==",
"os": ["linux"],
"cpu": ["x64"]
},
- "@rollup/rollup-linux-x64-musl@4.52.5": {
- "integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==",
+ "@rollup/rollup-linux-x64-musl@4.55.1": {
+ "integrity": "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==",
"os": ["linux"],
"cpu": ["x64"]
},
- "@rollup/rollup-openharmony-arm64@4.52.5": {
- "integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==",
+ "@rollup/rollup-openbsd-x64@4.55.1": {
+ "integrity": "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==",
+ "os": ["openbsd"],
+ "cpu": ["x64"]
+ },
+ "@rollup/rollup-openharmony-arm64@4.55.1": {
+ "integrity": "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==",
"os": ["openharmony"],
"cpu": ["arm64"]
},
- "@rollup/rollup-win32-arm64-msvc@4.52.5": {
- "integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==",
+ "@rollup/rollup-win32-arm64-msvc@4.55.1": {
+ "integrity": "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==",
"os": ["win32"],
"cpu": ["arm64"]
},
- "@rollup/rollup-win32-ia32-msvc@4.52.5": {
- "integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==",
+ "@rollup/rollup-win32-ia32-msvc@4.55.1": {
+ "integrity": "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==",
"os": ["win32"],
"cpu": ["ia32"]
},
- "@rollup/rollup-win32-x64-gnu@4.52.5": {
- "integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==",
+ "@rollup/rollup-win32-x64-gnu@4.55.1": {
+ "integrity": "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==",
"os": ["win32"],
"cpu": ["x64"]
},
- "@rollup/rollup-win32-x64-msvc@4.52.5": {
- "integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==",
+ "@rollup/rollup-win32-x64-msvc@4.55.1": {
+ "integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==",
"os": ["win32"],
"cpu": ["x64"]
},
- "@tailwindcss/node@4.1.16": {
- "integrity": "sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw==",
+ "@tailwindcss/node@4.1.18": {
+ "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==",
"dependencies": [
"@jridgewell/remapping",
"enhanced-resolve",
@@ -846,67 +851,67 @@
"tailwindcss"
]
},
- "@tailwindcss/oxide-android-arm64@4.1.16": {
- "integrity": "sha512-8+ctzkjHgwDJ5caq9IqRSgsP70xhdhJvm+oueS/yhD5ixLhqTw9fSL1OurzMUhBwE5zK26FXLCz2f/RtkISqHA==",
+ "@tailwindcss/oxide-android-arm64@4.1.18": {
+ "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==",
"os": ["android"],
"cpu": ["arm64"]
},
- "@tailwindcss/oxide-darwin-arm64@4.1.16": {
- "integrity": "sha512-C3oZy5042v2FOALBZtY0JTDnGNdS6w7DxL/odvSny17ORUnaRKhyTse8xYi3yKGyfnTUOdavRCdmc8QqJYwFKA==",
+ "@tailwindcss/oxide-darwin-arm64@4.1.18": {
+ "integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==",
"os": ["darwin"],
"cpu": ["arm64"]
},
- "@tailwindcss/oxide-darwin-x64@4.1.16": {
- "integrity": "sha512-vjrl/1Ub9+JwU6BP0emgipGjowzYZMjbWCDqwA2Z4vCa+HBSpP4v6U2ddejcHsolsYxwL5r4bPNoamlV0xDdLg==",
+ "@tailwindcss/oxide-darwin-x64@4.1.18": {
+ "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==",
"os": ["darwin"],
"cpu": ["x64"]
},
- "@tailwindcss/oxide-freebsd-x64@4.1.16": {
- "integrity": "sha512-TSMpPYpQLm+aR1wW5rKuUuEruc/oOX3C7H0BTnPDn7W/eMw8W+MRMpiypKMkXZfwH8wqPIRKppuZoedTtNj2tg==",
+ "@tailwindcss/oxide-freebsd-x64@4.1.18": {
+ "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==",
"os": ["freebsd"],
"cpu": ["x64"]
},
- "@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16": {
- "integrity": "sha512-p0GGfRg/w0sdsFKBjMYvvKIiKy/LNWLWgV/plR4lUgrsxFAoQBFrXkZ4C0w8IOXfslB9vHK/JGASWD2IefIpvw==",
+ "@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18": {
+ "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==",
"os": ["linux"],
"cpu": ["arm"]
},
- "@tailwindcss/oxide-linux-arm64-gnu@4.1.16": {
- "integrity": "sha512-DoixyMmTNO19rwRPdqviTrG1rYzpxgyYJl8RgQvdAQUzxC1ToLRqtNJpU/ATURSKgIg6uerPw2feW0aS8SNr/w==",
+ "@tailwindcss/oxide-linux-arm64-gnu@4.1.18": {
+ "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==",
"os": ["linux"],
"cpu": ["arm64"]
},
- "@tailwindcss/oxide-linux-arm64-musl@4.1.16": {
- "integrity": "sha512-H81UXMa9hJhWhaAUca6bU2wm5RRFpuHImrwXBUvPbYb+3jo32I9VIwpOX6hms0fPmA6f2pGVlybO6qU8pF4fzQ==",
+ "@tailwindcss/oxide-linux-arm64-musl@4.1.18": {
+ "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==",
"os": ["linux"],
"cpu": ["arm64"]
},
- "@tailwindcss/oxide-linux-x64-gnu@4.1.16": {
- "integrity": "sha512-ZGHQxDtFC2/ruo7t99Qo2TTIvOERULPl5l0K1g0oK6b5PGqjYMga+FcY1wIUnrUxY56h28FxybtDEla+ICOyew==",
+ "@tailwindcss/oxide-linux-x64-gnu@4.1.18": {
+ "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==",
"os": ["linux"],
"cpu": ["x64"]
},
- "@tailwindcss/oxide-linux-x64-musl@4.1.16": {
- "integrity": "sha512-Oi1tAaa0rcKf1Og9MzKeINZzMLPbhxvm7rno5/zuP1WYmpiG0bEHq4AcRUiG2165/WUzvxkW4XDYCscZWbTLZw==",
+ "@tailwindcss/oxide-linux-x64-musl@4.1.18": {
+ "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==",
"os": ["linux"],
"cpu": ["x64"]
},
- "@tailwindcss/oxide-wasm32-wasi@4.1.16": {
- "integrity": "sha512-B01u/b8LteGRwucIBmCQ07FVXLzImWESAIMcUU6nvFt/tYsQ6IHz8DmZ5KtvmwxD+iTYBtM1xwoGXswnlu9v0Q==",
+ "@tailwindcss/oxide-wasm32-wasi@4.1.18": {
+ "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==",
"cpu": ["wasm32"]
},
- "@tailwindcss/oxide-win32-arm64-msvc@4.1.16": {
- "integrity": "sha512-zX+Q8sSkGj6HKRTMJXuPvOcP8XfYON24zJBRPlszcH1Np7xuHXhWn8qfFjIujVzvH3BHU+16jBXwgpl20i+v9A==",
+ "@tailwindcss/oxide-win32-arm64-msvc@4.1.18": {
+ "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==",
"os": ["win32"],
"cpu": ["arm64"]
},
- "@tailwindcss/oxide-win32-x64-msvc@4.1.16": {
- "integrity": "sha512-m5dDFJUEejbFqP+UXVstd4W/wnxA4F61q8SoL+mqTypId2T2ZpuxosNSgowiCnLp2+Z+rivdU0AqpfgiD7yCBg==",
+ "@tailwindcss/oxide-win32-x64-msvc@4.1.18": {
+ "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==",
"os": ["win32"],
"cpu": ["x64"]
},
- "@tailwindcss/oxide@4.1.16": {
- "integrity": "sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg==",
+ "@tailwindcss/oxide@4.1.18": {
+ "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==",
"optionalDependencies": [
"@tailwindcss/oxide-android-arm64",
"@tailwindcss/oxide-darwin-arm64",
@@ -922,22 +927,13 @@
"@tailwindcss/oxide-win32-x64-msvc"
]
},
- "@tailwindcss/vite@4.1.16_vite@7.1.12__picomatch@4.0.3": {
- "integrity": "sha512-bbguNBcDxsRmi9nnlWJxhfDWamY3lmcyACHcdO1crxfzuLpOhHLLtEIN/nCbbAtj5rchUgQD17QVAKi1f7IsKg==",
- "dependencies": [
- "@tailwindcss/node",
- "@tailwindcss/oxide",
- "tailwindcss",
- "vite@7.1.12_picomatch@4.0.3"
- ]
- },
- "@tailwindcss/vite@4.1.16_vite@7.1.12__picomatch@4.0.3_@types+node@24.2.0": {
- "integrity": "sha512-bbguNBcDxsRmi9nnlWJxhfDWamY3lmcyACHcdO1crxfzuLpOhHLLtEIN/nCbbAtj5rchUgQD17QVAKi1f7IsKg==",
+ "@tailwindcss/vite@4.1.18_vite@7.3.1__@types+node@24.2.0__picomatch@4.0.3_@types+node@24.2.0": {
+ "integrity": "sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==",
"dependencies": [
"@tailwindcss/node",
"@tailwindcss/oxide",
"tailwindcss",
- "vite@7.1.12_picomatch@4.0.3_@types+node@24.2.0"
+ "vite"
]
},
"@types/estree@1.0.8": {
@@ -949,12 +945,12 @@
"undici-types"
]
},
- "baseline-browser-mapping@2.8.24": {
- "integrity": "sha512-uUhTRDPXamakPyghwrUcjaGvvBqGrWvBHReoiULMIpOJVM9IYzQh83Xk2Onx5HlGI2o10NNCzcs9TG/S3TkwrQ==",
+ "baseline-browser-mapping@2.9.14": {
+ "integrity": "sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg==",
"bin": true
},
- "browserslist@4.27.0": {
- "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==",
+ "browserslist@4.28.1": {
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
"dependencies": [
"baseline-browser-mapping",
"caniuse-lite",
@@ -964,8 +960,8 @@
],
"bin": true
},
- "caniuse-lite@1.0.30001753": {
- "integrity": "sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw=="
+ "caniuse-lite@1.0.30001763": {
+ "integrity": "sha512-mh/dGtq56uN98LlNX9qdbKnzINhX0QzhiWBFEkFfsFO4QyCvL8YegrJAazCwXIeqkIob8BlZPGM3xdnY+sgmvQ=="
},
"commander@8.3.0": {
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="
@@ -1010,11 +1006,11 @@
"domhandler"
]
},
- "electron-to-chromium@1.5.244": {
- "integrity": "sha512-OszpBN7xZX4vWMPJwB9illkN/znA8M36GQqQxi6MNy9axWxhOfJyZZJtSLQCpEFLHP2xK33BiWx9aIuIEXVCcw=="
+ "electron-to-chromium@1.5.267": {
+ "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw=="
},
- "enhanced-resolve@5.18.3": {
- "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
+ "enhanced-resolve@5.18.4": {
+ "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==",
"dependencies": [
"graceful-fs",
"tapable"
@@ -1027,39 +1023,6 @@
"integrity": "sha512-rZqkjL3Y6FwLpSHzLnaEy8Ps6veCNo1kZa9EOfJvmWtBq5dJH4iVjfmOO6Mlkv9B0tt9WFPFmb/VxlgJOnueNg==",
"bin": true
},
- "esbuild@0.25.12": {
- "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
- "optionalDependencies": [
- "@esbuild/aix-ppc64@0.25.12",
- "@esbuild/android-arm@0.25.12",
- "@esbuild/android-arm64@0.25.12",
- "@esbuild/android-x64@0.25.12",
- "@esbuild/darwin-arm64@0.25.12",
- "@esbuild/darwin-x64@0.25.12",
- "@esbuild/freebsd-arm64@0.25.12",
- "@esbuild/freebsd-x64@0.25.12",
- "@esbuild/linux-arm@0.25.12",
- "@esbuild/linux-arm64@0.25.12",
- "@esbuild/linux-ia32@0.25.12",
- "@esbuild/linux-loong64@0.25.12",
- "@esbuild/linux-mips64el@0.25.12",
- "@esbuild/linux-ppc64@0.25.12",
- "@esbuild/linux-riscv64@0.25.12",
- "@esbuild/linux-s390x@0.25.12",
- "@esbuild/linux-x64@0.25.12",
- "@esbuild/netbsd-arm64@0.25.12",
- "@esbuild/netbsd-x64@0.25.12",
- "@esbuild/openbsd-arm64@0.25.12",
- "@esbuild/openbsd-x64@0.25.12",
- "@esbuild/openharmony-arm64@0.25.12",
- "@esbuild/sunos-x64@0.25.12",
- "@esbuild/win32-arm64@0.25.12",
- "@esbuild/win32-ia32@0.25.12",
- "@esbuild/win32-x64@0.25.12"
- ],
- "scripts": true,
- "bin": true
- },
"esbuild@0.25.7": {
"integrity": "sha512-daJB0q2dmTzo90L9NjRaohhRWrCzYxWNFTjEi72/h+p5DcY3yn4MacWfDakHmaBaDzDiuLJsCh0+6LK/iX+c+Q==",
"optionalDependencies": [
@@ -1093,6 +1056,39 @@
"scripts": true,
"bin": true
},
+ "esbuild@0.27.2": {
+ "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
+ "optionalDependencies": [
+ "@esbuild/aix-ppc64@0.27.2",
+ "@esbuild/android-arm@0.27.2",
+ "@esbuild/android-arm64@0.27.2",
+ "@esbuild/android-x64@0.27.2",
+ "@esbuild/darwin-arm64@0.27.2",
+ "@esbuild/darwin-x64@0.27.2",
+ "@esbuild/freebsd-arm64@0.27.2",
+ "@esbuild/freebsd-x64@0.27.2",
+ "@esbuild/linux-arm@0.27.2",
+ "@esbuild/linux-arm64@0.27.2",
+ "@esbuild/linux-ia32@0.27.2",
+ "@esbuild/linux-loong64@0.27.2",
+ "@esbuild/linux-mips64el@0.27.2",
+ "@esbuild/linux-ppc64@0.27.2",
+ "@esbuild/linux-riscv64@0.27.2",
+ "@esbuild/linux-s390x@0.27.2",
+ "@esbuild/linux-x64@0.27.2",
+ "@esbuild/netbsd-arm64@0.27.2",
+ "@esbuild/netbsd-x64@0.27.2",
+ "@esbuild/openbsd-arm64@0.27.2",
+ "@esbuild/openbsd-x64@0.27.2",
+ "@esbuild/openharmony-arm64@0.27.2",
+ "@esbuild/sunos-x64@0.27.2",
+ "@esbuild/win32-arm64@0.27.2",
+ "@esbuild/win32-ia32@0.27.2",
+ "@esbuild/win32-x64@0.27.2"
+ ],
+ "scripts": true,
+ "bin": true
+ },
"escalade@3.2.0": {
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="
},
@@ -1302,20 +1298,20 @@
"source-map-js"
]
},
- "preact-render-to-string@6.6.3_preact@10.27.2": {
- "integrity": "sha512-7oHG7jzjriqsFPkSPiPnzrQ0GcxFm6wOkYWNdStK5Ks9YlWSQQXKGBRAX4nKDdqX7HAQuRvI4pZNZMycK4WwDw==",
+ "preact-render-to-string@6.6.5_preact@10.28.2": {
+ "integrity": "sha512-O6MHzYNIKYaiSX3bOw0gGZfEbOmlIDtDfWwN1JJdc/T3ihzRT6tGGSEWE088dWrEDGa1u7101q+6fzQnO9XCPA==",
"dependencies": [
"preact"
]
},
- "preact@10.27.2": {
- "integrity": "sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg=="
+ "preact@10.28.2": {
+ "integrity": "sha512-lbteaWGzGHdlIuiJ0l2Jq454m6kcpI1zNje6d8MlGAFlYvP2GO4ibnat7P74Esfz4sPTdM6UxtTwh/d3pwM9JA=="
},
"prismjs@1.30.0": {
"integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="
},
- "rollup@4.52.5": {
- "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==",
+ "rollup@4.55.1": {
+ "integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==",
"dependencies": [
"@types/estree"
],
@@ -1331,12 +1327,15 @@
"@rollup/rollup-linux-arm64-gnu",
"@rollup/rollup-linux-arm64-musl",
"@rollup/rollup-linux-loong64-gnu",
+ "@rollup/rollup-linux-loong64-musl",
"@rollup/rollup-linux-ppc64-gnu",
+ "@rollup/rollup-linux-ppc64-musl",
"@rollup/rollup-linux-riscv64-gnu",
"@rollup/rollup-linux-riscv64-musl",
"@rollup/rollup-linux-s390x-gnu",
"@rollup/rollup-linux-x64-gnu",
"@rollup/rollup-linux-x64-musl",
+ "@rollup/rollup-openbsd-x64",
"@rollup/rollup-openharmony-arm64",
"@rollup/rollup-win32-arm64-msvc",
"@rollup/rollup-win32-ia32-msvc",
@@ -1364,8 +1363,8 @@
"source-map-js@1.2.1": {
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
},
- "tailwindcss@4.1.16": {
- "integrity": "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA=="
+ "tailwindcss@4.1.18": {
+ "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="
},
"tapable@2.3.0": {
"integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="
@@ -1380,8 +1379,8 @@
"undici-types@7.10.0": {
"integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="
},
- "update-browserslist-db@1.1.4_browserslist@4.27.0": {
- "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==",
+ "update-browserslist-db@1.2.3_browserslist@4.28.1": {
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
"dependencies": [
"browserslist",
"escalade",
@@ -1389,26 +1388,11 @@
],
"bin": true
},
- "vite@7.1.12_picomatch@4.0.3": {
- "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==",
- "dependencies": [
- "esbuild@0.25.12",
- "fdir",
- "picomatch@4.0.3",
- "postcss",
- "rollup",
- "tinyglobby"
- ],
- "optionalDependencies": [
- "fsevents"
- ],
- "bin": true
- },
- "vite@7.1.12_picomatch@4.0.3_@types+node@24.2.0": {
- "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==",
+ "vite@7.3.1_@types+node@24.2.0_picomatch@4.0.3": {
+ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"dependencies": [
"@types/node",
- "esbuild@0.25.12",
+ "esbuild@0.27.2",
"fdir",
"picomatch@4.0.3",
"postcss",
@@ -1466,19 +1450,20 @@
"https://deno.land/x/bcrypt@v0.4.1/mod.ts": "ff09bdae282583cf5f7d87efe37ddcecef7f14f6d12e8b8066a3058db8c6c2f7",
"https://deno.land/x/bcrypt@v0.4.1/src/bcrypt/base64.ts": "b8266450a4f1eb6960f60f2f7986afc4dde6b45bd2d7ee7ba10789e67e17b9f7",
"https://deno.land/x/bcrypt@v0.4.1/src/bcrypt/bcrypt.ts": "ec221648cc6453ea5e3803bc817c01157dada06aa6f7a0ba6b9f87aae32b21e2",
- "https://deno.land/x/bcrypt@v0.4.1/src/main.ts": "08d201b289c8d9c46f8839c69cd6625b213863db29775c7a200afc3b540e64f8"
+ "https://deno.land/x/bcrypt@v0.4.1/src/main.ts": "08d201b289c8d9c46f8839c69cd6625b213863db29775c7a200afc3b540e64f8",
+ "https://deno.land/x/bcrypt@v0.4.1/src/worker.ts": "5a73bdfee9c9e622f47c9733d374b627dce52fb3ec1e74c8226698b3fc57ffac"
},
"workspace": {
"dependencies": [
"jsr:@deno/gfm@0.11",
- "jsr:@fresh/core@^2.1.4",
- "jsr:@fresh/plugin-vite@^1.0.7",
- "jsr:@valibot/valibot@^1.1.0",
- "npm:@preact/signals@^2.3.2",
- "npm:@tailwindcss/vite@^4.1.16",
- "npm:preact@^10.27.2",
- "npm:tailwindcss@^4.1.16",
- "npm:vite@^7.1.12"
+ "jsr:@fresh/core@^2.2.0",
+ "jsr:@fresh/plugin-vite@^1.0.8",
+ "jsr:@valibot/valibot@^1.2.0",
+ "npm:@preact/signals@^2.5.1",
+ "npm:@tailwindcss/vite@^4.1.18",
+ "npm:preact@^10.28.2",
+ "npm:tailwindcss@^4.1.18",
+ "npm:vite@^7.3.1"
]
}
}
diff --git a/islands/ViewNote.tsx b/islands/ViewNote.tsx
index f07664d..2dd702b 100644
--- a/islands/ViewNote.tsx
+++ b/islands/ViewNote.tsx
@@ -14,6 +14,7 @@ import { ViewNoteSchema, viewNoteSchema } from '../lib/validation/note.ts';
import * as v from '@valibot/valibot';
import ErrorPage from '../components/ErrorPage.tsx';
import PasswordToggle from '../components/PasswordToggle.tsx';
+import CopyButton from '../components/CopyButton.tsx';
// Constants for messages
const MESSAGES = {
@@ -269,13 +270,7 @@ function DisplayDecryptedNote(
{/* Content section */}
diff --git a/lib/database/note-database.ts b/lib/database/note-database.ts
index 2ef7dd4..3b861d5 100644
--- a/lib/database/note-database.ts
+++ b/lib/database/note-database.ts
@@ -116,23 +116,25 @@ export class NoteDatabase {
async generateNoteId(): Promise {
if (!this.kv) throw new Error('Database not initialized');
- let id = '';
- let note = null;
- let attempts = 0;
- let length = 10; // Start with a length of 10 characters
-
- do {
- attempts++;
- if (attempts > 10) {
- length++;
- }
- id = Math.random().toString(36).substring(2, length + 2);
+ const MAX_ATTEMPTS = 5;
+ const BASE_LENGTH = 12; // Start with longer IDs to reduce collisions
+
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ const length = BASE_LENGTH + Math.floor(attempt / 2); // Increase length after failed attempts
+ const id = crypto.randomUUID().replace(/-/g, '').substring(0, length);
const res = await this.kv.get(['note', id]);
- note = res.value;
- } while (note);
+ if (!res.value) {
+ return id;
+ }
+ }
- return id;
+ // Fallback to cryptographically secure random ID if all attempts fail
+ const fallbackId = (
+ crypto.randomUUID().replace(/-/g, '') +
+ crypto.randomUUID().replace(/-/g, '')
+ ).substring(0, 20);
+ return fallbackId;
}
async clearAllNotes(): Promise {
diff --git a/lib/rate-limiting/src/hashing.ts b/lib/rate-limiting/src/hashing.ts
index 2936fa5..0b0e8e8 100644
--- a/lib/rate-limiting/src/hashing.ts
+++ b/lib/rate-limiting/src/hashing.ts
@@ -7,7 +7,10 @@
* const hash = await generateHMACSHA256('myInput', Deno.env.get('ARC_SECRET')!);
* console.log(hash); // Outputs the HMAC SHA-256 hash in base64 format
*/
-export async function generateHMACSHA256(input: string, secret: string): Promise {
+export async function generateHMACSHA256(
+ input: string,
+ secret: string,
+): Promise {
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
'raw',
@@ -16,6 +19,10 @@ export async function generateHMACSHA256(input: string, secret: string): Promise
false,
['sign'],
);
- const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(input));
+ const signature = await crypto.subtle.sign(
+ 'HMAC',
+ key,
+ encoder.encode(input),
+ );
return btoa(String.fromCharCode(...new Uint8Array(signature)));
}
diff --git a/lib/services/database-service.ts b/lib/services/database-service.ts
deleted file mode 100644
index 86eb821..0000000
--- a/lib/services/database-service.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { NoteDatabase } from '../database/note-database.ts';
-import { defaultLogger } from '../logging.ts';
-
-const databasePath = Deno.env.get('DATABASE_PATH');
-
-let _noteDatabase: NoteDatabase | null = null;
-
-export async function getNoteDatabase() {
- if (!_noteDatabase) {
- defaultLogger.log(`Database Path Source: ${databasePath ? 'env' : 'default'}`);
- _noteDatabase = await new NoteDatabase(databasePath).init();
- }
- return _noteDatabase;
-}
diff --git a/main.ts b/main.ts
index f31c835..1858bfd 100644
--- a/main.ts
+++ b/main.ts
@@ -2,13 +2,25 @@ import { App, cors, csp, csrf, staticFiles } from 'fresh';
import { headers } from './middleware.ts';
import { ArcRateLimiter } from './lib/rate-limiting/src/arc-rate-limiter.ts';
import { ORIGIN, State } from './lib/types/common.ts';
+import { NoteDatabase } from './lib/database/note-database.ts';
+import { defaultLogger } from './lib/logging.ts';
const serverSecret = Deno.env.get('ARC_SECRET');
+const databasePath = Deno.env.get('DATABASE_PATH');
+let noteDatabase: NoteDatabase;
if (!serverSecret) {
throw new Error('ARC_SECRET environment variable is not set');
}
+try {
+ noteDatabase = await new NoteDatabase(databasePath).init();
+ defaultLogger.log(`Database Path Source: ${databasePath ? 'env' : 'default'}`);
+} catch (error) {
+ defaultLogger.error('Failed to initialize NoteDatabase', error);
+ throw error;
+}
+
// Configure rate limiter: 15 requests per minute, 5 min block duration
const rateLimiter = new ArcRateLimiter({
maxRequests: 15,
@@ -18,6 +30,8 @@ const rateLimiter = new ArcRateLimiter({
serverSecret,
});
+export { noteDatabase };
+
export const app = new App()
.use(staticFiles())
.use(cors({
diff --git a/routes/[id].tsx b/routes/[id].tsx
index 7213caf..80d1e40 100644
--- a/routes/[id].tsx
+++ b/routes/[id].tsx
@@ -3,8 +3,8 @@ import { Note } from '../lib/types.ts';
import Header from '../components/Header.tsx';
import ViewEncryptedNote from '../islands/ViewNote.tsx';
import { HttpError } from 'fresh';
-import { getNoteDatabase } from '../lib/services/database-service.ts';
import { State } from '../lib/types/common.ts';
+import { noteDatabase } from '../main.ts';
interface NotePageProps {
note: Note;
@@ -18,8 +18,7 @@ export const handler = {
throw new HttpError(404);
}
- const db = await getNoteDatabase();
- const note = await db.getNoteById(id);
+ const note = await noteDatabase.getNoteById(id);
if (!note) {
throw new HttpError(404);
@@ -29,24 +28,6 @@ export const handler = {
// The client will handle password input and decryption entirely
return { data: { note, message: 'Note found - the client will handle decryption' } };
},
-
- async POST(ctx: Context) {
- // For backward compatibility, POST requests should also render the client-side component
- // All password validation and decryption now happens client-side
- const { id } = ctx.params;
- if (!id) {
- throw new HttpError(404);
- }
- const db = await getNoteDatabase();
- const note = await db.getNoteById(id);
-
- if (!note) {
- throw new HttpError(404);
- }
-
- // Always render the client-side component - password validation is now client-side
- return { data: { note, message: 'Note found - the client will handle decryption' } };
- },
};
export default function NotePage({ data }: { data: NotePageProps }) {
diff --git a/routes/api/notes.ts b/routes/api/notes.ts
index f403c1e..d731136 100644
--- a/routes/api/notes.ts
+++ b/routes/api/notes.ts
@@ -2,28 +2,26 @@ import { createNoteSchema } from '../../lib/validation/note.ts';
import { formatExpiration, Note } from '../../lib/types.ts';
import * as v from '@valibot/valibot';
import { Context } from 'fresh';
-import { getNoteDatabase } from '../../lib/services/database-service.ts';
import * as bcrypt from 'bcrypt';
import { State } from '../../lib/types/common.ts';
+import { noteDatabase } from '../../main.ts';
/* used for client side note creation and encryption
- * This endpoint handles only POST requests.
- * - POST: Creates a new note with the provided content, IV, password, and expiration time.
- *
- * Note: The content should be encrypted before sending to this endpoint and the password should be hashed with PBKDF2
- * on the client side for security, then securely hashed with bcrypt on the server for storage.
- *
- * rate-limiting (ARC - Anonymous Rate-Limited Credentials):
- * - Limit: 10 requests per minute per client
- * - Block duration: 5 minutes for rate limit violations
- * - Privacy-preserving: Uses anonymous tokens with daily rotation
- * - No IP address storage: Only hashed, rotated tokens are kept
- */
+ * This endpoint handles only POST requests.
+ * - POST: Creates a new note with the provided content, IV, password, and expiration time.
+ *
+ * Note: The content should be encrypted before sending to this endpoint and the password should be hashed with PBKDF2
+ * on the client side for security, then securely hashed with bcrypt on the server for storage.
+ *
+ * rate-limiting (ARC - Anonymous Rate-Limited Credentials):
+ * - Limit: 10 requests per minute per client
+ * - Block duration: 5 minutes for rate limit violations
+ * - Privacy-preserving: Uses anonymous tokens with daily rotation
+ * - No IP address storage: Only hashed, rotated tokens are kept
+ */
export const handler = {
async POST(ctx: Context) {
- const db = await getNoteDatabase();
-
try {
const { content, iv, password, expiresIn, manualDeletion } = await ctx.req.json();
@@ -42,11 +40,11 @@ export const handler = {
);
}
- const noteId = await db.generateNoteId();
+ const noteId = await noteDatabase.generateNoteId();
const hasPassword = password && password.trim() !== '';
// if password is provided, hash it with bcrypt (password should be PBKDF2 hashed on client before sending)
- const passwordHash = hasPassword ? generateHash(password) : undefined;
+ const passwordHash = hasPassword ? await generateHash(password) : undefined;
// check if content is encrypted
const result: Note = {
@@ -58,7 +56,7 @@ export const handler = {
manualDeletion: manualDeletion,
};
- const insertResult = await db.insertNote(result);
+ const insertResult = await noteDatabase.insertNote(result);
if (!insertResult.success) {
return new Response(
@@ -96,7 +94,7 @@ export const handler = {
},
};
-function generateHash(password: string): string {
- const salt = bcrypt.genSaltSync(12);
- return bcrypt.hashSync(password, salt);
+async function generateHash(password: string): Promise {
+ const salt = await bcrypt.genSalt(12);
+ return await bcrypt.hash(password, salt);
}
diff --git a/routes/api/notes/[id].ts b/routes/api/notes/[id].ts
index a9cd59c..4af8ebe 100644
--- a/routes/api/notes/[id].ts
+++ b/routes/api/notes/[id].ts
@@ -1,76 +1,73 @@
import { Note } from '../../../lib/types.ts';
import { Context } from 'fresh';
-import { getNoteDatabase } from '../../../lib/services/database-service.ts';
import * as bcrypt from 'bcrypt';
import { State } from '../../../lib/types/common.ts';
+import { noteDatabase } from '../../../main.ts';
+
+async function validateNoteAccess(id: string, passwordHash?: string): Promise<{ note: Note | null; error?: Response }> {
+ const note = await noteDatabase.getNoteById(id);
+
+ if (!note) {
+ return {
+ note: null,
+ error: new Response('Note not found', { status: 404 }),
+ };
+ }
+
+ if (note.password && passwordHash && !(await compareHash(passwordHash, note.password))) {
+ return {
+ note: null,
+ error: new Response('Invalid password or auth key', { status: 403 }),
+ };
+ }
+
+ return { note };
+}
export const handler = async (ctx: Context): Promise => {
if (ctx.req.method !== 'POST' && ctx.req.method !== 'DELETE') {
return new Response('Method not allowed', { status: 405 });
}
- const db = await getNoteDatabase();
const id = ctx.params.id;
if (!id) {
return new Response('Note ID is required', { status: 400 });
}
- if (ctx.req.method === 'POST') {
- const note = await db.getNoteById(id);
- const { passwordHash } = await ctx.req.json();
-
- if (!note || !passwordHash) {
- return new Response('Note not found or password hash missing', { status: 404 });
- }
+ const { passwordHash } = await ctx.req.json();
+ const { note, error } = await validateNoteAccess(id, passwordHash);
- if (note.password && !compareHash(passwordHash, note.password)) {
- return new Response('Invalid password or auth key', { status: 403 });
- }
+ if (error) return error;
+ if (!note) return new Response('Note not found', { status: 404 });
- // If the note doesn't require manual deletion, delete it to ensure it has been destroyed
+ if (ctx.req.method === 'POST') {
+ // Auto-delete non-manual notes after viewing
if (!note.manualDeletion) {
- await db.deleteNote(id);
+ await noteDatabase.deleteNote(id);
}
return new Response(
- JSON.stringify({
- id: note.id,
- content: note.content,
- iv: note.iv,
- expiresIn: note.expiresIn,
- manualDeletion: note.manualDeletion,
- } as Note),
+ JSON.stringify(note),
{
status: 200,
+ headers: { 'Content-Type': 'application/json' },
},
);
- } else if (ctx.req.method === 'DELETE') {
- const note = await db.getNoteById(id);
- const { passwordHash } = await ctx.req.json();
- if (!note) {
- return new Response('Note not found', { status: 404 });
- }
-
- if (note.password && !compareHash(passwordHash, note.password)) {
- return new Response('Invalid password or auth key', { status: 403 });
- }
- await db.deleteNote(id);
+ } else { // DELETE
+ await noteDatabase.deleteNote(id);
return new Response(
- JSON.stringify({
- message: 'Note deleted successfully',
- }),
+ JSON.stringify({ message: 'Note deleted successfully' }),
{
status: 200,
+ headers: { 'Content-Type': 'application/json' },
},
);
- } else {
- return new Response('Method not allowed', { status: 405 });
}
};
-function compareHash(plainText: string, hash: string): boolean {
+async function compareHash(plainText: string, hash: string): Promise {
try {
- return bcrypt.compareSync(plainText, hash);
+ return await bcrypt.compare(plainText, hash);
} catch (error) {
console.error('Error comparing hash:', error);
return false;
diff --git a/tests/arc-rate-limiter_test.ts b/tests/arc-rate-limiter_test.ts
index 2c31f38..c69badd 100644
--- a/tests/arc-rate-limiter_test.ts
+++ b/tests/arc-rate-limiter_test.ts
@@ -18,6 +18,7 @@ const defaultRateLimitOptions = {
enablePeriodicCleanup: false,
};
+Deno.env.set('ARC_SECRET', defaultRateLimitOptions.serverSecret);
Deno.test('ARC Rate Limiter - basic functionality', async () => {
const rateLimiter = new ArcRateLimiter(defaultRateLimitOptions);
const app = new App()
diff --git a/tests/main_test.ts b/tests/main_test.ts
index 35881b1..f53c17a 100644
--- a/tests/main_test.ts
+++ b/tests/main_test.ts
@@ -21,6 +21,9 @@ export class TestDataFactory {
};
}
+const testArcSecret = crypto.randomUUID();
+Deno.env.set('ARC_SECRET', testArcSecret);
+
// Test suite for basic HTTP functionality
Deno.test({
name: 'HTTP - Basic functionality',