aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package-lock.json814
-rw-r--r--package.json1
-rw-r--r--src/lib/components/HealthChart.svelte110
-rw-r--r--src/lib/server/services/routines.test.ts3
-rw-r--r--src/lib/server/services/routines.ts12
-rw-r--r--src/lib/shared/routine-metrics.test.ts49
-rw-r--r--src/lib/shared/routine-metrics.ts72
-rw-r--r--src/routes/projects/[id]/+page.server.ts1
-rw-r--r--src/routes/projects/[id]/+page.svelte28
-rw-r--r--src/routes/projects/[projectId]/routine/[routineId]/+page.svelte87
10 files changed, 960 insertions, 217 deletions
diff --git a/package-lock.json b/package-lock.json
index 1cd516b..350ecba 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
"@libsql/client": "^0.15.0",
"drizzle-orm": "^0.44.0",
"fuzzysort": "^4.0.2",
+ "layerchart": "^2.4.0",
"valibot": "^1.4.2"
},
"devDependencies": {
@@ -1629,6 +1630,21 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@dagrejs/dagre": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@dagrejs/dagre/-/dagre-2.0.4.tgz",
+ "integrity": "sha512-J6vCWTNpicHF4zFlZG1cS5DkGzMr9941gddYkakjrg3ZNev4bbqEgLHFTWiFrcJm7UCRu7olO3K6IRDd9gSGhA==",
+ "license": "MIT",
+ "dependencies": {
+ "@dagrejs/graphlib": "3.0.4"
+ }
+ },
+ "node_modules/@dagrejs/graphlib": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@dagrejs/graphlib/-/graphlib-3.0.4.tgz",
+ "integrity": "sha512-HxZ7fCvAwTLCWCO0WjDkzAFQze8LdC6iOpKbetDKHIuDfIgMlIzYzqZ4nxwLlclQX+3ZVeZ1K2OuaOE2WWcyOg==",
+ "license": "MIT"
+ },
"node_modules/@drizzle-team/brocli": {
"version": "0.10.2",
"resolved": "https://registry.npmjs.org/@drizzle-team/brocli/-/brocli-0.10.2.tgz",
@@ -2079,7 +2095,6 @@
"cpu": [
"ppc64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2096,7 +2111,6 @@
"cpu": [
"arm"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2113,7 +2127,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2130,7 +2143,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2147,7 +2159,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2164,7 +2175,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2181,7 +2191,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2198,7 +2207,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2215,7 +2223,6 @@
"cpu": [
"arm"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2232,7 +2239,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2249,7 +2255,6 @@
"cpu": [
"ia32"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2266,7 +2271,6 @@
"cpu": [
"loong64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2283,7 +2287,6 @@
"cpu": [
"mips64el"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2300,7 +2303,6 @@
"cpu": [
"ppc64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2317,7 +2319,6 @@
"cpu": [
"riscv64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2334,7 +2335,6 @@
"cpu": [
"s390x"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2351,7 +2351,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2368,7 +2367,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2385,7 +2383,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2402,7 +2399,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2419,7 +2415,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2436,7 +2431,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2453,7 +2447,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2470,7 +2463,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2487,7 +2479,6 @@
"cpu": [
"ia32"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2504,7 +2495,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2658,6 +2648,31 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
+ "node_modules/@floating-ui/core": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz",
+ "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.12"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz",
+ "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/core": "^1.8.0",
+ "@floating-ui/utils": "^0.2.12"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.12",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz",
+ "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==",
+ "license": "MIT"
+ },
"node_modules/@humanfs/core": {
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
@@ -2738,7 +2753,6 @@
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0",
@@ -2749,7 +2763,6 @@
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
@@ -2760,7 +2773,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
@@ -2770,7 +2782,7 @@
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
"integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
@@ -2781,20 +2793,83 @@
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
- "dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.31",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@layerstack/svelte-actions": {
+ "version": "1.0.1-next.18",
+ "resolved": "https://registry.npmjs.org/@layerstack/svelte-actions/-/svelte-actions-1.0.1-next.18.tgz",
+ "integrity": "sha512-gxPzCnJ1c9LTfWtRqLUzefCx+k59ZpxDUQ2XB+LokveZQPe7IDSOwHaBOEMlaGoGrtwc3Ft8dSZq+2WT2o9u/g==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/dom": "^1.7.0",
+ "@layerstack/utils": "2.0.0-next.18",
+ "d3-scale": "^4.0.2"
+ }
+ },
+ "node_modules/@layerstack/svelte-actions/node_modules/@layerstack/utils": {
+ "version": "2.0.0-next.18",
+ "resolved": "https://registry.npmjs.org/@layerstack/utils/-/utils-2.0.0-next.18.tgz",
+ "integrity": "sha512-EYILHpfBRYMMEahajInu9C2AXQom5IcAEdtCeucD3QIl/fdDgRbtzn6/8QW9ewumfyNZetdUvitOksmI1+gZYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "d3-array": "^3.2.4",
+ "d3-time": "^3.1.0",
+ "d3-time-format": "^4.1.0"
+ }
+ },
+ "node_modules/@layerstack/svelte-state": {
+ "version": "0.1.0-next.23",
+ "resolved": "https://registry.npmjs.org/@layerstack/svelte-state/-/svelte-state-0.1.0-next.23.tgz",
+ "integrity": "sha512-7O4umv+gXwFfs3/vjzFWYHNXGwYnnjBapWJ5Y+9u99F4eVk6rh4ocNwqkqQNkpMZ5tUJBlRTWjPE1So6+hEzIg==",
+ "license": "MIT",
+ "dependencies": {
+ "@layerstack/utils": "2.0.0-next.18"
+ }
+ },
+ "node_modules/@layerstack/svelte-state/node_modules/@layerstack/utils": {
+ "version": "2.0.0-next.18",
+ "resolved": "https://registry.npmjs.org/@layerstack/utils/-/utils-2.0.0-next.18.tgz",
+ "integrity": "sha512-EYILHpfBRYMMEahajInu9C2AXQom5IcAEdtCeucD3QIl/fdDgRbtzn6/8QW9ewumfyNZetdUvitOksmI1+gZYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "d3-array": "^3.2.4",
+ "d3-time": "^3.1.0",
+ "d3-time-format": "^4.1.0"
+ }
+ },
+ "node_modules/@layerstack/tailwind": {
+ "version": "2.0.0-next.21",
+ "resolved": "https://registry.npmjs.org/@layerstack/tailwind/-/tailwind-2.0.0-next.21.tgz",
+ "integrity": "sha512-Qgp2EpmEHmjtura8MQzWicR6ztBRSsRvddakFtx9ShrLMz6jWzd6bCMVVRu44Q3ZOrtXmSu4QxjCZWu1ytvuPg==",
+ "license": "MIT",
+ "dependencies": {
+ "@layerstack/utils": "^2.0.0-next.18",
+ "clsx": "^2.1.1",
+ "d3-array": "^3.2.4",
+ "tailwind-merge": "^3.2.0"
+ }
+ },
+ "node_modules/@layerstack/utils": {
+ "version": "2.0.0-next.19",
+ "resolved": "https://registry.npmjs.org/@layerstack/utils/-/utils-2.0.0-next.19.tgz",
+ "integrity": "sha512-HyXCEz9pge4rFWklrSgFr3YIwdE1gP8WM6GPetmnKPDfYLF1/oetRFKI9FqiE+dw4brnULGPKJsVhjM3vQfD4A==",
+ "license": "MIT",
+ "dependencies": {
+ "d3-array": "^3.2.4",
+ "d3-time": "^3.1.0",
+ "d3-time-format": "^4.1.0"
+ }
+ },
"node_modules/@libsql/client": {
"version": "0.15.15",
"resolved": "https://registry.npmjs.org/@libsql/client/-/client-0.15.15.tgz",
@@ -2975,7 +3050,7 @@
"version": "1.0.0-next.29",
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
"integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/@rollup/plugin-babel": {
@@ -3153,7 +3228,6 @@
"cpu": [
"arm"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3167,7 +3241,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3181,7 +3254,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3195,7 +3267,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3209,7 +3280,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3223,7 +3293,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3237,7 +3306,6 @@
"cpu": [
"arm"
],
- "dev": true,
"libc": [
"glibc"
],
@@ -3254,7 +3322,6 @@
"cpu": [
"arm"
],
- "dev": true,
"libc": [
"musl"
],
@@ -3271,7 +3338,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"libc": [
"glibc"
],
@@ -3288,7 +3354,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"libc": [
"musl"
],
@@ -3305,7 +3370,6 @@
"cpu": [
"loong64"
],
- "dev": true,
"libc": [
"glibc"
],
@@ -3322,7 +3386,6 @@
"cpu": [
"loong64"
],
- "dev": true,
"libc": [
"musl"
],
@@ -3339,7 +3402,6 @@
"cpu": [
"ppc64"
],
- "dev": true,
"libc": [
"glibc"
],
@@ -3356,7 +3418,6 @@
"cpu": [
"ppc64"
],
- "dev": true,
"libc": [
"musl"
],
@@ -3373,7 +3434,6 @@
"cpu": [
"riscv64"
],
- "dev": true,
"libc": [
"glibc"
],
@@ -3390,7 +3450,6 @@
"cpu": [
"riscv64"
],
- "dev": true,
"libc": [
"musl"
],
@@ -3407,7 +3466,6 @@
"cpu": [
"s390x"
],
- "dev": true,
"libc": [
"glibc"
],
@@ -3424,7 +3482,6 @@
"cpu": [
"x64"
],
- "dev": true,
"libc": [
"glibc"
],
@@ -3441,7 +3498,6 @@
"cpu": [
"x64"
],
- "dev": true,
"libc": [
"musl"
],
@@ -3458,7 +3514,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3472,7 +3527,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3486,7 +3540,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3500,7 +3553,6 @@
"cpu": [
"ia32"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3514,7 +3566,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3528,7 +3579,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3539,14 +3589,13 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/@sveltejs/acorn-typescript": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.11.tgz",
"integrity": "sha512-LFuZUkjJ9iF7JZye/aG5XM0SFcQ5VyL0oVX4WJ9dc0Va3R3s0OauX1BESVCb+YN/ol8TAfqGDDAQsTG627Y5kw==",
- "dev": true,
"license": "MIT",
"peerDependencies": {
"acorn": "^8.9.0"
@@ -3573,7 +3622,7 @@
"version": "2.70.1",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.70.1.tgz",
"integrity": "sha512-nY9SPHGOZro3doud9vZXDBwl9tCZIouuJztjgSHs6PAIrv9M/z5O7eOhPV5xU7CgVHA976Jwu3BA1hIFvXztkA==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.0.0",
@@ -3625,7 +3674,7 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-5.1.1.tgz",
"integrity": "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"@sveltejs/vite-plugin-svelte-inspector": "^4.0.1",
@@ -3647,7 +3696,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-4.0.1.tgz",
"integrity": "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"debug": "^4.3.7"
@@ -3692,9 +3741,25 @@
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
+ "node_modules/@types/d3-array": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz",
+ "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-contour": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz",
+ "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-array": "*",
+ "@types/geojson": "*"
+ }
+ },
"node_modules/@types/deep-eql": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
@@ -3706,7 +3771,12 @@
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
"integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
- "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/geojson": {
+ "version": "7946.0.16",
+ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
+ "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
"license": "MIT"
},
"node_modules/@types/json-schema": {
@@ -3736,7 +3806,6 @@
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
- "dev": true,
"license": "MIT"
},
"node_modules/@types/ws": {
@@ -3898,7 +3967,7 @@
"version": "8.65.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz",
"integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -4186,7 +4255,6 @@
"version": "8.17.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz",
"integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==",
- "dev": true,
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
@@ -4249,7 +4317,6 @@
"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",
"engines": {
"node": ">= 0.4"
@@ -4351,7 +4418,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
"integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
- "dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">= 0.4"
@@ -4478,7 +4544,7 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/cac": {
@@ -4636,7 +4702,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -4666,7 +4731,7 @@
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/common-tags": {
@@ -4704,7 +4769,7 @@
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
@@ -4762,6 +4827,352 @@
"node": ">=4"
}
},
+ "node_modules/d3-array": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
+ "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
+ "license": "ISC",
+ "dependencies": {
+ "internmap": "1 - 2"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-chord": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
+ "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-path": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-contour": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
+ "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
+ "license": "ISC",
+ "dependencies": {
+ "delaunator": "5"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dispatch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+ "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dsv": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
+ "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
+ "license": "ISC",
+ "dependencies": {
+ "commander": "7",
+ "iconv-lite": "0.6",
+ "rw": "1"
+ },
+ "bin": {
+ "csv2json": "bin/dsv2json.js",
+ "csv2tsv": "bin/dsv2dsv.js",
+ "dsv2dsv": "bin/dsv2dsv.js",
+ "dsv2json": "bin/dsv2json.js",
+ "json2csv": "bin/json2dsv.js",
+ "json2dsv": "bin/json2dsv.js",
+ "json2tsv": "bin/json2dsv.js",
+ "tsv2csv": "bin/dsv2dsv.js",
+ "tsv2json": "bin/dsv2json.js"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dsv/node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/d3-force": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
+ "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-quadtree": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-format": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz",
+ "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-geo": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",
+ "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2.5.0 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-geo-voronoi": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/d3-geo-voronoi/-/d3-geo-voronoi-2.1.0.tgz",
+ "integrity": "sha512-kqE4yYuOjPbKdBXG0xztCacPwkVSK2REF1opSNrnqqtXJmNcM++UbwQ8SxvwP6IQTj9RvIjjK4qeiVsEfj0Z2Q==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "3",
+ "d3-delaunay": "6",
+ "d3-geo": "3",
+ "d3-tricontour": "1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-hierarchy": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
+ "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-interpolate-path": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/d3-interpolate-path/-/d3-interpolate-path-2.3.0.tgz",
+ "integrity": "sha512-tZYtGXxBmbgHsIc9Wms6LS5u4w6KbP8C09a4/ZYc4KLMYYqub57rRBUgpUr2CIarIrJEpdAWWxWQvofgaMpbKQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-polygon": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
+ "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-quadtree": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
+ "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-random": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
+ "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-sankey": {
+ "version": "0.12.3",
+ "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz",
+ "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-array": "1 - 2",
+ "d3-shape": "^1.2.0"
+ }
+ },
+ "node_modules/d3-sankey/node_modules/d3-array": {
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
+ "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "internmap": "^1.0.0"
+ }
+ },
+ "node_modules/d3-sankey/node_modules/d3-path": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",
+ "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-sankey/node_modules/d3-shape": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz",
+ "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-path": "1"
+ }
+ },
+ "node_modules/d3-sankey/node_modules/internmap": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz",
+ "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==",
+ "license": "ISC"
+ },
+ "node_modules/d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale-chromatic": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
+ "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-interpolate": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-shape": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+ "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-path": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-tile": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/d3-tile/-/d3-tile-1.0.0.tgz",
+ "integrity": "sha512-79fnTKpPMPDS5xQ0xuS9ir0165NEwwkFpe/DSOmc2Gl9ldYzKKRDWogmTTE8wAJ8NA7PMapNfEcyKhI9Lxdu5Q==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-time": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+ "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time-format": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-time": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-tricontour": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/d3-tricontour/-/d3-tricontour-1.1.0.tgz",
+ "integrity": "sha512-G7gHKj89n2owmkGb6WX6ixcnQ0Kf/0wpa9VIh9DGdbHu8wdrlaHU4ir3/bFNERl8N8nn4G7e7qbtBG8N9caihQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-delaunay": "6",
+ "d3-scale": "4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/data-uri-to-buffer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
@@ -4829,7 +5240,7 @@
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -4864,7 +5275,7 @@
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -4906,6 +5317,24 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/delaunator": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz",
+ "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==",
+ "license": "ISC",
+ "dependencies": {
+ "robust-predicates": "^3.0.2"
+ }
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/detect-libc": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
@@ -4919,7 +5348,6 @@
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.2.tgz",
"integrity": "sha512-DObPPAfdtFbXjxLqK8s2Xk9ZuWz5+ZoFEhC7J76es4GU/rEiXwHTmbImoCdyoCOcBH1UF3+Cz6Z2sYD4hyl5TA==",
- "dev": true,
"license": "MIT"
},
"node_modules/drizzle-kit": {
@@ -5270,7 +5698,7 @@
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
"integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
- "dev": true,
+ "devOptional": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
@@ -5472,7 +5900,6 @@
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
"integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==",
- "dev": true,
"license": "MIT"
},
"node_modules/espree": {
@@ -5510,7 +5937,6 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/esrap/-/esrap-2.3.0.tgz",
"integrity": "sha512-GQ/7RN8uOtEfNpzZzBMTzW9JBcX42oaSVtPzdF+6cEL8pqIL094iUpr9jzYGn4O4P/1S60dJ6izyT8F4LYARng==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15"
@@ -5629,7 +6055,7 @@
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
@@ -5815,7 +6241,6 @@
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
@@ -6194,6 +6619,18 @@
"node": ">= 0.4"
}
},
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/idb": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz",
@@ -6253,6 +6690,15 @@
"node": ">= 0.4"
}
},
+ "node_modules/internmap": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/is-array-buffer": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
@@ -6869,7 +7315,7 @@
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
"integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -6889,6 +7335,47 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/layerchart": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/layerchart/-/layerchart-2.4.0.tgz",
+ "integrity": "sha512-POOduarWXSi9aTbcBrPxBp7AK0lomd/tdeNnquDo6f2cUyYYx+Qjn+dgCaaBCX/JSWT31NNHDUYdtZB7ocC57A==",
+ "license": "MIT",
+ "dependencies": {
+ "@dagrejs/dagre": "^2.0.4",
+ "@layerstack/svelte-actions": "1.0.1-next.18",
+ "@layerstack/svelte-state": "0.1.0-next.23",
+ "@layerstack/tailwind": "2.0.0-next.21",
+ "@layerstack/utils": "2.0.0-next.19",
+ "@types/d3-contour": "^3.0.6",
+ "d3-array": "^3.2.4",
+ "d3-chord": "^3.0.1",
+ "d3-color": "^3.1.0",
+ "d3-contour": "^4.0.2",
+ "d3-delaunay": "^6.0.4",
+ "d3-dsv": "^3.0.1",
+ "d3-force": "^3.0.0",
+ "d3-geo": "^3.1.1",
+ "d3-geo-voronoi": "^2.1.0",
+ "d3-hierarchy": "^3.1.2",
+ "d3-interpolate": "^3.0.1",
+ "d3-interpolate-path": "^2.3.0",
+ "d3-path": "^3.1.0",
+ "d3-polygon": "^3.0.1",
+ "d3-quadtree": "^3.0.1",
+ "d3-random": "^3.0.1",
+ "d3-sankey": "^0.12.3",
+ "d3-scale": "^4.0.2",
+ "d3-scale-chromatic": "^3.1.0",
+ "d3-shape": "^3.2.0",
+ "d3-tile": "^1.0.0",
+ "d3-time": "^3.1.0",
+ "memoize": "^10.2.0",
+ "runed": "^0.37.1"
+ },
+ "peerDependencies": {
+ "svelte": "^5.0.0"
+ }
+ },
"node_modules/leven": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@@ -6959,7 +7446,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
- "dev": true,
"license": "MIT"
},
"node_modules/locate-path": {
@@ -7009,11 +7495,19 @@
"yallist": "^3.0.2"
}
},
+ "node_modules/lz-string": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
+ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
+ "license": "MIT",
+ "bin": {
+ "lz-string": "bin/bin.js"
+ }
+ },
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
@@ -7029,6 +7523,33 @@
"node": ">= 0.4"
}
},
+ "node_modules/memoize": {
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/memoize/-/memoize-10.2.0.tgz",
+ "integrity": "sha512-DeC6b7QBrZsRs3Y02A6A7lQyzFbsQbqgjI6UW0GigGWV+u1s25TycMr0XHZE4cJce7rY/vyw2ctMQqfDkIhUEA==",
+ "license": "MIT",
+ "dependencies": {
+ "mimic-function": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/memoize?sponsor=1"
+ }
+ },
+ "node_modules/mimic-function": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
+ "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/minimatch": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
@@ -7066,7 +7587,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
"integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -7076,14 +7597,14 @@
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.16",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
"integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
- "dev": true,
+ "devOptional": true,
"funding": [
{
"type": "github",
@@ -7361,14 +7882,14 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "dev": true,
+ "devOptional": true,
"license": "ISC"
},
"node_modules/picomatch": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
"integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -7391,7 +7912,7 @@
"version": "8.5.23",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz",
"integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==",
- "dev": true,
+ "devOptional": true,
"funding": [
{
"type": "opencollective",
@@ -7758,11 +8279,17 @@
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
+ "node_modules/robust-predicates": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz",
+ "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==",
+ "license": "Unlicense"
+ },
"node_modules/rollup": {
"version": "4.62.3",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.3.tgz",
"integrity": "sha512-Gu0c0iH9FzgX1L1t7ByIbbS3Vmdz+6KHm/EsqmmC71gUQ82yvZRkTK6XzrFObSka91WUVdynqp6nsfilzr5k6Q==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.9"
@@ -7803,6 +8330,40 @@
"fsevents": "~2.3.2"
}
},
+ "node_modules/runed": {
+ "version": "0.37.1",
+ "resolved": "https://registry.npmjs.org/runed/-/runed-0.37.1.tgz",
+ "integrity": "sha512-MeFY73xBW8IueWBm012nNFIGy19WUGPLtknavyUPMpnyt350M47PhGSGrGoSLbidwn+Zlt/O0cp8/OZE3LASWA==",
+ "funding": [
+ "https://github.com/sponsors/huntabyte",
+ "https://github.com/sponsors/tglide"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.3",
+ "esm-env": "^1.0.0",
+ "lz-string": "^1.5.0"
+ },
+ "peerDependencies": {
+ "@sveltejs/kit": "^2.21.0",
+ "svelte": "^5.7.0",
+ "zod": "^4.1.0"
+ },
+ "peerDependenciesMeta": {
+ "@sveltejs/kit": {
+ "optional": true
+ },
+ "zod": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/rw": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+ "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/sade": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz",
@@ -7871,6 +8432,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
"node_modules/semver": {
"version": "7.8.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
@@ -7898,7 +8465,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.2.tgz",
"integrity": "sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/set-function-length": {
@@ -8073,7 +8640,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz",
"integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"@polka/url": "^1.0.0-next.24",
@@ -8098,7 +8665,7 @@
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
+ "devOptional": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@@ -8108,7 +8675,7 @@
"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,
+ "devOptional": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@@ -8118,7 +8685,7 @@
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
@@ -8322,7 +8889,6 @@
"version": "5.56.8",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.56.8.tgz",
"integrity": "sha512-PY8LOw7xP6c8IOiVqdo0sbbZVYhXRSfklOQLAUyGBKqjTX0wx/z4l/9J+PmBpmlLnxzEb1NqltxQ5/wZme/Cmg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/remapping": "^2.3.4",
@@ -8406,12 +8972,21 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
"integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.6"
}
},
+ "node_modules/tailwind-merge": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.6.0.tgz",
+ "integrity": "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
"node_modules/temp-dir": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz",
@@ -8445,7 +9020,7 @@
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.49.0.tgz",
"integrity": "sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA==",
- "dev": true,
+ "devOptional": true,
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
@@ -8478,7 +9053,7 @@
"version": "0.2.17",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
"integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
@@ -8525,7 +9100,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
"integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -8548,7 +9123,7 @@
"version": "4.23.1",
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.1.tgz",
"integrity": "sha512-GQHnkIfxyx1wYCOS/wonik5MVRZU9hi1TEZmzGZSCJB1y9YgoZ8H6itNE/u4suE+yLmOzuE4E5S4TZ/ZX2wcWQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"esbuild": "~0.28.0"
@@ -8570,7 +9145,6 @@
"cpu": [
"ppc64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8587,7 +9161,6 @@
"cpu": [
"arm"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8604,7 +9177,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8621,7 +9193,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8638,7 +9209,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8655,7 +9225,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8672,7 +9241,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8689,7 +9257,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8706,7 +9273,6 @@
"cpu": [
"arm"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8723,7 +9289,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8740,7 +9305,6 @@
"cpu": [
"ia32"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8757,7 +9321,6 @@
"cpu": [
"loong64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8774,7 +9337,6 @@
"cpu": [
"mips64el"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8791,7 +9353,6 @@
"cpu": [
"ppc64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8808,7 +9369,6 @@
"cpu": [
"riscv64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8825,7 +9385,6 @@
"cpu": [
"s390x"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8842,7 +9401,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8859,7 +9417,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8876,7 +9433,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8893,7 +9449,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8910,7 +9465,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8927,7 +9481,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8944,7 +9497,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8961,7 +9513,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8978,7 +9529,6 @@
"cpu": [
"ia32"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8995,7 +9545,6 @@
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -9009,7 +9558,7 @@
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
"integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
- "dev": true,
+ "devOptional": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
@@ -9358,7 +9907,7 @@
"version": "6.4.3",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz",
"integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.25.0",
@@ -9487,7 +10036,7 @@
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz",
"integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"workspaces": [
"tests/deps/*",
@@ -10033,7 +10582,6 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz",
"integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==",
- "dev": true,
"license": "MIT"
}
}
diff --git a/package.json b/package.json
index 17ce365..7dcb8bd 100644
--- a/package.json
+++ b/package.json
@@ -38,6 +38,7 @@
"@libsql/client": "^0.15.0",
"drizzle-orm": "^0.44.0",
"fuzzysort": "^4.0.2",
+ "layerchart": "^2.4.0",
"valibot": "^1.4.2"
}
}
diff --git a/src/lib/components/HealthChart.svelte b/src/lib/components/HealthChart.svelte
new file mode 100644
index 0000000..f1f87ed
--- /dev/null
+++ b/src/lib/components/HealthChart.svelte
@@ -0,0 +1,110 @@
+<script lang="ts">
+ import { AreaChart, Area, Points, Text } from 'layerchart';
+ import type { HealthPoint } from '$lib/shared/routine-metrics';
+
+ let {
+ series,
+ markers = [],
+ ariaLabel,
+ }: {
+ series: HealthPoint[];
+ markers?: number[];
+ ariaLabel: string;
+ } = $props();
+
+ const dateFormatter = new Intl.DateTimeFormat('fr-FR', {
+ day: 'numeric',
+ month: 'short',
+ });
+
+ const data = $derived(
+ series.map((p) => ({ date: new Date(p.at), health: p.health })),
+ );
+ const dots = $derived(
+ markers.map((t) => ({ date: new Date(t), health: 100 })),
+ );
+
+ // Up to 5 x ticks spread across the series, skipping repeats (two samples
+ // the same day would print the same label twice).
+ const xTicks = $derived.by(() => {
+ if (data.length < 2) return undefined;
+ const step = Math.max(1, Math.floor((data.length - 1) / 4));
+ const ticks: Date[] = [];
+ for (let i = 0; i < data.length; i += step) {
+ const last = i + step >= data.length;
+ const date = last ? data[data.length - 1].date : data[i].date;
+ const label = dateFormatter.format(date);
+ if (
+ ticks.length > 0 &&
+ dateFormatter.format(ticks[ticks.length - 1]) === label
+ )
+ continue;
+ ticks.push(date);
+ if (last) break;
+ }
+ return ticks;
+ });
+</script>
+
+{#if data.length >= 2}
+ <div class="health-chart" role="img" aria-label={ariaLabel}>
+ <AreaChart
+ {data}
+ x="date"
+ y="health"
+ yDomain={[0, 100]}
+ series={[{ key: 'santé', value: 'health', color: 'var(--accent)' }]}
+ axis
+ grid={{ y: true, yTicks: [25, 50, 75, 100] }}
+ rule={false}
+ props={{
+ xAxis: {
+ ticks: xTicks,
+ format: (d: Date) => dateFormatter.format(d),
+ tickLabel: xTickLabel,
+ },
+ yAxis: { ticks: [25, 50, 75, 100] },
+ }}
+ marks={chartMarks}
+ />
+ </div>
+{:else}
+ <p class="empty">Pas encore assez d'historique pour tracer la courbe.</p>
+{/if}
+
+{#snippet xTickLabel({
+ props,
+ index,
+}: {
+ props: Record<string, unknown>;
+ index: number;
+})}
+ <Text
+ {...props}
+ textAnchor={index === 0
+ ? 'start'
+ : index === (xTicks?.length ?? 0) - 1
+ ? 'end'
+ : 'middle'}
+ />
+{/snippet}
+
+{#snippet chartMarks()}
+ <Area seriesKey="santé" fillOpacity={0.2} line={{ strokeWidth: 2 }} />
+ <Points
+ data={dots}
+ x="date"
+ y="health"
+ r={4}
+ fill="var(--success)"
+ stroke="var(--white)"
+ strokeWidth={1.5}
+ />
+{/snippet}
+
+<style>
+ .health-chart {
+ width: 100%;
+ height: 11rem;
+ }
+</style>
diff --git a/src/lib/server/services/routines.test.ts b/src/lib/server/services/routines.test.ts
index e8c4f35..04151c2 100644
--- a/src/lib/server/services/routines.test.ts
+++ b/src/lib/server/services/routines.test.ts
@@ -141,6 +141,9 @@ describe('routines service', () => {
expect(dashboard.routines).toHaveLength(1);
expect(dashboard.routines[0].status).toBe('veryLate');
expect(dashboard.healthScore).toBe(0);
+ const last = dashboard.healthSeries[dashboard.healthSeries.length - 1];
+ expect(last.at).toBe(now);
+ expect(last.health).toBe(dashboard.healthScore);
});
it('overview aggregates per-project health and a global score', async () => {
diff --git a/src/lib/server/services/routines.ts b/src/lib/server/services/routines.ts
index b314d50..ec03c50 100644
--- a/src/lib/server/services/routines.ts
+++ b/src/lib/server/services/routines.ts
@@ -6,7 +6,9 @@ import type { Routine, RoutineCompletion } from './types';
import {
computeRoutineState,
computeHealthScore,
+ computeProjectHealthSeries,
type RoutineState,
+ type HealthPoint,
} from '$lib/shared/routine-metrics';
// ponytail: next routine id via MAX(id)+1, safe enough for a single-user app;
@@ -199,6 +201,7 @@ export async function complete(
export interface ProjectDashboard {
routines: RoutineState[];
healthScore: number;
+ healthSeries: HealthPoint[];
}
export async function loadProjectDashboard(
@@ -219,7 +222,8 @@ export async function loadProjectDashboard(
if (!existing || r.version > existing.version) current.set(r.id, r);
}
const routines_ = [...current.values()].filter((r) => !r.archived);
- if (routines_.length === 0) return { routines: [], healthScore: 100 };
+ if (routines_.length === 0)
+ return { routines: [], healthScore: 100, healthSeries: [] };
const completions = await db
.select()
@@ -232,7 +236,11 @@ export async function loadProjectDashboard(
);
const states = routines_.map((r) => computeRoutineState(r, completions, now));
- return { routines: states, healthScore: computeHealthScore(states) };
+ return {
+ routines: states,
+ healthScore: computeHealthScore(states),
+ healthSeries: computeProjectHealthSeries(routines_, completions, now),
+ };
}
export interface ProjectsOverview {
diff --git a/src/lib/shared/routine-metrics.test.ts b/src/lib/shared/routine-metrics.test.ts
index ccc06ba..57b8c38 100644
--- a/src/lib/shared/routine-metrics.test.ts
+++ b/src/lib/shared/routine-metrics.test.ts
@@ -3,6 +3,7 @@ import {
computeRoutineState,
computeHealthScore,
computeHealthSeries,
+ computeProjectHealthSeries,
parseIntervalToDays,
} from './routine-metrics';
@@ -125,6 +126,54 @@ describe('computeHealthSeries', () => {
});
});
+describe('computeProjectHealthSeries', () => {
+ it('is empty with no routines', () => {
+ expect(computeProjectHealthSeries([], [], Date.parse(CREATED))).toEqual([]);
+ });
+
+ it('averages per-routine health and ends at the current score', () => {
+ const second = { ...routine, id: 2, name: 'read' };
+ const doneAt = Date.parse(CREATED) + 5 * DAY_MS;
+ const now = Date.parse(CREATED) + 8 * DAY_MS;
+ const series = computeProjectHealthSeries(
+ [routine, second],
+ [{ routineId: 2, doneAt: new Date(doneAt).toISOString() }],
+ now,
+ );
+ const states = [
+ computeRoutineState(routine, [], now),
+ computeRoutineState(
+ second,
+ [{ routineId: 2, doneAt: new Date(doneAt).toISOString() }],
+ now,
+ ),
+ ];
+ const last = series[series.length - 1];
+ expect(last.at).toBe(now);
+ expect(last.health).toBe(computeHealthScore(states));
+ });
+
+ it('only counts routines alive that day', () => {
+ const late = {
+ ...routine,
+ id: 2,
+ name: 'latecomer',
+ createdAt: new Date(Date.parse(CREATED) + 10 * DAY_MS).toISOString(),
+ };
+ const now = Date.parse(CREATED) + 11 * DAY_MS;
+ const series = computeProjectHealthSeries([routine, late], [], now);
+ const beforeLate = series.find(
+ (p) => p.at === Date.parse(CREATED) + 5 * DAY_MS,
+ );
+ const single = computeHealthSeries(
+ routine,
+ [],
+ Date.parse(CREATED) + 5 * DAY_MS,
+ );
+ expect(beforeLate?.health).toBe(single[single.length - 1].health);
+ });
+});
+
describe('parseIntervalToDays', () => {
it('converts units to days', () => {
expect(parseIntervalToDays('1d')).toBe(1);
diff --git a/src/lib/shared/routine-metrics.ts b/src/lib/shared/routine-metrics.ts
index 4af989d..891a0c9 100644
--- a/src/lib/shared/routine-metrics.ts
+++ b/src/lib/shared/routine-metrics.ts
@@ -103,6 +103,13 @@ export interface HealthPoint {
health: number;
}
+/** Health at instant t given the reference anchor (creation or last completion). */
+function healthAtPoint(refMs: number, periodMs: number, t: number): number {
+ const dueMs = startOfUtcDay(refMs) + periodMs;
+ const rawRatio = (t - dueMs) / periodMs;
+ return Math.max(0, Math.round(100 - Math.max(0, rawRatio) * 100));
+}
+
/**
* Daily health samples from the routine's creation to now: 100 right after a
* completion, then a linear decay to 0 once a full period is overdue (sawtooth).
@@ -139,9 +146,7 @@ export function computeHealthSeries(
refMs = completionMs[nextCompletion];
nextCompletion++;
}
- const dueMs = startOfUtcDay(refMs) + periodMs;
- const rawRatio = (t - dueMs) / periodMs;
- return Math.max(0, Math.round(100 - Math.max(0, rawRatio) * 100));
+ return healthAtPoint(refMs, periodMs, t);
};
for (let t = startMs; t <= now; t += DAY_MS) {
@@ -155,6 +160,67 @@ export function computeHealthSeries(
return points;
}
+/**
+ * Project health series: daily mean of per-routine health across the routines
+ * alive that day (createdAt <= t). Same sawtooth as the per-routine series,
+ * so the project curve ends at the current health score. Empty when no
+ * routines.
+ */
+export function computeProjectHealthSeries(
+ routines: RoutineInput[],
+ completions: CompletionInput[],
+ now: number,
+): HealthPoint[] {
+ if (routines.length === 0) return [];
+ const ids = new Set(routines.map((r) => r.id));
+ const completionsByRoutine = new Map<number, number[]>();
+ for (const c of completions) {
+ if (!ids.has(c.routineId)) continue;
+ const t = Date.parse(c.doneAt);
+ if (!Number.isFinite(t)) continue;
+ completionsByRoutine.set(c.routineId, [
+ ...(completionsByRoutine.get(c.routineId) ?? []),
+ t,
+ ]);
+ }
+ for (const times of completionsByRoutine.values())
+ times.sort((a, b) => a - b);
+
+ const createdOrNow = (r: RoutineInput): number => {
+ const t = Date.parse(r.createdAt);
+ return Number.isFinite(t) ? t : now;
+ };
+ // Mean per-routine health across the routines alive at t.
+ const sampleAt = (t: number): number | null => {
+ const alive = routines.filter((r) => Date.parse(r.createdAt) <= t);
+ if (alive.length === 0) return null;
+ const total = alive.reduce((sum, r) => {
+ const doneAt = (completionsByRoutine.get(r.id) ?? []).filter(
+ (d) => d <= t,
+ );
+ const anchor = doneAt.length > 0 ? Math.max(...doneAt) : createdOrNow(r);
+ return sum + healthAtPoint(anchor, r.frequencyDays * DAY_MS, t);
+ }, 0);
+ return Math.round(total / alive.length);
+ };
+
+ const startMs = Math.min(...routines.map(createdOrNow));
+ if (now <= startMs) return [{ at: now, health: 100 }];
+
+ const points: HealthPoint[] = [];
+ for (let t = startMs; t <= now; t += DAY_MS) {
+ const health = sampleAt(t);
+ if (health !== null) points.push({ at: t, health });
+ }
+ if (points.length === 0) return [{ at: now, health: 100 }];
+
+ // Final unaligned sample so the curve ends at the present instant.
+ if (points[points.length - 1].at < now) {
+ points.push({ at: now, health: sampleAt(now) ?? 100 });
+ }
+ return points;
+}
+
export function formatFrequency(days: number): string {
if (days === 1) return 'chaque jour';
if (days === 7) return 'chaque semaine';
diff --git a/src/routes/projects/[id]/+page.server.ts b/src/routes/projects/[id]/+page.server.ts
index 66cb9f3..7cb90ff 100644
--- a/src/routes/projects/[id]/+page.server.ts
+++ b/src/routes/projects/[id]/+page.server.ts
@@ -32,6 +32,7 @@ export const load: PageServerLoad = async ({ params }) => {
project: { id: project.id, name: project.name },
routines: dashboard.routines,
healthScore: dashboard.healthScore,
+ healthSeries: dashboard.healthSeries,
archived,
};
};
diff --git a/src/routes/projects/[id]/+page.svelte b/src/routes/projects/[id]/+page.svelte
index b67ff45..a58aeba 100644
--- a/src/routes/projects/[id]/+page.svelte
+++ b/src/routes/projects/[id]/+page.svelte
@@ -1,6 +1,7 @@
<script lang="ts">
import { enhance } from '$app/forms';
import { matchItems } from '$lib/components/fuzzy-search';
+ import HealthChart from '$lib/components/HealthChart.svelte';
import ScoreBadge from '$lib/components/ScoreBadge.svelte';
import StatusBadge from '$lib/components/StatusBadge.svelte';
import {
@@ -53,6 +54,18 @@
<ScoreBadge score={data.healthScore} />
</header>
+ {#if data.healthSeries.length > 0}
+ <section>
+ <h2 class="section-title">Santé</h2>
+ <div class="form-card chart-card">
+ <HealthChart
+ series={data.healthSeries}
+ ariaLabel="Évolution de la santé du projet dans le temps"
+ />
+ </div>
+ </section>
+ {/if}
+
<form
class="add-bar"
method="POST"
@@ -251,6 +264,21 @@
</main>
<style>
+ .section-title {
+ font-size: 0.75rem;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ color: var(--accent);
+ margin: 1.5rem 0 0.5rem;
+ padding-left: 0.5rem;
+ border-left: 3px solid var(--accent);
+ }
+
+ .chart-card {
+ align-items: stretch;
+ }
+
.routine-row {
flex-wrap: wrap;
padding: 0.75rem 1rem;
diff --git a/src/routes/projects/[projectId]/routine/[routineId]/+page.svelte b/src/routes/projects/[projectId]/routine/[routineId]/+page.svelte
index bcdca79..ab2644d 100644
--- a/src/routes/projects/[projectId]/routine/[routineId]/+page.svelte
+++ b/src/routes/projects/[projectId]/routine/[routineId]/+page.svelte
@@ -1,5 +1,6 @@
<script lang="ts">
import { enhance } from '$app/forms';
+ import HealthChart from '$lib/components/HealthChart.svelte';
import ScoreBadge from '$lib/components/ScoreBadge.svelte';
import StatusBadge from '$lib/components/StatusBadge.svelte';
import {
@@ -7,21 +8,17 @@
parseIntervalToDays,
computeHealthSeries,
} from '$lib/shared/routine-metrics';
-
let { data, form } = $props();
-
let name = $state('');
let frequency = $state('');
let description = $state('');
let note = $state('');
let saved = $state(false);
-
$effect(() => {
name = data.routine.name;
frequency = `${data.routine.frequencyDays}d`;
description = data.routine.description;
});
-
$effect(() => {
if (form?.success && form?.type === 'edit') {
saved = true;
@@ -29,9 +26,7 @@
return () => clearTimeout(t);
}
});
-
const frequencyDays = $derived(parseIntervalToDays(frequency));
-
const dateFormatter = new Intl.DateTimeFormat('fr-FR', {
day: 'numeric',
month: 'short',
@@ -41,39 +36,15 @@
month: 'short',
year: 'numeric',
});
-
const frozenAt = $derived(
data.routine.archived
? Date.parse(data.routine.archivedAt ?? data.routine.createdAt)
: Date.now(),
);
-
const series = $derived(
computeHealthSeries(data.routine, data.completions, frozenAt),
);
-
- const PAD = 10;
- const W = 800;
- const H = 140;
- const geometry = $derived.by(() => {
- if (series.length < 2) return null;
- const minT = series[0].at;
- const maxT = series[series.length - 1].at;
- const span = Math.max(1, maxT - minT);
- const x = (t: number) => PAD + ((t - minT) / span) * (W - 2 * PAD);
- const y = (h: number) => H - PAD - (h / 100) * (H - 2 * PAD);
- const points = series
- .map((p) => `${x(p.at).toFixed(1)},${y(p.health).toFixed(1)}`)
- .join(' ');
- const grid = [0, 25, 50, 75, 100].map((h) => ({ y: y(h), label: h }));
- const markers = data.completions
- .map((c) => {
- const t = Date.parse(c.doneAt);
- return t >= minT && t <= maxT ? { x: x(t), y: y(100) } : null;
- })
- .filter((m): m is { x: number; y: number } => m !== null);
- return { points, grid, markers };
- });
+ const markers = $derived(data.completions.map((c) => Date.parse(c.doneAt)));
</script>
<main>
@@ -159,28 +130,12 @@
<h2 class="section-title">Santé</h2>
<div class="form-card chart-card">
- {#if geometry}
- <svg
- class="health-chart"
- viewBox="0 0 {W} {H}"
- preserveAspectRatio="none"
- role="img"
- aria-label="Évolution de la santé de la routine dans le temps"
- >
- {#each geometry.grid as line (line.label)}
- <line
- x1={PAD}
- y1={line.y}
- x2={W - PAD}
- y2={line.y}
- class="chart-grid"
- />
- {/each}
- <polyline points={geometry.points} class="chart-line" fill="none" />
- {#each geometry.markers as m (m.x)}
- <circle cx={m.x} cy={m.y} r="4" class="chart-marker" />
- {/each}
- </svg>
+ <HealthChart
+ {series}
+ {markers}
+ ariaLabel="Évolution de la santé de la routine dans le temps"
+ />
+ {#if series.length >= 2}
<div class="chart-footer">
<StatusBadge
status={data.state.status}
@@ -195,8 +150,6 @@
{/if}
</span>
</div>
- {:else}
- <p class="empty">Pas encore assez d'historique pour tracer la courbe.</p>
{/if}
</div>
@@ -280,30 +233,6 @@
align-items: stretch;
}
- .health-chart {
- width: 100%;
- height: 9rem;
- }
-
- .chart-grid {
- stroke: var(--border);
- stroke-width: 1;
- stroke-dasharray: 2 4;
- }
-
- .chart-line {
- stroke: var(--accent);
- stroke-width: 2;
- stroke-linejoin: round;
- stroke-linecap: round;
- }
-
- .chart-marker {
- fill: var(--success);
- stroke: var(--white);
- stroke-width: 1.5;
- }
-
.chart-footer {
display: flex;
align-items: center;