diff --git a/package-lock.json b/package-lock.json
index 896f540a682a11a7e9c361731b6f01e0e790a377..0ca1112a69c6e3b7f3a72bfae7b02f397190f876 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -63,6 +63,7 @@
         "tailwindcss": "^3.0.15",
         "to-words": "^3.6.1",
         "tslib": "^2.0.0",
+        "tsx": "^3.13.0",
         "typescript": "^5.0.4",
         "uuid": "^9.0.0",
         "vite": "^4.3.0",
@@ -1687,6 +1688,12 @@
       "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
       "dev": true
     },
+    "node_modules/buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+      "dev": true
+    },
     "node_modules/builtin-modules": {
       "version": "3.3.0",
       "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
@@ -2916,6 +2923,18 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/get-tsconfig": {
+      "version": "4.7.2",
+      "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz",
+      "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==",
+      "dev": true,
+      "dependencies": {
+        "resolve-pkg-maps": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+      }
+    },
     "node_modules/glob": {
       "version": "8.1.0",
       "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
@@ -4768,6 +4787,15 @@
         "node": ">=4"
       }
     },
+    "node_modules/resolve-pkg-maps": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+      "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+      }
+    },
     "node_modules/reusify": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -5055,6 +5083,15 @@
         "sorcery": "bin/sorcery"
       }
     },
+    "node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/source-map-js": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
@@ -5064,6 +5101,16 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/source-map-support": {
+      "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,
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
     "node_modules/split": {
       "version": "0.3.3",
       "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz",
@@ -5643,6 +5690,37 @@
       "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
       "dev": true
     },
+    "node_modules/tsx": {
+      "version": "3.13.0",
+      "resolved": "https://registry.npmjs.org/tsx/-/tsx-3.13.0.tgz",
+      "integrity": "sha512-rjmRpTu3as/5fjNq/kOkOtihgLxuIz6pbKdj9xwP4J5jOLkBxw/rjN5ANw+KyrrOXV5uB7HC8+SrrSJxT65y+A==",
+      "dev": true,
+      "dependencies": {
+        "esbuild": "~0.18.20",
+        "get-tsconfig": "^4.7.2",
+        "source-map-support": "^0.5.21"
+      },
+      "bin": {
+        "tsx": "dist/cli.mjs"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      }
+    },
+    "node_modules/tsx/node_modules/fsevents": {
+      "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,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
     "node_modules/type-check": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
diff --git a/package.json b/package.json
index fbf691d7c97e1ae4a0c007ed98bb3627e652d9a9..1ade9c1c729745be87924739d9b12992b7a4a490 100644
--- a/package.json
+++ b/package.json
@@ -68,6 +68,7 @@
     "tailwindcss": "^3.0.15",
     "to-words": "^3.6.1",
     "tslib": "^2.0.0",
+    "tsx": "^3.13.0",
     "typescript": "^5.0.4",
     "uuid": "^9.0.0",
     "vite": "^4.3.0",
diff --git a/src/scripts/clear_budget_simulations_cache.ts b/src/scripts/clear_budget_simulations_cache.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dfeb9c751218f34449ebdd96d26dc33ba1d4a249
--- /dev/null
+++ b/src/scripts/clear_budget_simulations_cache.ts
@@ -0,0 +1,27 @@
+import fs from "fs-extra"
+import path from "path"
+
+import config from "$lib/server/config"
+
+const { simulationsBudgetDir } = config
+
+// Remove private budget simulations from index.
+const indexFilePath = path.join(simulationsBudgetDir, "index.json")
+const publicSimulationsSummary = (
+  fs.readJsonSync(indexFilePath) as Array<{ public: boolean }>
+).filter((summary) => summary.public)
+fs.writeJsonSync(indexFilePath, publicSimulationsSummary, {
+  encoding: "utf-8",
+  spaces: 2,
+})
+
+// Remove every budget simulations from cache.
+for (const dirName of await fs.readdir(simulationsBudgetDir)) {
+  if (dirName.startsWith(".")) {
+    continue
+  }
+  const dir = path.join(simulationsBudgetDir, dirName)
+  if (fs.lstatSync(dir).isDirectory()) {
+    fs.removeSync(dir)
+  }
+}
diff --git a/tsconfig.json b/tsconfig.json
index 794b95b6426836ad7f5e8683729dd758e7f2bc7c..e45aa0974570bd4185c14a3e73eaa0f005a938a7 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -8,7 +8,11 @@
     "resolveJsonModule": true,
     "skipLibCheck": true,
     "sourceMap": true,
-    "strict": true
+    "strict": true,
+    "paths": {
+      "$lib": ["./src/lib"],
+      "$lib/*": ["./src/lib/*"]
+    }
   }
   // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
   //