├── .github
└── FUNDING.yml
├── .gitignore
├── Caddyfile
├── LICENSE
├── Makefile
├── README.md
├── frontend
├── .gitignore
├── README.md
├── index.html
├── package-lock.json
├── package.json
├── public
│ └── assets
│ │ ├── favicon.png
│ │ ├── logo-dark.svg
│ │ └── logo-light.svg
├── src
│ ├── App.svelte
│ ├── app.css
│ ├── lib
│ │ ├── Node.svelte
│ │ └── Root.svelte
│ ├── main.ts
│ └── vite-env.d.ts
├── svelte.config.js
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
├── go.mod
├── go.sum
├── middleware.go
├── page.go
├── port.go
├── response.go
├── screenshot.png
├── server.go
├── static
├── assets
│ ├── favicon.png
│ ├── index-B254VF7R.css
│ ├── index-CLXqnKY8.js
│ ├── logo-dark.svg
│ └── logo-light.svg
└── index.html
├── store.go
└── xcaddy.sh
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: abiosoft
2 | custom:
3 | - "https://buymeacoffee.com/abiosoft"
4 |
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | caddy
2 |
--------------------------------------------------------------------------------
/Caddyfile:
--------------------------------------------------------------------------------
1 | # example Caddyfile for debugging purposes
2 |
3 | :8888
4 |
5 | inspect
6 |
7 | route /sample {
8 | request_header +Extra-header "Extra values"
9 | header +My-header "Some header value"
10 | rewrite /sample/* /sample/secondrequest
11 | respond "Hello from caddy server"
12 | }
13 |
14 | route /assets/* {
15 | header Cache-Control "public, max-age=3600"
16 | inspect
17 | file_server {
18 | root /var/www/assets
19 | }
20 | }
21 |
22 | # Rewrite rule for custom API versioning
23 | rewrite /api/* /api/v1/*
24 |
25 | # Route for custom API with rate limiting
26 | route /api/v1/* {
27 | respond "API request received"
28 | }
29 |
30 | # Route for a custom endpoint like a webhook
31 | route /webhook/* {
32 | respond "Webhook received"
33 | }
34 |
35 | route /error* {
36 | inspect
37 | error "This is user triggered error" 503
38 | }
39 |
40 | # Custom headers for enhanced security and performance
41 | header {
42 | Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
43 | X-Content-Type-Options "nosniff"
44 | X-Frame-Options "DENY"
45 | Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self';"
46 | Referrer-Policy "no-referrer"
47 | Permissions-Policy "geolocation=(self)"
48 | }
49 |
50 | handle_errors {
51 | inspect
52 | respond {err.message} {err.status_code}
53 | }
54 |
55 | # Catch-all route for everything else
56 | route {
57 | inspect
58 | respond "404 Page Not Found" 404
59 | }
60 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Abiola Ibrahim
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | all: build
2 |
3 | build:
4 | bash xcaddy.sh
5 |
6 | .PHONY: frontend
7 | frontend:
8 | cd frontend && rm -rf dist && npm run build
9 | rm -rf static/assets && cp -R frontend/dist/. static
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # caddy-inspect
2 |
3 | The Inspect plugin provides an easy way to inspect HTTP requests in Caddy.
4 |
5 | The Caddyfile is already easy to write. The Inspect plugin takes it a step further by allowing you to place breakpoints in the Caddyfile and ascertain the behaviour of Caddy.
6 |
7 | 
8 |
9 | ## Features
10 |
11 | - Insert a breakpoint anywhere in the Caddyfile with the `inspect` keyword.
12 | - Inspecting HTTP and Caddy contexts
13 | - Intercepting and terminating HTTP requests and responses
14 | - Works in the browser, zero setup required.
15 |
16 | ## Installation
17 |
18 | ```
19 | xcaddy build --with github.com/abiosoft/caddy-inspect
20 | ```
21 |
22 | ## Getting Started
23 |
24 | ### Specify breakpoints in the Caddyfile
25 |
26 | Place the `inspect` keyword anywhere an [HTTP directive](https://caddyserver.com/docs/caddyfile/directives#caddyfile-directives) is supported in the Caddyfile. The keyword can be specified multiple times.
27 |
28 | ```caddy
29 | :8080
30 |
31 | route /api {
32 | rewrite /api/* /api/v1{uri}
33 | inspect
34 | ...
35 | }
36 | ```
37 |
38 | ### Start Caddy
39 |
40 | Caddy can be started with the `--watch` flag to autoreload the Caddyfile on each modification.
41 |
42 | ```sh
43 | caddy run --watch
44 | ```
45 |
46 | ### Access the Inspect console
47 |
48 | Open http://localhost:2020 in the browser to access the console.
49 |
50 | > [!NOTE]
51 | > Another port would be assigned if `2020` is not available.
52 | > The URL can be confirmed in the Caddy logs.
53 |
54 | ### Enjoy
55 |
56 | Any HTTP request(s) made to a route containing the `inspect` keyword would pause the request and activate the console.
57 |
58 | ## Caveats
59 |
60 | > [!CAUTION]
61 | > This plugin is intended for development purposes only, sensitive information may be exposed if used in a production environment.
62 |
63 | - The plugin is tailored towards Caddyfile config. However, JSON config can be used with limited experience.
64 | - Due to the in-built [order of directives](https://caddyserver.com/docs/caddyfile/directives#directive-order) in Caddy, `inspect` is more predictable in a `route` block. Otherwise, it is ordered after the `encode` directive.
65 | - The information displayed are read-only and cannot be modified.
66 | - HTTP request and response bodies cannot be inspected. It is a deliberate limitation until there is a strong argument in favour.
67 | - The plugin stemmed from a personal use-case. Feedbacks would be appreciated to accommodate more use-cases.
68 |
69 | ## Attribution
70 |
71 | The Caddy logo is a trademark of Caddy Web Server.
72 |
73 | ## License
74 |
75 | MIT
76 |
77 | ## Sponsoring
78 |
79 | You can support the author by donating on [Github Sponsors](https://github.com/sponsors/abiosoft) or [Buy me a coffee](https://www.buymeacoffee.com/abiosoft).
80 |
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | # Svelte + TS + Vite
2 |
3 | This template should help get you started developing with Svelte and TypeScript in Vite.
4 |
5 | ## Recommended IDE Setup
6 |
7 | [VS Code](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode).
8 |
9 | ## Need an official Svelte framework?
10 |
11 | Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more.
12 |
13 | ## Technical considerations
14 |
15 | **Why use this over SvelteKit?**
16 |
17 | - It brings its own routing solution which might not be preferable for some users.
18 | - It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app.
19 |
20 | This template contains as little as possible to get started with Vite + TypeScript + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project.
21 |
22 | Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate.
23 |
24 | **Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?**
25 |
26 | Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information.
27 |
28 | **Why include `.vscode/extensions.json`?**
29 |
30 | Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project.
31 |
32 | **Why enable `allowJs` in the TS template?**
33 |
34 | While `allowJs: false` would indeed prevent the use of `.js` files in the project, it does not prevent the use of JavaScript syntax in `.svelte` files. In addition, it would force `checkJs: false`, bringing the worst of both worlds: not being able to guarantee the entire codebase is TypeScript, and also having worse typechecking for the existing JavaScript. In addition, there are valid use cases in which a mixed codebase may be relevant.
35 |
36 | **Why is HMR not preserving my local component state?**
37 |
38 | HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/rixo/svelte-hmr#svelte-hmr).
39 |
40 | If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR.
41 |
42 | ```ts
43 | // store.ts
44 | // An extremely simple external store
45 | import { writable } from 'svelte/store'
46 | export default writable(0)
47 | ```
48 |
--------------------------------------------------------------------------------
/frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Caddy Inspect
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/frontend/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "inspect",
3 | "version": "0.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "inspect",
9 | "version": "0.0.0",
10 | "devDependencies": {
11 | "@sveltejs/vite-plugin-svelte": "^5.0.3",
12 | "@tsconfig/svelte": "^5.0.4",
13 | "svelte": "^5.19.6",
14 | "svelte-check": "^4.1.4",
15 | "typescript": "~5.7.2",
16 | "vite": "^6.1.0"
17 | }
18 | },
19 | "node_modules/@ampproject/remapping": {
20 | "version": "2.3.0",
21 | "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
22 | "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
23 | "dev": true,
24 | "license": "Apache-2.0",
25 | "dependencies": {
26 | "@jridgewell/gen-mapping": "^0.3.5",
27 | "@jridgewell/trace-mapping": "^0.3.24"
28 | },
29 | "engines": {
30 | "node": ">=6.0.0"
31 | }
32 | },
33 | "node_modules/@esbuild/aix-ppc64": {
34 | "version": "0.24.2",
35 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz",
36 | "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==",
37 | "cpu": [
38 | "ppc64"
39 | ],
40 | "dev": true,
41 | "license": "MIT",
42 | "optional": true,
43 | "os": [
44 | "aix"
45 | ],
46 | "engines": {
47 | "node": ">=18"
48 | }
49 | },
50 | "node_modules/@esbuild/android-arm": {
51 | "version": "0.24.2",
52 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz",
53 | "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==",
54 | "cpu": [
55 | "arm"
56 | ],
57 | "dev": true,
58 | "license": "MIT",
59 | "optional": true,
60 | "os": [
61 | "android"
62 | ],
63 | "engines": {
64 | "node": ">=18"
65 | }
66 | },
67 | "node_modules/@esbuild/android-arm64": {
68 | "version": "0.24.2",
69 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz",
70 | "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==",
71 | "cpu": [
72 | "arm64"
73 | ],
74 | "dev": true,
75 | "license": "MIT",
76 | "optional": true,
77 | "os": [
78 | "android"
79 | ],
80 | "engines": {
81 | "node": ">=18"
82 | }
83 | },
84 | "node_modules/@esbuild/android-x64": {
85 | "version": "0.24.2",
86 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz",
87 | "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==",
88 | "cpu": [
89 | "x64"
90 | ],
91 | "dev": true,
92 | "license": "MIT",
93 | "optional": true,
94 | "os": [
95 | "android"
96 | ],
97 | "engines": {
98 | "node": ">=18"
99 | }
100 | },
101 | "node_modules/@esbuild/darwin-arm64": {
102 | "version": "0.24.2",
103 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz",
104 | "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==",
105 | "cpu": [
106 | "arm64"
107 | ],
108 | "dev": true,
109 | "license": "MIT",
110 | "optional": true,
111 | "os": [
112 | "darwin"
113 | ],
114 | "engines": {
115 | "node": ">=18"
116 | }
117 | },
118 | "node_modules/@esbuild/darwin-x64": {
119 | "version": "0.24.2",
120 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz",
121 | "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==",
122 | "cpu": [
123 | "x64"
124 | ],
125 | "dev": true,
126 | "license": "MIT",
127 | "optional": true,
128 | "os": [
129 | "darwin"
130 | ],
131 | "engines": {
132 | "node": ">=18"
133 | }
134 | },
135 | "node_modules/@esbuild/freebsd-arm64": {
136 | "version": "0.24.2",
137 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz",
138 | "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==",
139 | "cpu": [
140 | "arm64"
141 | ],
142 | "dev": true,
143 | "license": "MIT",
144 | "optional": true,
145 | "os": [
146 | "freebsd"
147 | ],
148 | "engines": {
149 | "node": ">=18"
150 | }
151 | },
152 | "node_modules/@esbuild/freebsd-x64": {
153 | "version": "0.24.2",
154 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz",
155 | "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==",
156 | "cpu": [
157 | "x64"
158 | ],
159 | "dev": true,
160 | "license": "MIT",
161 | "optional": true,
162 | "os": [
163 | "freebsd"
164 | ],
165 | "engines": {
166 | "node": ">=18"
167 | }
168 | },
169 | "node_modules/@esbuild/linux-arm": {
170 | "version": "0.24.2",
171 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz",
172 | "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==",
173 | "cpu": [
174 | "arm"
175 | ],
176 | "dev": true,
177 | "license": "MIT",
178 | "optional": true,
179 | "os": [
180 | "linux"
181 | ],
182 | "engines": {
183 | "node": ">=18"
184 | }
185 | },
186 | "node_modules/@esbuild/linux-arm64": {
187 | "version": "0.24.2",
188 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz",
189 | "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==",
190 | "cpu": [
191 | "arm64"
192 | ],
193 | "dev": true,
194 | "license": "MIT",
195 | "optional": true,
196 | "os": [
197 | "linux"
198 | ],
199 | "engines": {
200 | "node": ">=18"
201 | }
202 | },
203 | "node_modules/@esbuild/linux-ia32": {
204 | "version": "0.24.2",
205 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz",
206 | "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==",
207 | "cpu": [
208 | "ia32"
209 | ],
210 | "dev": true,
211 | "license": "MIT",
212 | "optional": true,
213 | "os": [
214 | "linux"
215 | ],
216 | "engines": {
217 | "node": ">=18"
218 | }
219 | },
220 | "node_modules/@esbuild/linux-loong64": {
221 | "version": "0.24.2",
222 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz",
223 | "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==",
224 | "cpu": [
225 | "loong64"
226 | ],
227 | "dev": true,
228 | "license": "MIT",
229 | "optional": true,
230 | "os": [
231 | "linux"
232 | ],
233 | "engines": {
234 | "node": ">=18"
235 | }
236 | },
237 | "node_modules/@esbuild/linux-mips64el": {
238 | "version": "0.24.2",
239 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz",
240 | "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==",
241 | "cpu": [
242 | "mips64el"
243 | ],
244 | "dev": true,
245 | "license": "MIT",
246 | "optional": true,
247 | "os": [
248 | "linux"
249 | ],
250 | "engines": {
251 | "node": ">=18"
252 | }
253 | },
254 | "node_modules/@esbuild/linux-ppc64": {
255 | "version": "0.24.2",
256 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz",
257 | "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==",
258 | "cpu": [
259 | "ppc64"
260 | ],
261 | "dev": true,
262 | "license": "MIT",
263 | "optional": true,
264 | "os": [
265 | "linux"
266 | ],
267 | "engines": {
268 | "node": ">=18"
269 | }
270 | },
271 | "node_modules/@esbuild/linux-riscv64": {
272 | "version": "0.24.2",
273 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz",
274 | "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==",
275 | "cpu": [
276 | "riscv64"
277 | ],
278 | "dev": true,
279 | "license": "MIT",
280 | "optional": true,
281 | "os": [
282 | "linux"
283 | ],
284 | "engines": {
285 | "node": ">=18"
286 | }
287 | },
288 | "node_modules/@esbuild/linux-s390x": {
289 | "version": "0.24.2",
290 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz",
291 | "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==",
292 | "cpu": [
293 | "s390x"
294 | ],
295 | "dev": true,
296 | "license": "MIT",
297 | "optional": true,
298 | "os": [
299 | "linux"
300 | ],
301 | "engines": {
302 | "node": ">=18"
303 | }
304 | },
305 | "node_modules/@esbuild/linux-x64": {
306 | "version": "0.24.2",
307 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz",
308 | "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==",
309 | "cpu": [
310 | "x64"
311 | ],
312 | "dev": true,
313 | "license": "MIT",
314 | "optional": true,
315 | "os": [
316 | "linux"
317 | ],
318 | "engines": {
319 | "node": ">=18"
320 | }
321 | },
322 | "node_modules/@esbuild/netbsd-arm64": {
323 | "version": "0.24.2",
324 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz",
325 | "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==",
326 | "cpu": [
327 | "arm64"
328 | ],
329 | "dev": true,
330 | "license": "MIT",
331 | "optional": true,
332 | "os": [
333 | "netbsd"
334 | ],
335 | "engines": {
336 | "node": ">=18"
337 | }
338 | },
339 | "node_modules/@esbuild/netbsd-x64": {
340 | "version": "0.24.2",
341 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz",
342 | "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==",
343 | "cpu": [
344 | "x64"
345 | ],
346 | "dev": true,
347 | "license": "MIT",
348 | "optional": true,
349 | "os": [
350 | "netbsd"
351 | ],
352 | "engines": {
353 | "node": ">=18"
354 | }
355 | },
356 | "node_modules/@esbuild/openbsd-arm64": {
357 | "version": "0.24.2",
358 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz",
359 | "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==",
360 | "cpu": [
361 | "arm64"
362 | ],
363 | "dev": true,
364 | "license": "MIT",
365 | "optional": true,
366 | "os": [
367 | "openbsd"
368 | ],
369 | "engines": {
370 | "node": ">=18"
371 | }
372 | },
373 | "node_modules/@esbuild/openbsd-x64": {
374 | "version": "0.24.2",
375 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz",
376 | "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==",
377 | "cpu": [
378 | "x64"
379 | ],
380 | "dev": true,
381 | "license": "MIT",
382 | "optional": true,
383 | "os": [
384 | "openbsd"
385 | ],
386 | "engines": {
387 | "node": ">=18"
388 | }
389 | },
390 | "node_modules/@esbuild/sunos-x64": {
391 | "version": "0.24.2",
392 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz",
393 | "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==",
394 | "cpu": [
395 | "x64"
396 | ],
397 | "dev": true,
398 | "license": "MIT",
399 | "optional": true,
400 | "os": [
401 | "sunos"
402 | ],
403 | "engines": {
404 | "node": ">=18"
405 | }
406 | },
407 | "node_modules/@esbuild/win32-arm64": {
408 | "version": "0.24.2",
409 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz",
410 | "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==",
411 | "cpu": [
412 | "arm64"
413 | ],
414 | "dev": true,
415 | "license": "MIT",
416 | "optional": true,
417 | "os": [
418 | "win32"
419 | ],
420 | "engines": {
421 | "node": ">=18"
422 | }
423 | },
424 | "node_modules/@esbuild/win32-ia32": {
425 | "version": "0.24.2",
426 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz",
427 | "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==",
428 | "cpu": [
429 | "ia32"
430 | ],
431 | "dev": true,
432 | "license": "MIT",
433 | "optional": true,
434 | "os": [
435 | "win32"
436 | ],
437 | "engines": {
438 | "node": ">=18"
439 | }
440 | },
441 | "node_modules/@esbuild/win32-x64": {
442 | "version": "0.24.2",
443 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz",
444 | "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==",
445 | "cpu": [
446 | "x64"
447 | ],
448 | "dev": true,
449 | "license": "MIT",
450 | "optional": true,
451 | "os": [
452 | "win32"
453 | ],
454 | "engines": {
455 | "node": ">=18"
456 | }
457 | },
458 | "node_modules/@jridgewell/gen-mapping": {
459 | "version": "0.3.8",
460 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
461 | "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
462 | "dev": true,
463 | "license": "MIT",
464 | "dependencies": {
465 | "@jridgewell/set-array": "^1.2.1",
466 | "@jridgewell/sourcemap-codec": "^1.4.10",
467 | "@jridgewell/trace-mapping": "^0.3.24"
468 | },
469 | "engines": {
470 | "node": ">=6.0.0"
471 | }
472 | },
473 | "node_modules/@jridgewell/resolve-uri": {
474 | "version": "3.1.2",
475 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
476 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
477 | "dev": true,
478 | "license": "MIT",
479 | "engines": {
480 | "node": ">=6.0.0"
481 | }
482 | },
483 | "node_modules/@jridgewell/set-array": {
484 | "version": "1.2.1",
485 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
486 | "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
487 | "dev": true,
488 | "license": "MIT",
489 | "engines": {
490 | "node": ">=6.0.0"
491 | }
492 | },
493 | "node_modules/@jridgewell/sourcemap-codec": {
494 | "version": "1.5.0",
495 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
496 | "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
497 | "dev": true,
498 | "license": "MIT"
499 | },
500 | "node_modules/@jridgewell/trace-mapping": {
501 | "version": "0.3.25",
502 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
503 | "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
504 | "dev": true,
505 | "license": "MIT",
506 | "dependencies": {
507 | "@jridgewell/resolve-uri": "^3.1.0",
508 | "@jridgewell/sourcemap-codec": "^1.4.14"
509 | }
510 | },
511 | "node_modules/@rollup/rollup-android-arm-eabi": {
512 | "version": "4.34.3",
513 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.3.tgz",
514 | "integrity": "sha512-8kq/NjMKkMTGKMPldWihncOl62kgnLYk7cW+/4NCUWfS70/wz4+gQ7rMxMMpZ3dIOP/xw7wKNzIuUnN/H2GfUg==",
515 | "cpu": [
516 | "arm"
517 | ],
518 | "dev": true,
519 | "license": "MIT",
520 | "optional": true,
521 | "os": [
522 | "android"
523 | ]
524 | },
525 | "node_modules/@rollup/rollup-android-arm64": {
526 | "version": "4.34.3",
527 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.3.tgz",
528 | "integrity": "sha512-1PqMHiuRochQ6++SDI7SaRDWJKr/NgAlezBi5nOne6Da6IWJo3hK0TdECBDwd92IUDPG4j/bZmWuwOnomNT8wA==",
529 | "cpu": [
530 | "arm64"
531 | ],
532 | "dev": true,
533 | "license": "MIT",
534 | "optional": true,
535 | "os": [
536 | "android"
537 | ]
538 | },
539 | "node_modules/@rollup/rollup-darwin-arm64": {
540 | "version": "4.34.3",
541 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.3.tgz",
542 | "integrity": "sha512-fqbrykX4mGV3DlCDXhF4OaMGcchd2tmLYxVt3On5oOZWVDFfdEoYAV2alzNChl8OzNaeMAGqm1f7gk7eIw/uDg==",
543 | "cpu": [
544 | "arm64"
545 | ],
546 | "dev": true,
547 | "license": "MIT",
548 | "optional": true,
549 | "os": [
550 | "darwin"
551 | ]
552 | },
553 | "node_modules/@rollup/rollup-darwin-x64": {
554 | "version": "4.34.3",
555 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.3.tgz",
556 | "integrity": "sha512-8Wxrx/KRvMsTyLTbdrMXcVKfpW51cCNW8x7iQD72xSEbjvhCY3b+w83Bea3nQfysTMR7K28esc+ZFITThXm+1w==",
557 | "cpu": [
558 | "x64"
559 | ],
560 | "dev": true,
561 | "license": "MIT",
562 | "optional": true,
563 | "os": [
564 | "darwin"
565 | ]
566 | },
567 | "node_modules/@rollup/rollup-freebsd-arm64": {
568 | "version": "4.34.3",
569 | "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.3.tgz",
570 | "integrity": "sha512-lpBmV2qSiELh+ATQPTjQczt5hvbTLsE0c43Rx4bGxN2VpnAZWy77we7OO62LyOSZNY7CzjMoceRPc+Lt4e9J6A==",
571 | "cpu": [
572 | "arm64"
573 | ],
574 | "dev": true,
575 | "license": "MIT",
576 | "optional": true,
577 | "os": [
578 | "freebsd"
579 | ]
580 | },
581 | "node_modules/@rollup/rollup-freebsd-x64": {
582 | "version": "4.34.3",
583 | "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.3.tgz",
584 | "integrity": "sha512-sNPvBIXpgaYcI6mAeH13GZMXFrrw5mdZVI1M9YQPRG2LpjwL8DSxSIflZoh/B5NEuOi53kxsR/S2GKozK1vDXA==",
585 | "cpu": [
586 | "x64"
587 | ],
588 | "dev": true,
589 | "license": "MIT",
590 | "optional": true,
591 | "os": [
592 | "freebsd"
593 | ]
594 | },
595 | "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
596 | "version": "4.34.3",
597 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.3.tgz",
598 | "integrity": "sha512-MW6N3AoC61OfE1VgnN5O1OW0gt8VTbhx9s/ZEPLBM11wEdHjeilPzOxVmmsrx5YmejpGPvez8QwGGvMU+pGxpw==",
599 | "cpu": [
600 | "arm"
601 | ],
602 | "dev": true,
603 | "license": "MIT",
604 | "optional": true,
605 | "os": [
606 | "linux"
607 | ]
608 | },
609 | "node_modules/@rollup/rollup-linux-arm-musleabihf": {
610 | "version": "4.34.3",
611 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.3.tgz",
612 | "integrity": "sha512-2SQkhr5xvatYq0/+H6qyW0zvrQz9LM4lxGkpWURLoQX5+yP8MsERh4uWmxFohOvwCP6l/+wgiHZ1qVwLDc7Qmw==",
613 | "cpu": [
614 | "arm"
615 | ],
616 | "dev": true,
617 | "license": "MIT",
618 | "optional": true,
619 | "os": [
620 | "linux"
621 | ]
622 | },
623 | "node_modules/@rollup/rollup-linux-arm64-gnu": {
624 | "version": "4.34.3",
625 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.3.tgz",
626 | "integrity": "sha512-R3JLYt8YoRwKI5shJsovLpcR6pwIMui/MGG/MmxZ1DYI3iRSKI4qcYrvYgDf4Ss2oCR3RL3F3dYK7uAGQgMIuQ==",
627 | "cpu": [
628 | "arm64"
629 | ],
630 | "dev": true,
631 | "license": "MIT",
632 | "optional": true,
633 | "os": [
634 | "linux"
635 | ]
636 | },
637 | "node_modules/@rollup/rollup-linux-arm64-musl": {
638 | "version": "4.34.3",
639 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.3.tgz",
640 | "integrity": "sha512-4XQhG8v/t3S7Rxs7rmFUuM6j09hVrTArzONS3fUZ6oBRSN/ps9IPQjVhp62P0W3KhqJdQADo/MRlYRMdgxr/3w==",
641 | "cpu": [
642 | "arm64"
643 | ],
644 | "dev": true,
645 | "license": "MIT",
646 | "optional": true,
647 | "os": [
648 | "linux"
649 | ]
650 | },
651 | "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
652 | "version": "4.34.3",
653 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.3.tgz",
654 | "integrity": "sha512-QlW1jCUZ1LHUIYCAK2FciVw1ptHsxzApYVi05q7bz2A8oNE8QxQ85NhM4arLxkAlcnS42t4avJbSfzSQwbIaKg==",
655 | "cpu": [
656 | "loong64"
657 | ],
658 | "dev": true,
659 | "license": "MIT",
660 | "optional": true,
661 | "os": [
662 | "linux"
663 | ]
664 | },
665 | "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
666 | "version": "4.34.3",
667 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.3.tgz",
668 | "integrity": "sha512-kMbLToizVeCcN69+nnm20Dh0hrRIAjgaaL+Wh0gWZcNt8e542d2FUGtsyuNsHVNNF3gqTJrpzUGIdwMGLEUM7g==",
669 | "cpu": [
670 | "ppc64"
671 | ],
672 | "dev": true,
673 | "license": "MIT",
674 | "optional": true,
675 | "os": [
676 | "linux"
677 | ]
678 | },
679 | "node_modules/@rollup/rollup-linux-riscv64-gnu": {
680 | "version": "4.34.3",
681 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.3.tgz",
682 | "integrity": "sha512-YgD0DnZ3CHtvXRH8rzjVSxwI0kMTr0RQt3o1N92RwxGdx7YejzbBO0ELlSU48DP96u1gYYVWfUhDRyaGNqJqJg==",
683 | "cpu": [
684 | "riscv64"
685 | ],
686 | "dev": true,
687 | "license": "MIT",
688 | "optional": true,
689 | "os": [
690 | "linux"
691 | ]
692 | },
693 | "node_modules/@rollup/rollup-linux-s390x-gnu": {
694 | "version": "4.34.3",
695 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.3.tgz",
696 | "integrity": "sha512-dIOoOz8altjp6UjAi3U9EW99s8nta4gzi52FeI45GlPyrUH4QixUoBMH9VsVjt+9A2RiZBWyjYNHlJ/HmJOBCQ==",
697 | "cpu": [
698 | "s390x"
699 | ],
700 | "dev": true,
701 | "license": "MIT",
702 | "optional": true,
703 | "os": [
704 | "linux"
705 | ]
706 | },
707 | "node_modules/@rollup/rollup-linux-x64-gnu": {
708 | "version": "4.34.3",
709 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.3.tgz",
710 | "integrity": "sha512-lOyG3aF4FTKrhpzXfMmBXgeKUUXdAWmP2zSNf8HTAXPqZay6QYT26l64hVizBjq+hJx3pl0DTEyvPi9sTA6VGA==",
711 | "cpu": [
712 | "x64"
713 | ],
714 | "dev": true,
715 | "license": "MIT",
716 | "optional": true,
717 | "os": [
718 | "linux"
719 | ]
720 | },
721 | "node_modules/@rollup/rollup-linux-x64-musl": {
722 | "version": "4.34.3",
723 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.3.tgz",
724 | "integrity": "sha512-usztyYLu2i+mYzzOjqHZTaRXbUOqw3P6laNUh1zcqxbPH1P2Tz/QdJJCQSnGxCtsRQeuU2bCyraGMtMumC46rw==",
725 | "cpu": [
726 | "x64"
727 | ],
728 | "dev": true,
729 | "license": "MIT",
730 | "optional": true,
731 | "os": [
732 | "linux"
733 | ]
734 | },
735 | "node_modules/@rollup/rollup-win32-arm64-msvc": {
736 | "version": "4.34.3",
737 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.3.tgz",
738 | "integrity": "sha512-ojFOKaz/ZyalIrizdBq2vyc2f0kFbJahEznfZlxdB6pF9Do6++i1zS5Gy6QLf8D7/S57MHrmBLur6AeRYeQXSA==",
739 | "cpu": [
740 | "arm64"
741 | ],
742 | "dev": true,
743 | "license": "MIT",
744 | "optional": true,
745 | "os": [
746 | "win32"
747 | ]
748 | },
749 | "node_modules/@rollup/rollup-win32-ia32-msvc": {
750 | "version": "4.34.3",
751 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.3.tgz",
752 | "integrity": "sha512-K/V97GMbNa+Da9mGcZqmSl+DlJmWfHXTuI9V8oB2evGsQUtszCl67+OxWjBKpeOnYwox9Jpmt/J6VhpeRCYqow==",
753 | "cpu": [
754 | "ia32"
755 | ],
756 | "dev": true,
757 | "license": "MIT",
758 | "optional": true,
759 | "os": [
760 | "win32"
761 | ]
762 | },
763 | "node_modules/@rollup/rollup-win32-x64-msvc": {
764 | "version": "4.34.3",
765 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.3.tgz",
766 | "integrity": "sha512-CUypcYP31Q8O04myV6NKGzk9GVXslO5EJNfmARNSzLF2A+5rmZUlDJ4et6eoJaZgBT9wrC2p4JZH04Vkic8HdQ==",
767 | "cpu": [
768 | "x64"
769 | ],
770 | "dev": true,
771 | "license": "MIT",
772 | "optional": true,
773 | "os": [
774 | "win32"
775 | ]
776 | },
777 | "node_modules/@sveltejs/vite-plugin-svelte": {
778 | "version": "5.0.3",
779 | "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-5.0.3.tgz",
780 | "integrity": "sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw==",
781 | "dev": true,
782 | "license": "MIT",
783 | "dependencies": {
784 | "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1",
785 | "debug": "^4.4.0",
786 | "deepmerge": "^4.3.1",
787 | "kleur": "^4.1.5",
788 | "magic-string": "^0.30.15",
789 | "vitefu": "^1.0.4"
790 | },
791 | "engines": {
792 | "node": "^18.0.0 || ^20.0.0 || >=22"
793 | },
794 | "peerDependencies": {
795 | "svelte": "^5.0.0",
796 | "vite": "^6.0.0"
797 | }
798 | },
799 | "node_modules/@sveltejs/vite-plugin-svelte-inspector": {
800 | "version": "4.0.1",
801 | "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-4.0.1.tgz",
802 | "integrity": "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==",
803 | "dev": true,
804 | "license": "MIT",
805 | "dependencies": {
806 | "debug": "^4.3.7"
807 | },
808 | "engines": {
809 | "node": "^18.0.0 || ^20.0.0 || >=22"
810 | },
811 | "peerDependencies": {
812 | "@sveltejs/vite-plugin-svelte": "^5.0.0",
813 | "svelte": "^5.0.0",
814 | "vite": "^6.0.0"
815 | }
816 | },
817 | "node_modules/@tsconfig/svelte": {
818 | "version": "5.0.4",
819 | "resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-5.0.4.tgz",
820 | "integrity": "sha512-BV9NplVgLmSi4mwKzD8BD/NQ8erOY/nUE/GpgWe2ckx+wIQF5RyRirn/QsSSCPeulVpc3RA/iJt6DpfTIZps0Q==",
821 | "dev": true,
822 | "license": "MIT"
823 | },
824 | "node_modules/@types/estree": {
825 | "version": "1.0.6",
826 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
827 | "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
828 | "dev": true,
829 | "license": "MIT"
830 | },
831 | "node_modules/acorn": {
832 | "version": "8.14.0",
833 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
834 | "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
835 | "dev": true,
836 | "license": "MIT",
837 | "bin": {
838 | "acorn": "bin/acorn"
839 | },
840 | "engines": {
841 | "node": ">=0.4.0"
842 | }
843 | },
844 | "node_modules/acorn-typescript": {
845 | "version": "1.4.13",
846 | "resolved": "https://registry.npmjs.org/acorn-typescript/-/acorn-typescript-1.4.13.tgz",
847 | "integrity": "sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==",
848 | "dev": true,
849 | "license": "MIT",
850 | "peerDependencies": {
851 | "acorn": ">=8.9.0"
852 | }
853 | },
854 | "node_modules/aria-query": {
855 | "version": "5.3.2",
856 | "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
857 | "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
858 | "dev": true,
859 | "license": "Apache-2.0",
860 | "engines": {
861 | "node": ">= 0.4"
862 | }
863 | },
864 | "node_modules/axobject-query": {
865 | "version": "4.1.0",
866 | "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
867 | "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
868 | "dev": true,
869 | "license": "Apache-2.0",
870 | "engines": {
871 | "node": ">= 0.4"
872 | }
873 | },
874 | "node_modules/chokidar": {
875 | "version": "4.0.3",
876 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
877 | "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
878 | "dev": true,
879 | "license": "MIT",
880 | "dependencies": {
881 | "readdirp": "^4.0.1"
882 | },
883 | "engines": {
884 | "node": ">= 14.16.0"
885 | },
886 | "funding": {
887 | "url": "https://paulmillr.com/funding/"
888 | }
889 | },
890 | "node_modules/clsx": {
891 | "version": "2.1.1",
892 | "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
893 | "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
894 | "dev": true,
895 | "license": "MIT",
896 | "engines": {
897 | "node": ">=6"
898 | }
899 | },
900 | "node_modules/debug": {
901 | "version": "4.4.0",
902 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
903 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
904 | "dev": true,
905 | "license": "MIT",
906 | "dependencies": {
907 | "ms": "^2.1.3"
908 | },
909 | "engines": {
910 | "node": ">=6.0"
911 | },
912 | "peerDependenciesMeta": {
913 | "supports-color": {
914 | "optional": true
915 | }
916 | }
917 | },
918 | "node_modules/deepmerge": {
919 | "version": "4.3.1",
920 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
921 | "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
922 | "dev": true,
923 | "license": "MIT",
924 | "engines": {
925 | "node": ">=0.10.0"
926 | }
927 | },
928 | "node_modules/esbuild": {
929 | "version": "0.24.2",
930 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz",
931 | "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==",
932 | "dev": true,
933 | "hasInstallScript": true,
934 | "license": "MIT",
935 | "bin": {
936 | "esbuild": "bin/esbuild"
937 | },
938 | "engines": {
939 | "node": ">=18"
940 | },
941 | "optionalDependencies": {
942 | "@esbuild/aix-ppc64": "0.24.2",
943 | "@esbuild/android-arm": "0.24.2",
944 | "@esbuild/android-arm64": "0.24.2",
945 | "@esbuild/android-x64": "0.24.2",
946 | "@esbuild/darwin-arm64": "0.24.2",
947 | "@esbuild/darwin-x64": "0.24.2",
948 | "@esbuild/freebsd-arm64": "0.24.2",
949 | "@esbuild/freebsd-x64": "0.24.2",
950 | "@esbuild/linux-arm": "0.24.2",
951 | "@esbuild/linux-arm64": "0.24.2",
952 | "@esbuild/linux-ia32": "0.24.2",
953 | "@esbuild/linux-loong64": "0.24.2",
954 | "@esbuild/linux-mips64el": "0.24.2",
955 | "@esbuild/linux-ppc64": "0.24.2",
956 | "@esbuild/linux-riscv64": "0.24.2",
957 | "@esbuild/linux-s390x": "0.24.2",
958 | "@esbuild/linux-x64": "0.24.2",
959 | "@esbuild/netbsd-arm64": "0.24.2",
960 | "@esbuild/netbsd-x64": "0.24.2",
961 | "@esbuild/openbsd-arm64": "0.24.2",
962 | "@esbuild/openbsd-x64": "0.24.2",
963 | "@esbuild/sunos-x64": "0.24.2",
964 | "@esbuild/win32-arm64": "0.24.2",
965 | "@esbuild/win32-ia32": "0.24.2",
966 | "@esbuild/win32-x64": "0.24.2"
967 | }
968 | },
969 | "node_modules/esm-env": {
970 | "version": "1.2.2",
971 | "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
972 | "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==",
973 | "dev": true,
974 | "license": "MIT"
975 | },
976 | "node_modules/esrap": {
977 | "version": "1.4.3",
978 | "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.4.3.tgz",
979 | "integrity": "sha512-Xddc1RsoFJ4z9nR7W7BFaEPIp4UXoeQ0+077UdWLxbafMQFyU79sQJMk7kxNgRwQ9/aVgaKacCHC2pUACGwmYw==",
980 | "dev": true,
981 | "license": "MIT",
982 | "dependencies": {
983 | "@jridgewell/sourcemap-codec": "^1.4.15"
984 | }
985 | },
986 | "node_modules/fdir": {
987 | "version": "6.4.3",
988 | "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz",
989 | "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
990 | "dev": true,
991 | "license": "MIT",
992 | "peerDependencies": {
993 | "picomatch": "^3 || ^4"
994 | },
995 | "peerDependenciesMeta": {
996 | "picomatch": {
997 | "optional": true
998 | }
999 | }
1000 | },
1001 | "node_modules/fsevents": {
1002 | "version": "2.3.3",
1003 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
1004 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
1005 | "dev": true,
1006 | "hasInstallScript": true,
1007 | "license": "MIT",
1008 | "optional": true,
1009 | "os": [
1010 | "darwin"
1011 | ],
1012 | "engines": {
1013 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
1014 | }
1015 | },
1016 | "node_modules/is-reference": {
1017 | "version": "3.0.3",
1018 | "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
1019 | "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
1020 | "dev": true,
1021 | "license": "MIT",
1022 | "dependencies": {
1023 | "@types/estree": "^1.0.6"
1024 | }
1025 | },
1026 | "node_modules/kleur": {
1027 | "version": "4.1.5",
1028 | "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
1029 | "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
1030 | "dev": true,
1031 | "license": "MIT",
1032 | "engines": {
1033 | "node": ">=6"
1034 | }
1035 | },
1036 | "node_modules/locate-character": {
1037 | "version": "3.0.0",
1038 | "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
1039 | "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
1040 | "dev": true,
1041 | "license": "MIT"
1042 | },
1043 | "node_modules/magic-string": {
1044 | "version": "0.30.17",
1045 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
1046 | "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
1047 | "dev": true,
1048 | "license": "MIT",
1049 | "dependencies": {
1050 | "@jridgewell/sourcemap-codec": "^1.5.0"
1051 | }
1052 | },
1053 | "node_modules/mri": {
1054 | "version": "1.2.0",
1055 | "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
1056 | "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==",
1057 | "dev": true,
1058 | "license": "MIT",
1059 | "engines": {
1060 | "node": ">=4"
1061 | }
1062 | },
1063 | "node_modules/ms": {
1064 | "version": "2.1.3",
1065 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1066 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1067 | "dev": true,
1068 | "license": "MIT"
1069 | },
1070 | "node_modules/nanoid": {
1071 | "version": "3.3.8",
1072 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
1073 | "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
1074 | "dev": true,
1075 | "funding": [
1076 | {
1077 | "type": "github",
1078 | "url": "https://github.com/sponsors/ai"
1079 | }
1080 | ],
1081 | "license": "MIT",
1082 | "bin": {
1083 | "nanoid": "bin/nanoid.cjs"
1084 | },
1085 | "engines": {
1086 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1087 | }
1088 | },
1089 | "node_modules/picocolors": {
1090 | "version": "1.1.1",
1091 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
1092 | "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
1093 | "dev": true,
1094 | "license": "ISC"
1095 | },
1096 | "node_modules/postcss": {
1097 | "version": "8.5.1",
1098 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz",
1099 | "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==",
1100 | "dev": true,
1101 | "funding": [
1102 | {
1103 | "type": "opencollective",
1104 | "url": "https://opencollective.com/postcss/"
1105 | },
1106 | {
1107 | "type": "tidelift",
1108 | "url": "https://tidelift.com/funding/github/npm/postcss"
1109 | },
1110 | {
1111 | "type": "github",
1112 | "url": "https://github.com/sponsors/ai"
1113 | }
1114 | ],
1115 | "license": "MIT",
1116 | "dependencies": {
1117 | "nanoid": "^3.3.8",
1118 | "picocolors": "^1.1.1",
1119 | "source-map-js": "^1.2.1"
1120 | },
1121 | "engines": {
1122 | "node": "^10 || ^12 || >=14"
1123 | }
1124 | },
1125 | "node_modules/readdirp": {
1126 | "version": "4.1.1",
1127 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz",
1128 | "integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==",
1129 | "dev": true,
1130 | "license": "MIT",
1131 | "engines": {
1132 | "node": ">= 14.18.0"
1133 | },
1134 | "funding": {
1135 | "type": "individual",
1136 | "url": "https://paulmillr.com/funding/"
1137 | }
1138 | },
1139 | "node_modules/rollup": {
1140 | "version": "4.34.3",
1141 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.3.tgz",
1142 | "integrity": "sha512-ORCtU0UBJyiAIn9m0llUXJXAswG/68pZptCrqxHG7//Z2DDzAUeyyY5hqf4XrsGlUxscMr9GkQ2QI7KTLqeyPw==",
1143 | "dev": true,
1144 | "license": "MIT",
1145 | "dependencies": {
1146 | "@types/estree": "1.0.6"
1147 | },
1148 | "bin": {
1149 | "rollup": "dist/bin/rollup"
1150 | },
1151 | "engines": {
1152 | "node": ">=18.0.0",
1153 | "npm": ">=8.0.0"
1154 | },
1155 | "optionalDependencies": {
1156 | "@rollup/rollup-android-arm-eabi": "4.34.3",
1157 | "@rollup/rollup-android-arm64": "4.34.3",
1158 | "@rollup/rollup-darwin-arm64": "4.34.3",
1159 | "@rollup/rollup-darwin-x64": "4.34.3",
1160 | "@rollup/rollup-freebsd-arm64": "4.34.3",
1161 | "@rollup/rollup-freebsd-x64": "4.34.3",
1162 | "@rollup/rollup-linux-arm-gnueabihf": "4.34.3",
1163 | "@rollup/rollup-linux-arm-musleabihf": "4.34.3",
1164 | "@rollup/rollup-linux-arm64-gnu": "4.34.3",
1165 | "@rollup/rollup-linux-arm64-musl": "4.34.3",
1166 | "@rollup/rollup-linux-loongarch64-gnu": "4.34.3",
1167 | "@rollup/rollup-linux-powerpc64le-gnu": "4.34.3",
1168 | "@rollup/rollup-linux-riscv64-gnu": "4.34.3",
1169 | "@rollup/rollup-linux-s390x-gnu": "4.34.3",
1170 | "@rollup/rollup-linux-x64-gnu": "4.34.3",
1171 | "@rollup/rollup-linux-x64-musl": "4.34.3",
1172 | "@rollup/rollup-win32-arm64-msvc": "4.34.3",
1173 | "@rollup/rollup-win32-ia32-msvc": "4.34.3",
1174 | "@rollup/rollup-win32-x64-msvc": "4.34.3",
1175 | "fsevents": "~2.3.2"
1176 | }
1177 | },
1178 | "node_modules/sade": {
1179 | "version": "1.8.1",
1180 | "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz",
1181 | "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==",
1182 | "dev": true,
1183 | "license": "MIT",
1184 | "dependencies": {
1185 | "mri": "^1.1.0"
1186 | },
1187 | "engines": {
1188 | "node": ">=6"
1189 | }
1190 | },
1191 | "node_modules/source-map-js": {
1192 | "version": "1.2.1",
1193 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
1194 | "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
1195 | "dev": true,
1196 | "license": "BSD-3-Clause",
1197 | "engines": {
1198 | "node": ">=0.10.0"
1199 | }
1200 | },
1201 | "node_modules/svelte": {
1202 | "version": "5.19.7",
1203 | "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.19.7.tgz",
1204 | "integrity": "sha512-I0UUp2MpB5gF8aqHJVklOcRcoLgQNnBolSwLMMqDepE9gVwmGeYBmJp1/obzae72QpxdPIymA4AunIm2x70LBg==",
1205 | "dev": true,
1206 | "license": "MIT",
1207 | "dependencies": {
1208 | "@ampproject/remapping": "^2.3.0",
1209 | "@jridgewell/sourcemap-codec": "^1.5.0",
1210 | "@types/estree": "^1.0.5",
1211 | "acorn": "^8.12.1",
1212 | "acorn-typescript": "^1.4.13",
1213 | "aria-query": "^5.3.1",
1214 | "axobject-query": "^4.1.0",
1215 | "clsx": "^2.1.1",
1216 | "esm-env": "^1.2.1",
1217 | "esrap": "^1.4.3",
1218 | "is-reference": "^3.0.3",
1219 | "locate-character": "^3.0.0",
1220 | "magic-string": "^0.30.11",
1221 | "zimmerframe": "^1.1.2"
1222 | },
1223 | "engines": {
1224 | "node": ">=18"
1225 | }
1226 | },
1227 | "node_modules/svelte-check": {
1228 | "version": "4.1.4",
1229 | "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.1.4.tgz",
1230 | "integrity": "sha512-v0j7yLbT29MezzaQJPEDwksybTE2Ups9rUxEXy92T06TiA0cbqcO8wAOwNUVkFW6B0hsYHA+oAX3BS8b/2oHtw==",
1231 | "dev": true,
1232 | "license": "MIT",
1233 | "dependencies": {
1234 | "@jridgewell/trace-mapping": "^0.3.25",
1235 | "chokidar": "^4.0.1",
1236 | "fdir": "^6.2.0",
1237 | "picocolors": "^1.0.0",
1238 | "sade": "^1.7.4"
1239 | },
1240 | "bin": {
1241 | "svelte-check": "bin/svelte-check"
1242 | },
1243 | "engines": {
1244 | "node": ">= 18.0.0"
1245 | },
1246 | "peerDependencies": {
1247 | "svelte": "^4.0.0 || ^5.0.0-next.0",
1248 | "typescript": ">=5.0.0"
1249 | }
1250 | },
1251 | "node_modules/typescript": {
1252 | "version": "5.7.3",
1253 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
1254 | "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
1255 | "dev": true,
1256 | "license": "Apache-2.0",
1257 | "bin": {
1258 | "tsc": "bin/tsc",
1259 | "tsserver": "bin/tsserver"
1260 | },
1261 | "engines": {
1262 | "node": ">=14.17"
1263 | }
1264 | },
1265 | "node_modules/vite": {
1266 | "version": "6.1.0",
1267 | "resolved": "https://registry.npmjs.org/vite/-/vite-6.1.0.tgz",
1268 | "integrity": "sha512-RjjMipCKVoR4hVfPY6GQTgveinjNuyLw+qruksLDvA5ktI1150VmcMBKmQaEWJhg/j6Uaf6dNCNA0AfdzUb/hQ==",
1269 | "dev": true,
1270 | "license": "MIT",
1271 | "dependencies": {
1272 | "esbuild": "^0.24.2",
1273 | "postcss": "^8.5.1",
1274 | "rollup": "^4.30.1"
1275 | },
1276 | "bin": {
1277 | "vite": "bin/vite.js"
1278 | },
1279 | "engines": {
1280 | "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
1281 | },
1282 | "funding": {
1283 | "url": "https://github.com/vitejs/vite?sponsor=1"
1284 | },
1285 | "optionalDependencies": {
1286 | "fsevents": "~2.3.3"
1287 | },
1288 | "peerDependencies": {
1289 | "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
1290 | "jiti": ">=1.21.0",
1291 | "less": "*",
1292 | "lightningcss": "^1.21.0",
1293 | "sass": "*",
1294 | "sass-embedded": "*",
1295 | "stylus": "*",
1296 | "sugarss": "*",
1297 | "terser": "^5.16.0",
1298 | "tsx": "^4.8.1",
1299 | "yaml": "^2.4.2"
1300 | },
1301 | "peerDependenciesMeta": {
1302 | "@types/node": {
1303 | "optional": true
1304 | },
1305 | "jiti": {
1306 | "optional": true
1307 | },
1308 | "less": {
1309 | "optional": true
1310 | },
1311 | "lightningcss": {
1312 | "optional": true
1313 | },
1314 | "sass": {
1315 | "optional": true
1316 | },
1317 | "sass-embedded": {
1318 | "optional": true
1319 | },
1320 | "stylus": {
1321 | "optional": true
1322 | },
1323 | "sugarss": {
1324 | "optional": true
1325 | },
1326 | "terser": {
1327 | "optional": true
1328 | },
1329 | "tsx": {
1330 | "optional": true
1331 | },
1332 | "yaml": {
1333 | "optional": true
1334 | }
1335 | }
1336 | },
1337 | "node_modules/vitefu": {
1338 | "version": "1.0.5",
1339 | "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.5.tgz",
1340 | "integrity": "sha512-h4Vflt9gxODPFNGPwp4zAMZRpZR7eslzwH2c5hn5kNZ5rhnKyRJ50U+yGCdc2IRaBs8O4haIgLNGrV5CrpMsCA==",
1341 | "dev": true,
1342 | "license": "MIT",
1343 | "workspaces": [
1344 | "tests/deps/*",
1345 | "tests/projects/*"
1346 | ],
1347 | "peerDependencies": {
1348 | "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0"
1349 | },
1350 | "peerDependenciesMeta": {
1351 | "vite": {
1352 | "optional": true
1353 | }
1354 | }
1355 | },
1356 | "node_modules/zimmerframe": {
1357 | "version": "1.1.2",
1358 | "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz",
1359 | "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==",
1360 | "dev": true,
1361 | "license": "MIT"
1362 | }
1363 | }
1364 | }
1365 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "inspect",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "preview": "vite preview",
10 | "check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json"
11 | },
12 | "devDependencies": {
13 | "@sveltejs/vite-plugin-svelte": "^5.0.3",
14 | "@tsconfig/svelte": "^5.0.4",
15 | "svelte": "^5.19.6",
16 | "svelte-check": "^4.1.4",
17 | "typescript": "~5.7.2",
18 | "vite": "^6.1.0"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/frontend/public/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abiosoft/caddy-inspect/96cdb1dfb122f79913d60ecb34030d302a4f4ec1/frontend/public/assets/favicon.png
--------------------------------------------------------------------------------
/frontend/public/assets/logo-dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/frontend/public/assets/logo-light.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/frontend/src/App.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/frontend/src/app.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --bg-color: #ffffff;
3 | --text-color: #000000;
4 | --border-color: #ddd;
5 | --button-bg-color: #007bff;
6 | --button-hover-bg-color: #0056b3;
7 | --button-active-bg-color: #004085;
8 | --button-danger-color: #c73636;
9 | --button-hover-danger-color: #a62d2d;
10 | --calm-color: #7e7e7e;
11 | --input-focus-border-color: #007bff;
12 | --input-focus-shadow-color: rgba(0, 123, 255, 0.5);
13 | --snippet-highlight-color: rgba(150, 150, 150, 0.5);
14 | }
15 |
16 | .logo {
17 | content: url("/assets/logo-light.svg");
18 | height: 30px;
19 | margin-right: 5px;
20 | }
21 |
22 | @media (prefers-color-scheme: dark) {
23 | :root {
24 | --bg-color: #121212;
25 | --text-color: #ffffff;
26 | --border-color: #333;
27 | --button-bg-color: #1a73e8;
28 | --button-hover-bg-color: #135ba1;
29 | --button-active-bg-color: #0e447a;
30 | --button-danger-color: #a0222f;
31 | --button-hover-danger-color: #881c28;
32 | --calm-color: #7e7e7e;
33 | --input-focus-border-color: #1a73e8;
34 | --input-focus-shadow-color: rgba(26, 115, 232, 0.5);
35 | --snippet-highlight-color: rgba(150, 150, 150, 0.5);
36 | }
37 |
38 | .logo {
39 | content: url("/assets/logo-dark.svg");
40 | }
41 |
42 | }
43 |
44 | body {
45 | font-family: Arial, sans-serif;
46 | margin: 0;
47 | padding: 0;
48 | box-sizing: border-box;
49 | background-color: var(--bg-color);
50 | color: var(--text-color);
51 | }
52 |
53 | .inspect {
54 | padding: 10px;
55 | }
56 |
57 | button {
58 | padding: 10px 20px;
59 | font-size: 16px;
60 | color: white;
61 | background-color: var(--button-bg-color);
62 | border: none;
63 | border-radius: 4px;
64 | cursor: pointer;
65 | }
66 |
67 | button:hover {
68 | background-color: var(--button-hover-bg-color);
69 | }
70 |
71 | button:active {
72 | background-color: var(--button-active-bg-color);
73 | transform: translateY(1px);
74 | }
75 |
76 | button.danger {
77 | background-color: var(--button-danger-color);
78 | }
79 |
80 | button.danger:hover {
81 | background-color: var(--button-hover-danger-color);
82 | }
83 |
84 |
85 | input[type="text"] {
86 | width: 100%;
87 | padding: 10px;
88 | font-size: 14px;
89 | border: 1px solid var(--border-color);
90 | border-radius: 4px;
91 | background-color: var(--textarea-bg-color);
92 | color: var(--text-color);
93 | }
94 |
95 | input[type="text"]:focus {
96 | outline: none;
97 | border-color: var(--input-focus-border-color);
98 | box-shadow: 0 0 4px var(--input-focus-shadow-color);
99 | }
100 |
101 | .loading {
102 | display: flex;
103 | justify-content: center;
104 | align-items: center;
105 | height: 30vh;
106 | font-size: 20px;
107 | color: var(--text-color);
108 | }
109 |
--------------------------------------------------------------------------------
/frontend/src/lib/Node.svelte:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
39 |
40 | {#if hasChildren && isOpen}
41 |
42 | {#if nodeIsArray}
43 | {#each node as node}
44 |
45 | {/each}
46 | {:else}
47 | {#each nodeEntries as [key, node]}
48 |
49 | {/each}
50 | {/if}
51 |
52 | {/if}
53 |
54 |
55 |
108 |
--------------------------------------------------------------------------------
/frontend/src/lib/Root.svelte:
--------------------------------------------------------------------------------
1 |
126 |
127 |
128 |
131 |
132 | {#if hasData}
133 |
134 |
135 | ⏵
140 |
141 |
142 | {#if !hasResponse}
143 |
144 | ⏭
149 |
150 |
151 | {/if}
152 |
153 | ⏹
159 |
160 |
161 |
162 |
163 | {#if hasSourceFile}
164 |
165 | {sourceFile}:{sourceLine}
166 |
167 |
168 | {#each sources as line, i}
169 |
175 |
176 | {#if i + sourceLineStart == sourceLine}
177 | {#if hasResponse}
178 | ↑
179 | {:else}
180 | ↓
181 | {/if}
182 | ◉
183 | {/if}
184 |
185 | {i + sourceLineStart}
188 | {line}
189 |
190 | {:else}
191 |
192 | {/each}
193 |
194 | {/if}
195 |
196 | {#each treeData as [key, node]}
197 |
198 | {/each}
199 |
200 | {:else}
201 |
Waiting for request...
202 | {/if}
203 |
204 |
205 |
310 |
--------------------------------------------------------------------------------
/frontend/src/main.ts:
--------------------------------------------------------------------------------
1 | import { mount } from 'svelte'
2 | import './app.css'
3 | import App from './App.svelte'
4 |
5 | const app = mount(App, {
6 | target: document.getElementById('app')!,
7 | })
8 |
9 | export default app
10 |
--------------------------------------------------------------------------------
/frontend/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/frontend/svelte.config.js:
--------------------------------------------------------------------------------
1 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
2 |
3 | export default {
4 | // Consult https://svelte.dev/docs#compile-time-svelte-preprocess
5 | // for more information about preprocessors
6 | preprocess: vitePreprocess(),
7 | }
8 |
--------------------------------------------------------------------------------
/frontend/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@tsconfig/svelte/tsconfig.json",
3 | "compilerOptions": {
4 | "target": "ESNext",
5 | "useDefineForClassFields": true,
6 | "module": "ESNext",
7 | "resolveJsonModule": true,
8 | /**
9 | * Typecheck JS in `.svelte` and `.js` files by default.
10 | * Disable checkJs if you'd like to use dynamic types in JS.
11 | * Note that setting allowJs false does not prevent the use
12 | * of JS in `.svelte` files.
13 | */
14 | "allowJs": true,
15 | "checkJs": true,
16 | "isolatedModules": true,
17 | "moduleDetection": "force"
18 | },
19 | "include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
20 | }
21 |
--------------------------------------------------------------------------------
/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | { "path": "./tsconfig.app.json" },
5 | { "path": "./tsconfig.node.json" }
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/frontend/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4 | "target": "ES2022",
5 | "lib": ["ES2023"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "isolatedModules": true,
13 | "moduleDetection": "force",
14 | "noEmit": true,
15 |
16 | /* Linting */
17 | "strict": true,
18 | "noUnusedLocals": true,
19 | "noUnusedParameters": true,
20 | "noFallthroughCasesInSwitch": true,
21 | "noUncheckedSideEffectImports": true
22 | },
23 | "include": ["vite.config.ts"]
24 | }
25 |
--------------------------------------------------------------------------------
/frontend/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import { svelte } from '@sveltejs/vite-plugin-svelte'
3 |
4 | // https://vite.dev/config/
5 | export default defineConfig({
6 | plugins: [svelte()],
7 | server: {
8 | host: '0.0.0.0',
9 | }
10 | })
11 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/abiosoft/caddy-inspect
2 |
3 | go 1.22.3
4 |
5 | toolchain go1.23.4
6 |
7 | require github.com/caddyserver/caddy/v2 v2.9.1
8 |
9 | require (
10 | dario.cat/mergo v1.0.1 // indirect
11 | filippo.io/edwards25519 v1.1.0 // indirect
12 | github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
13 | github.com/Masterminds/goutils v1.1.1 // indirect
14 | github.com/Masterminds/semver/v3 v3.3.0 // indirect
15 | github.com/Masterminds/sprig/v3 v3.3.0 // indirect
16 | github.com/Microsoft/go-winio v0.6.0 // indirect
17 | github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
18 | github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect
19 | github.com/beorn7/perks v1.0.1 // indirect
20 | github.com/caddyserver/certmagic v0.21.6 // indirect
21 | github.com/caddyserver/zerossl v0.1.3 // indirect
22 | github.com/cespare/xxhash v1.1.0 // indirect
23 | github.com/cespare/xxhash/v2 v2.3.0 // indirect
24 | github.com/chzyer/readline v1.5.1 // indirect
25 | github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
26 | github.com/dgraph-io/badger v1.6.2 // indirect
27 | github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
28 | github.com/dgraph-io/ristretto v0.1.0 // indirect
29 | github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
30 | github.com/dustin/go-humanize v1.0.1 // indirect
31 | github.com/francoispqt/gojay v1.2.13 // indirect
32 | github.com/go-jose/go-jose/v3 v3.0.3 // indirect
33 | github.com/go-kit/kit v0.13.0 // indirect
34 | github.com/go-kit/log v0.2.1 // indirect
35 | github.com/go-logfmt/logfmt v0.6.0 // indirect
36 | github.com/go-sql-driver/mysql v1.7.1 // indirect
37 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
38 | github.com/golang/glog v1.2.2 // indirect
39 | github.com/golang/protobuf v1.5.4 // indirect
40 | github.com/golang/snappy v0.0.4 // indirect
41 | github.com/google/cel-go v0.21.0 // indirect
42 | github.com/google/pprof v0.0.0-20231212022811-ec68065c825e // indirect
43 | github.com/google/uuid v1.6.0 // indirect
44 | github.com/huandu/xstrings v1.5.0 // indirect
45 | github.com/inconshreveable/mousetrap v1.1.0 // indirect
46 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect
47 | github.com/jackc/pgconn v1.14.3 // indirect
48 | github.com/jackc/pgio v1.0.0 // indirect
49 | github.com/jackc/pgpassfile v1.0.0 // indirect
50 | github.com/jackc/pgproto3/v2 v2.3.3 // indirect
51 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
52 | github.com/jackc/pgtype v1.14.0 // indirect
53 | github.com/jackc/pgx/v4 v4.18.3 // indirect
54 | github.com/klauspost/compress v1.17.11 // indirect
55 | github.com/klauspost/cpuid/v2 v2.2.9 // indirect
56 | github.com/libdns/libdns v0.2.2 // indirect
57 | github.com/manifoldco/promptui v0.9.0 // indirect
58 | github.com/mattn/go-colorable v0.1.13 // indirect
59 | github.com/mattn/go-isatty v0.0.20 // indirect
60 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
61 | github.com/mholt/acmez/v3 v3.0.0 // indirect
62 | github.com/miekg/dns v1.1.62 // indirect
63 | github.com/mitchellh/copystructure v1.2.0 // indirect
64 | github.com/mitchellh/go-ps v1.0.0 // indirect
65 | github.com/mitchellh/reflectwalk v1.0.2 // indirect
66 | github.com/onsi/ginkgo/v2 v2.13.2 // indirect
67 | github.com/pkg/errors v0.9.1 // indirect
68 | github.com/prometheus/client_golang v1.19.1 // indirect
69 | github.com/prometheus/client_model v0.5.0 // indirect
70 | github.com/prometheus/common v0.48.0 // indirect
71 | github.com/prometheus/procfs v0.12.0 // indirect
72 | github.com/quic-go/qpack v0.5.1 // indirect
73 | github.com/quic-go/quic-go v0.48.2 // indirect
74 | github.com/rs/xid v1.5.0 // indirect
75 | github.com/russross/blackfriday/v2 v2.1.0 // indirect
76 | github.com/shopspring/decimal v1.4.0 // indirect
77 | github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
78 | github.com/slackhq/nebula v1.6.1 // indirect
79 | github.com/smallstep/certificates v0.26.1 // indirect
80 | github.com/smallstep/nosql v0.6.1 // indirect
81 | github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81 // indirect
82 | github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d // indirect
83 | github.com/smallstep/truststore v0.13.0 // indirect
84 | github.com/spf13/cast v1.7.0 // indirect
85 | github.com/spf13/cobra v1.8.1 // indirect
86 | github.com/spf13/pflag v1.0.5 // indirect
87 | github.com/stoewer/go-strcase v1.2.0 // indirect
88 | github.com/tailscale/tscert v0.0.0-20240608151842-d3f834017e53 // indirect
89 | github.com/urfave/cli v1.22.14 // indirect
90 | github.com/zeebo/blake3 v0.2.4 // indirect
91 | go.etcd.io/bbolt v1.3.9 // indirect
92 | go.step.sm/cli-utils v0.9.0 // indirect
93 | go.step.sm/crypto v0.45.0 // indirect
94 | go.step.sm/linkedca v0.20.1 // indirect
95 | go.uber.org/automaxprocs v1.6.0 // indirect
96 | go.uber.org/mock v0.4.0 // indirect
97 | go.uber.org/multierr v1.11.0 // indirect
98 | go.uber.org/zap v1.27.0 // indirect
99 | go.uber.org/zap/exp v0.3.0 // indirect
100 | golang.org/x/crypto v0.31.0 // indirect
101 | golang.org/x/crypto/x509roots/fallback v0.0.0-20241104001025-71ed71b4faf9 // indirect
102 | golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
103 | golang.org/x/mod v0.18.0 // indirect
104 | golang.org/x/net v0.33.0 // indirect
105 | golang.org/x/sync v0.10.0 // indirect
106 | golang.org/x/sys v0.28.0 // indirect
107 | golang.org/x/term v0.27.0 // indirect
108 | golang.org/x/text v0.21.0 // indirect
109 | golang.org/x/time v0.7.0 // indirect
110 | golang.org/x/tools v0.22.0 // indirect
111 | google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect
112 | google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect
113 | google.golang.org/grpc v1.67.1 // indirect
114 | google.golang.org/protobuf v1.35.1 // indirect
115 | gopkg.in/yaml.v3 v3.0.1 // indirect
116 | howett.net/plist v1.0.0 // indirect
117 | )
118 |
--------------------------------------------------------------------------------
/middleware.go:
--------------------------------------------------------------------------------
1 | package inspect
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "net/http"
7 | "os"
8 | "strings"
9 |
10 | "github.com/caddyserver/caddy/v2"
11 | "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
12 | "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
13 | "github.com/caddyserver/caddy/v2/modules/caddyhttp"
14 | "go.uber.org/zap"
15 | )
16 |
17 | func init() {
18 | caddy.RegisterModule(Middleware{})
19 | httpcaddyfile.RegisterHandlerDirective("inspect", parseCaddyfile)
20 | httpcaddyfile.RegisterDirectiveOrder("inspect", httpcaddyfile.After, "encode")
21 | }
22 |
23 | var errRequestTerminated = errors.New("request terminated")
24 |
25 | // Middleware implements an HTTP handler that
26 | // inspects the current request.
27 | type Middleware struct {
28 | logger *zap.Logger
29 | ctx caddy.Context
30 |
31 | Key string
32 | snippetDetails
33 | }
34 |
35 | // CaddyModule returns the Caddy module information.
36 | func (Middleware) CaddyModule() caddy.ModuleInfo {
37 | return caddy.ModuleInfo{
38 | ID: "http.handlers.inspect",
39 | New: func() caddy.Module { return new(Middleware) },
40 | }
41 | }
42 |
43 | // Provision implements caddy.Provisioner.
44 | func (m *Middleware) Provision(ctx caddy.Context) error {
45 | m.logger = ctx.Logger()
46 | m.ctx = ctx
47 | m.snippetDetails = configMap.get(m.Key)
48 |
49 | if err := setUpServer(ctx); err != nil {
50 | return err
51 | }
52 |
53 | server := getServerInstance()
54 | port, started, err := server.start()
55 | if err != nil {
56 | return fmt.Errorf("error occured during provision: %w", err)
57 | }
58 |
59 | // print the server listen address if not previously running
60 | if !started {
61 | m.logger.Info(fmt.Sprintf("inspect console available at http://127.0.0.1:%d", port))
62 | }
63 |
64 | return nil
65 | }
66 |
67 | // Validate implements caddy.Validator.
68 | func (m *Middleware) Validate() error {
69 | return nil
70 | }
71 |
72 | // ServeHTTP implements caddyhttp.MiddlewareHandler.
73 | func (m Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
74 | logger := m.logger.With(zap.String("file", m.file), zap.Int("line", m.line))
75 |
76 | logger.Info("inspecting")
77 |
78 | server := getServerInstance()
79 | action := server.handle(m, nil, r)
80 |
81 | switch action {
82 | case requestActionResume:
83 | logger.Info("resumed")
84 | return next.ServeHTTP(w, r)
85 |
86 | case requestActionStep:
87 | logger.Info("proceeding to response")
88 |
89 | // process middleware chain
90 | err := next.ServeHTTP(w, r)
91 |
92 | // handle the updated request details
93 | action := server.handle(m, w, r)
94 | if err != nil {
95 | return err
96 | }
97 | if action == requestActionResume {
98 | return nil
99 | }
100 |
101 | // request stopped
102 | fallthrough
103 | case requestActionStop:
104 | logger.Info("stopped")
105 | }
106 |
107 | return caddyhttp.Error(http.StatusServiceUnavailable, errRequestTerminated)
108 | }
109 |
110 | // UnmarshalCaddyfile implements caddyfile.Unmarshaler.
111 | func (m *Middleware) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
112 | d.Next() // consume directive name
113 |
114 | // persist the file name and line number
115 | var s snippetDetails
116 | s.file = d.File()
117 | s.line = d.Line()
118 |
119 | m.Key = configKey(s.file, s.line)
120 |
121 | if s.file != "" {
122 | if src, lineStart, err := loadCaddyfileSnippet(s.file, s.line); err == nil {
123 | s.source = src
124 | s.sourceLineStart = lineStart
125 | }
126 |
127 | configMap.set(m.Key, s)
128 | }
129 |
130 | return nil
131 | }
132 |
133 | // parseCaddyfile unmarshals tokens from h into a new Middleware.
134 | func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
135 | var m Middleware
136 | err := m.UnmarshalCaddyfile(h.Dispenser)
137 | return m, err
138 | }
139 |
140 | // loadCaddyfileSnippet reads 5 lines within context in the loaded Caddyfile.
141 | func loadCaddyfileSnippet(file string, line int) (snippet []string, firstLine int, err error) {
142 | f, err := os.ReadFile(file)
143 | if err != nil {
144 | return nil, 0, fmt.Errorf("error reading Caddyfile: %w", err)
145 | }
146 |
147 | lines := strings.Split(string(f), "\n")
148 | if len(lines) < line {
149 | return nil, 0, fmt.Errorf("invalid line: %d", line)
150 | }
151 |
152 | lineIndex := line - 1 // slice index
153 |
154 | start := lineIndex - 2
155 | if start < 0 {
156 | start = 0
157 | }
158 |
159 | end := (lineIndex + 1) + 2 // the extra 1 is for ending index which is not inclusive.
160 | if end > len(lines) {
161 | end = len(lines)
162 | }
163 |
164 | return lines[start:end], start + 1, nil
165 | }
166 |
167 | type snippetDetails struct {
168 | file string
169 | line int
170 | source []string
171 | sourceLineStart int // for lack of a better name
172 | }
173 |
174 | func (c snippetDetails) valid() bool {
175 | return c.file != "" && c.line > 0
176 | }
177 |
178 | // Interface guards
179 | var (
180 | _ caddy.Provisioner = (*Middleware)(nil)
181 | _ caddy.Validator = (*Middleware)(nil)
182 | _ caddyhttp.MiddlewareHandler = (*Middleware)(nil)
183 | _ caddyfile.Unmarshaler = (*Middleware)(nil)
184 | )
185 |
--------------------------------------------------------------------------------
/page.go:
--------------------------------------------------------------------------------
1 | package inspect
2 |
3 | import (
4 | "embed"
5 | )
6 |
7 | //go:embed static/assets static/index.html
8 | var staticFS embed.FS
9 |
--------------------------------------------------------------------------------
/port.go:
--------------------------------------------------------------------------------
1 | package inspect
2 |
3 | import (
4 | "fmt"
5 | "net"
6 | )
7 |
8 | const httpServerListenPort = 2020
9 |
10 | // findAvailablePort returns an available port on the host machine.
11 | // it attempts port 2020 up till 2029
12 | func findAvailablePort() (int, error) {
13 | allocatePort := func(port int) error {
14 | listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
15 | if err != nil {
16 | return fmt.Errorf("error picking an available port: %w", err)
17 | }
18 |
19 | if err := listener.Close(); err != nil {
20 | return fmt.Errorf("error closing temporary port listener: %w", err)
21 | }
22 |
23 | return nil
24 | }
25 |
26 | var err error
27 | for i := 0; i < 10; i++ {
28 | port := httpServerListenPort + i
29 | err = allocatePort(port)
30 | if err == nil {
31 | return port, nil
32 | }
33 | }
34 |
35 | return 0, err
36 | }
37 |
--------------------------------------------------------------------------------
/response.go:
--------------------------------------------------------------------------------
1 | package inspect
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "runtime"
7 |
8 | "github.com/caddyserver/caddy/v2"
9 | "github.com/caddyserver/caddy/v2/modules/caddyhttp"
10 | )
11 |
12 | type Response struct {
13 | URL string `json:"url,omitempty"`
14 | Method string `json:"method,omitempty"`
15 | Host string `json:"host,omitempty"`
16 | RequestHeaders http.Header `json:"request_headers,omitempty"`
17 | ResponseHeaders http.Header `json:"response_headers,omitempty"`
18 | RemoteAddress string `json:"remote_address,omitempty"`
19 | Form string `json:"form,omitempty"`
20 | Proto string `json:"proto,omitempty"`
21 | UserAgent string `json:"user_agent,omitempty"`
22 | Referer string `json:"referer,omitempty"`
23 | ContentLength int64 `json:"content_length,omitempty"`
24 | BasicAuth *struct {
25 | Username string `json:"username,omitempty"`
26 | Password string `json:"password,omitempty"`
27 | } `json:"basic_auth,omitempty"`
28 | Cookies []*http.Cookie `json:"cookies,omitempty"`
29 | CaddyVersion string `json:"caddy_version,omitempty"`
30 | CaddyContext struct {
31 | Variables map[string]any `json:"Variables,omitempty"`
32 | Modules []string `json:"Modules,omitempty"`
33 | Error any `json:"Error,omitempty"`
34 | } `json:"caddy_context,omitempty"`
35 | CaddyModules []string `json:"caddy_modules,omitempty"`
36 | Caddyfile *struct {
37 | File string `json:"file,omitempty"`
38 | Line int `json:"line,omitempty"`
39 | Source []string `json:"source,omitempty"`
40 | SourceLineStart int `json:"source_line_start,omitempty"`
41 | } `json:"caddyfile,omitempty"`
42 | MemoryUsage string `json:"memory_usage,omitempty"`
43 | responseMode bool
44 | }
45 |
46 | func buildResponse(m Middleware, w http.ResponseWriter, r *http.Request) (d Response) {
47 | d.URL = r.URL.String()
48 | d.Method = r.Method
49 | d.Host = r.Host
50 | d.RequestHeaders = r.Header
51 | d.RemoteAddress = r.RemoteAddr
52 | d.Form = r.Form.Encode()
53 | d.Proto = r.Proto
54 | d.UserAgent = r.UserAgent()
55 | d.Referer = r.Referer()
56 | d.ContentLength = r.ContentLength
57 | d.Cookies = r.Cookies()
58 |
59 | if w != nil {
60 | d.ResponseHeaders = w.Header()
61 | d.responseMode = true
62 | }
63 |
64 | username, password, _ := r.BasicAuth()
65 | if username != "" || password != "" {
66 | d.BasicAuth = &struct {
67 | Username string `json:"username,omitempty"`
68 | Password string `json:"password,omitempty"`
69 | }{Username: username, Password: password}
70 | }
71 |
72 | d.CaddyModules = caddy.Modules()
73 | for _, m := range m.ctx.Modules() {
74 | d.CaddyContext.Modules = append(d.CaddyContext.Modules, m.CaddyModule().String())
75 | }
76 |
77 | _, d.CaddyVersion = caddy.Version()
78 |
79 | if m.valid() {
80 | d.Caddyfile = &struct {
81 | File string `json:"file,omitempty"`
82 | Line int `json:"line,omitempty"`
83 | Source []string `json:"source,omitempty"`
84 | SourceLineStart int `json:"source_line_start,omitempty"`
85 | }{
86 | File: m.file,
87 | Line: m.line,
88 | Source: m.source,
89 | SourceLineStart: m.sourceLineStart,
90 | }
91 | }
92 |
93 | vars, _ := r.Context().Value(caddyhttp.VarsCtxKey).(map[string]any)
94 | d.CaddyContext.Variables = vars
95 |
96 | if err, _ := r.Context().Value(caddyhttp.ErrorCtxKey).(error); err != nil {
97 | d.CaddyContext.Error = err.Error()
98 |
99 | // if it is an handler error, set specific error
100 | if err, ok := err.(caddyhttp.HandlerError); ok {
101 | herr := handlerErr{HandlerError: err}
102 | if err := err.Err; err != nil {
103 | herr.Err = err.Error()
104 | }
105 | d.CaddyContext.Error = herr
106 | }
107 | }
108 |
109 | d.MemoryUsage = getMemUsage().String()
110 |
111 | return
112 | }
113 |
114 | type handlerErr struct {
115 | Err string
116 | caddyhttp.HandlerError
117 | }
118 |
119 | type memNum uint64
120 |
121 | func (m memNum) String() string { return fmt.Sprintf("%d MiB", m/1024/1024) }
122 |
123 | func getMemUsage() memNum {
124 | var m runtime.MemStats
125 | runtime.ReadMemStats(&m)
126 |
127 | return memNum(m.Sys)
128 | }
129 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abiosoft/caddy-inspect/96cdb1dfb122f79913d60ecb34030d302a4f4ec1/screenshot.png
--------------------------------------------------------------------------------
/server.go:
--------------------------------------------------------------------------------
1 | package inspect
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io/fs"
7 | "net/http"
8 | "sync"
9 | "time"
10 |
11 | "github.com/caddyserver/caddy/v2"
12 | "go.uber.org/zap"
13 | )
14 |
15 | type Server struct {
16 | // http handler
17 | request *Response
18 | requestID int64
19 | action chan requestAction
20 |
21 | // static handler
22 | static http.Handler
23 |
24 | // instance
25 | port int
26 | logger *zap.Logger
27 | err error
28 |
29 | instanceMutex sync.Mutex
30 | requestMutex sync.RWMutex
31 | }
32 |
33 | type requestAction = int
34 |
35 | const (
36 | requestActionResume requestAction = iota
37 | requestActionStep
38 | requestActionStop
39 | )
40 |
41 | func (s *Server) hasRequest() bool {
42 | s.requestMutex.RLock()
43 | defer s.requestMutex.RUnlock()
44 |
45 | return s.request != nil
46 | }
47 |
48 | func (s *Server) hasResponse() bool {
49 | s.requestMutex.RLock()
50 | defer s.requestMutex.RUnlock()
51 |
52 | return s.request != nil && s.request.responseMode
53 | }
54 |
55 | // start starts the server. If the server is already running, nothing is done.
56 | //
57 | // returns the port the server is listening on,
58 | // whether the server has been previously started,
59 | // and an error if any
60 | func (s *Server) start() (port int, started bool, err error) {
61 | s.instanceMutex.Lock()
62 | defer s.instanceMutex.Unlock()
63 |
64 | // server already started
65 | if s.port > 0 {
66 | return s.port, true, nil
67 | }
68 |
69 | port, err = findAvailablePort()
70 | if err != nil {
71 | return port, false, fmt.Errorf("cannot start caddy-inspect server: %w", err)
72 | }
73 | s.port = port
74 |
75 | errChan := make(chan error)
76 | go func(server *Server) {
77 | errChan <- http.ListenAndServe(fmt.Sprintf("127.0.0.1:%d", port), server)
78 | s.instanceMutex.Lock()
79 | s.err = <-errChan
80 | s.instanceMutex.Unlock()
81 | }(s)
82 |
83 | return port, false, nil
84 | }
85 |
86 | func (s *Server) handle(m Middleware, w http.ResponseWriter, r *http.Request) requestAction {
87 | s.instanceMutex.Lock()
88 | defer s.instanceMutex.Unlock()
89 |
90 | request := buildResponse(m, w, r)
91 |
92 | s.requestMutex.Lock()
93 | s.request = &request
94 | s.requestID = time.Now().UnixNano()
95 | s.requestMutex.Unlock()
96 |
97 | action := <-s.action
98 |
99 | s.requestMutex.Lock()
100 | s.request = nil
101 | s.requestID = 0
102 | s.requestMutex.Unlock()
103 |
104 | return action
105 | }
106 |
107 | func writeJson(w http.ResponseWriter, body any) error {
108 | w.Header().Add("content-type", "application/json")
109 | w.Header().Add("Access-Control-Allow-Origin", "*") // this should probably be removed.
110 | return json.NewEncoder(w).Encode(body)
111 | }
112 |
113 | // ServeHTTP implements http.Handler.
114 | func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
115 | // post requests
116 | // resume and stop
117 | if r.Method == http.MethodPost {
118 | var action requestAction
119 |
120 | switch r.URL.Path {
121 | case "/stop":
122 | action = requestActionStop
123 | case "/step":
124 | action = requestActionStep
125 | default:
126 | action = requestActionResume
127 | }
128 |
129 | if s.hasRequest() {
130 | s.action <- action
131 | }
132 |
133 | if err := writeJson(w, "ok"); err != nil {
134 | s.logger.Error("error writing http response", zap.Error(err))
135 | }
136 | return
137 | }
138 |
139 | // handle /request
140 | if r.URL.Path == "/request" {
141 | var response struct {
142 | HasRequest bool `json:"has_request"`
143 | HasResponse bool `json:"has_response"`
144 | Request *Response `json:"request,omitempty"`
145 | ID int64 `json:"id"`
146 | }
147 |
148 | response.HasRequest = s.hasRequest()
149 | response.HasResponse = s.hasResponse()
150 | response.Request = s.request
151 | response.ID = s.requestID
152 |
153 | if err := writeJson(w, response); err != nil {
154 | s.logger.Error("error writing http response", zap.Error(err))
155 | }
156 | return
157 | }
158 |
159 | s.static.ServeHTTP(w, r)
160 | }
161 |
162 | var _ http.Handler = (*Server)(nil)
163 |
164 | var defaultServer *Server
165 |
166 | func setUpServer(ctx caddy.Context) error {
167 | // setup already done
168 | if defaultServer != nil {
169 | return nil
170 | }
171 |
172 | dir, err := fs.Sub(staticFS, "static")
173 | if err != nil {
174 | return fmt.Errorf("error setting up static file server: %w", err)
175 | }
176 |
177 | defaultServer = &Server{
178 | logger: ctx.Logger(),
179 | action: make(chan requestAction),
180 | static: http.FileServerFS(dir),
181 | }
182 | return nil
183 | }
184 |
185 | func getServerInstance() *Server { return defaultServer }
186 |
--------------------------------------------------------------------------------
/static/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abiosoft/caddy-inspect/96cdb1dfb122f79913d60ecb34030d302a4f4ec1/static/assets/favicon.png
--------------------------------------------------------------------------------
/static/assets/index-B254VF7R.css:
--------------------------------------------------------------------------------
1 | :root{--bg-color: #ffffff;--text-color: #000000;--border-color: #ddd;--button-bg-color: #007bff;--button-hover-bg-color: #0056b3;--button-active-bg-color: #004085;--button-danger-color: #c73636;--button-hover-danger-color: #a62d2d;--calm-color: #7e7e7e;--input-focus-border-color: #007bff;--input-focus-shadow-color: rgba(0, 123, 255, .5);--snippet-highlight-color: rgba(150, 150, 150, .5)}.logo{content:url(/assets/logo-light.svg);height:30px;margin-right:5px}@media (prefers-color-scheme: dark){:root{--bg-color: #121212;--text-color: #ffffff;--border-color: #333;--button-bg-color: #1a73e8;--button-hover-bg-color: #135ba1;--button-active-bg-color: #0e447a;--button-danger-color: #a0222f;--button-hover-danger-color: #881c28;--calm-color: #7e7e7e;--input-focus-border-color: #1a73e8;--input-focus-shadow-color: rgba(26, 115, 232, .5);--snippet-highlight-color: rgba(150, 150, 150, .5)}.logo{content:url(/assets/logo-dark.svg)}}body{font-family:Arial,sans-serif;margin:0;padding:0;box-sizing:border-box;background-color:var(--bg-color);color:var(--text-color)}.inspect{padding:10px}button{padding:10px 20px;font-size:16px;color:#fff;background-color:var(--button-bg-color);border:none;border-radius:4px;cursor:pointer}button:hover{background-color:var(--button-hover-bg-color)}button:active{background-color:var(--button-active-bg-color);transform:translateY(1px)}button.danger{background-color:var(--button-danger-color)}button.danger:hover{background-color:var(--button-hover-danger-color)}input[type=text]{width:100%;padding:10px;font-size:14px;border:1px solid var(--border-color);border-radius:4px;background-color:var(--textarea-bg-color);color:var(--text-color)}input[type=text]:focus{outline:none;border-color:var(--input-focus-border-color);box-shadow:0 0 4px var(--input-focus-shadow-color)}.loading{display:flex;justify-content:center;align-items:center;height:30vh;font-size:20px;color:var(--text-color)}.tree-node.svelte-1sbg00l{margin-left:20px;display:flex;flex-direction:column}.node-header.svelte-1sbg00l{cursor:pointer;display:flex;align-items:center}.toggle-icon.svelte-1sbg00l{width:15px;font-size:10px}.key-value-container.svelte-1sbg00l{display:flex;justify-content:space-between;width:100%}.key.svelte-1sbg00l{flex:1;margin-right:10px;font-family:monospace;padding:5px;font-weight:700}.value.svelte-1sbg00l,.value-only.svelte-1sbg00l{flex:2;margin-left:10px;border:none;background:transparent;font-family:monospace;font-size:inherit;color:inherit;padding:5px;border-radius:4px}.key-value-container.svelte-1sbg00l:has(.value-only:where(.svelte-1sbg00l)):not(:has(.key)) .value-only:where(.svelte-1sbg00l){flex:1 1 100%;margin-left:0}.children.svelte-1sbg00l{margin-left:20px}.tree.svelte-4p6snr{font-family:Arial,sans-serif}.json-tree.svelte-4p6snr{font-family:monospace;font-size:15px;padding:20px;border:1px solid var(--border-color);border-radius:4px;background-color:var(--textarea-bg-color);color:var(--text-color)}.container.svelte-4p6snr{max-width:800px;margin:20px auto;padding:20px;border-radius:8px;box-shadow:0 4px 8px #0000001a;background-color:var(--bg-color)}.top-row.svelte-4p6snr{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}.top-row-item.svelte-4p6snr{padding:5px}.top-row-item.svelte-4p6snr button:where(.svelte-4p6snr){width:50px;font-size:25px;padding:5px 0;line-height:1.2}.top-row-right.svelte-4p6snr{margin-left:auto}.header.svelte-4p6snr{display:flex;text-align:center;align-items:center;justify-content:center;font-size:20px;font-weight:700;color:var(--text-color)}.source-file.svelte-4p6snr{text-align:center;font-size:12px;font-weight:700;padding-bottom:10px}.snippet.svelte-4p6snr{display:flex;flex-direction:column;font-family:monospace;font-size:13px;padding:10px;border-radius:5px;overflow:auto;border:solid 1px var(--calm-color);margin-bottom:10px}.code-line.svelte-4p6snr{display:flex;padding:2px 5px}.mark.svelte-4p6snr{width:30px;text-align:right;margin-right:0}.highlight.svelte-4p6snr{background-color:var(--snippet-highlight-color)}.line-number.svelte-4p6snr{width:30px;text-align:right;margin-right:10px;color:var(--calm-color)}.highlight.svelte-4p6snr .line-number:where(.svelte-4p6snr){color:var(--text-color)}.line-content.svelte-4p6snr{white-space:pre;-moz-tab-size:4;tab-size:4;width:200px}
2 |
--------------------------------------------------------------------------------
/static/assets/index-CLXqnKY8.js:
--------------------------------------------------------------------------------
1 | (function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const l of document.querySelectorAll('link[rel="modulepreload"]'))r(l);new MutationObserver(l=>{for(const a of l)if(a.type==="childList")for(const u of a.addedNodes)u.tagName==="LINK"&&u.rel==="modulepreload"&&r(u)}).observe(document,{childList:!0,subtree:!0});function n(l){const a={};return l.integrity&&(a.integrity=l.integrity),l.referrerPolicy&&(a.referrerPolicy=l.referrerPolicy),l.crossOrigin==="use-credentials"?a.credentials="include":l.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function r(l){if(l.ep)return;l.ep=!0;const a=n(l);fetch(l.href,a)}})();const At=!1;var ct=Array.isArray,bn=Array.prototype.indexOf,_t=Array.from,xn=Object.defineProperty,ye=Object.getOwnPropertyDescriptor,kn=Object.prototype,Tn=Array.prototype,An=Object.getPrototypeOf;function Nn(e){for(var t=0;t=N.v&&w(N,R+1)}Ct(u)}return!0},ownKeys(f){h(u);var i=Reflect.ownKeys(f).filter(s=>{var c=l.get(s);return c===void 0||c.v!==D});for(var[o,v]of l)v.v!==D&&!(o in f)&&i.push(o);return i},setPrototypeOf(){Fn()}})}function Ct(e,t=1){w(e,e.v+t)}var qt,Kt,Wt;function Gn(){if(qt===void 0){qt=window;var e=Element.prototype,t=Node.prototype;Kt=ye(t,"firstChild").get,Wt=ye(t,"nextSibling").get,e.__click=void 0,e.__className="",e.__attributes=null,e.__styles=null,e.__e=void 0,Text.prototype.__t=void 0}}function Ke(e=""){return document.createTextNode(e)}function Se(e){return Kt.call(e)}function We(e){return Wt.call(e)}function O(e,t){return Se(e)}function de(e,t){{var n=Se(e);return n instanceof Comment&&n.data===""?We(n):n}}function H(e,t=1,n=!1){let r=e;for(;t--;)r=We(r);return r}function Xn(e){e.textContent=""}function ie(e){var t=Y|te,n=E!==null&&E.f&Y?E:null;return b===null||n!==null&&n.f&X?t|=X:b.f|=Ft,{ctx:T,deps:null,effects:null,equals:Lt,f:t,fn:e,reactions:null,rv:0,v:null,wv:0,parent:n??b}}function Zn(e){const t=ie(e);return t.equals=Mt,t}function Gt(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;nnew Promise(r=>{n.outro?De(t,()=>{le(t),r(void 0)}):(le(t),r(void 0))})}function zt(e){return Ge(Pt,e,!1)}function _e(e,t=[],n=ie){const r=t.map(n);return wt(()=>e(...r.map(h)))}function wt(e,t=0){return Ge(dt|pt|t,e,!0)}function xe(e,t=!0){return Ge(dt|ee,e,!0,t)}function Jt(e){var t=e.teardown;if(t!==null){const n=Et,r=E;Ot(!0),he(null);try{t.call(null)}finally{Ot(n),he(r)}}}function Qt(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){var r=n.next;le(n,t),n=r}}function tr(e){for(var t=e.first;t!==null;){var n=t.next;t.f&ee||le(t),t=n}}function le(e,t=!0){var n=!1;if((t||e.f&qn)&&e.nodes_start!==null){for(var r=e.nodes_start,l=e.nodes_end;r!==null;){var a=r===l?null:We(r);r.remove(),r=a}n=!0}Qt(e,t&&!n),Be(e,0),$(e,He);var u=e.transitions;if(u!==null)for(const f of u)f.stop();Jt(e);var _=e.parent;_!==null&&_.first!==null&&$t(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes_start=e.nodes_end=null}function $t(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function De(e,t){var n=[];yt(e,n,!0),en(n,()=>{le(e),t&&t()})}function en(e,t){var n=e.length;if(n>0){var r=()=>--n||t();for(var l of e)l.out(r)}else t()}function yt(e,t,n){if(!(e.f&J)){if(e.f^=J,e.transitions!==null)for(const u of e.transitions)(u.is_global||n)&&t.push(u);for(var r=e.first;r!==null;){var l=r.next,a=(r.f&ht)!==0||(r.f&ee)!==0;yt(r,t,a?n:!1),r=l}}}function Pe(e){tn(e,!0)}function tn(e,t){if(e.f&J){e.f^=J,e.f&F||(e.f^=F),Ae(e)&&($(e,te),Ze(e));for(var n=e.first;n!==null;){var r=n.next,l=(n.f&ht)!==0||(n.f&ee)!==0;tn(n,l?t:!1),n=r}if(e.transitions!==null)for(const a of e.transitions)(a.is_global||t)&&a.in()}}let it=!1,ut=[];function nr(){it=!1;const e=ut.slice();ut=[],Nn(e)}function rr(e){it||(it=!0,queueMicrotask(nr)),ut.push(e)}let Re=!1,Fe=!1,Le=null,pe=!1,Et=!1;function Rt(e){pe=e}function Ot(e){Et=e}let ot=[],Ee=0;let E=null,Q=!1;function he(e){E=e}let b=null;function ge(e){b=e}let G=null;function lr(e){G=e}let P=null,M=0,z=null;function ar(e){z=e}let nn=1,Me=0,se=!1;function rn(){return++nn}function Ae(e){var i;var t=e.f;if(t&te)return!0;if(t&me){var n=e.deps,r=(t&X)!==0;if(n!==null){var l,a,u=(t&Ie)!==0,_=r&&b!==null&&!se,f=n.length;if(u||_){for(l=0;le.wv)return!0}(!r||b!==null&&!se)&&$(e,F)}return!1}function sr(e,t){for(var n=t;n!==null;){if(n.f&Oe)try{n.fn(e);return}catch{n.f^=Oe}n=n.parent}throw Re=!1,e}function ir(e){return(e.f&He)===0&&(e.parent===null||(e.parent.f&Oe)===0)}function Xe(e,t,n,r){if(Re){if(n===null&&(Re=!1),ir(t))throw e;return}n!==null&&(Re=!0);{sr(e,t);return}}function ln(e,t,n=0){var r=e.reactions;if(r!==null)for(var l=0;l0)for(v.length=M+P.length,s=0;s1e3){Ee=0;try{Dn()}catch(e){if(Le!==null)Xe(e,Le,null);else throw e}}Ee++}function fr(e){var t=e.length;if(t!==0){or();var n=pe;pe=!0;try{for(var r=0;r1001)return;const e=ot;ot=[],fr(e),Fe||(Ee=0,Le=null)}function Ze(e){Fe||(Fe=!0,queueMicrotask(cr)),Le=e;for(var t=e;t.parent!==null;){t=t.parent;var n=t.f;if(n&(ke|ee)){if(!(n&F))return;t.f^=F}}ot.push(t)}function sn(e,t){var n=e.first,r=[];e:for(;n!==null;){var l=n.f,a=(l&ee)!==0,u=a&&(l&F)!==0,_=n.next;if(!u&&!(l&J))if(l&dt){if(a)n.f^=F;else{var f=E;try{E=n,Ae(n)&&bt(n)}catch(s){Xe(s,n,null,n.ctx)}finally{E=f}}var i=n.first;if(i!==null){n=i;continue}}else l&Pt&&r.push(n);if(_===null){let s=n.parent;for(;s!==null;){if(e===s)break e;var o=s.next;if(o!==null){n=o;continue e}s=s.parent}}n=_}for(var v=0;v{throw g});throw s}}finally{e.__root=t,delete e.currentTarget,he(o),ge(v)}}}function hr(e){var t=document.createElement("template");return t.innerHTML=e,t.content}function Ue(e,t){var n=b;n.nodes_start===null&&(n.nodes_start=e,n.nodes_end=t)}function L(e,t){var n=(t&Hn)!==0,r=(t&Yn)!==0,l,a=!e.startsWith("");return()=>{l===void 0&&(l=hr(a?e:""+e),n||(l=Se(l)));var u=r?document.importNode(l,!0):l.cloneNode(!0);if(n){var _=Se(u),f=u.lastChild;Ue(_,f)}else Ue(u,u);return u}}function at(e=""){{var t=Ke(e+"");return Ue(t,t),t}}function It(){var e=document.createDocumentFragment(),t=document.createComment(""),n=Ke();return e.append(t,n),Ue(t,n),e}function q(e,t){e!==null&&e.before(t)}function be(e,t){var n=t==null?"":typeof t=="object"?t+"":t;n!==(e.__t??(e.__t=e.nodeValue))&&(e.__t=n,e.nodeValue=n+"")}function gr(e,t){return mr(e,t)}const ce=new Map;function mr(e,{target:t,anchor:n,props:r={},events:l,context:a,intro:u=!0}){Gn();var _=new Set,f=v=>{for(var s=0;s{var v=n??t.appendChild(Ke());return xe(()=>{if(a){Vt({});var s=T;s.c=a}l&&(r.$$events=l),i=e(v,r)||{},a&&jt()}),()=>{var d;for(var s of _){t.removeEventListener(s,qe);var c=ce.get(s);--c===0?(document.removeEventListener(s,qe),ce.delete(s)):ce.set(s,c)}ft.delete(f),v!==n&&((d=v.parentNode)==null||d.removeChild(v))}});return wr.set(i,o),i}let wr=new WeakMap;function W(e,t,n=!1){var r=e,l=null,a=null,u=D,_=n?ht:0,f=!1;const i=(v,s=!0)=>{f=!0,o(s,v)},o=(v,s)=>{u!==(u=v)&&(u?(l?Pe(l):s&&(l=xe(()=>s(r))),a&&De(a,()=>{a=null})):(a?Pe(a):s&&(a=xe(()=>s(r))),l&&De(l,()=>{l=null})))};wt(()=>{f=!1,t(i),f||o(null,null)},_)}function Ve(e,t){return t}function yr(e,t,n,r){for(var l=[],a=t.length,u=0;u0&&l.length===0&&n!==null;if(_){var f=n.parentNode;Xn(f),f.append(n),r.clear(),re(e,t[0].prev,t[a-1].next)}en(l,()=>{for(var i=0;i {var c=n();return ct(c)?c:c==null?[]:_t(c)});wt(()=>{var c=h(s),d=c.length;v&&d===0||(v=d===0,Er(c,_,u,l,t,r,n),a!==null&&(d===0?o?Pe(o):o=xe(()=>a(u)):o!==null&&De(o,()=>{o=null})),h(s))})}function Er(e,t,n,r,l,a,u){var ue,oe,ae,Ne;var _=(l&Vn)!==0,f=(l&(gt|mt))!==0,i=e.length,o=t.items,v=t.first,s=v,c,d=null,A,x=[],N=[],R,g,p,m;if(_)for(m=0;m0){var we=l&Bt&&i===0?n:null;if(_){for(m=0;m{var Ce;if(A!==void 0)for(p of A)(Ce=p.a)==null||Ce.apply()}),b.first=t.first&&t.first.e,b.last=d&&d.e}function br(e,t,n,r){r>&&st(e.v,t),r&mt?st(e.i,n):e.i=n}function xr(e,t,n,r,l,a,u,_,f,i){var o=(f>)!==0,v=(f&jn)===0,s=o?v?Kn(l):B(l):l,c=f&mt?B(u):u,d={i:c,v:s,k:a,a:null,e:null,prev:n,next:r};try{return d.e=xe(()=>_(e,s,c,i),Yt),d.e.prev=n&&n.e,d.e.next=r&&r.e,n===null?t.first=d:(n.next=d,n.e.next=d.e),r!==null&&(r.prev=d,r.e.prev=d.e),d}finally{}}function St(e,t,n){for(var r=e.next?e.next.e.nodes_start:n,l=t?t.e.nodes_start:n,a=e.e.nodes_start;a!==r;){var u=We(a);l.before(a),a=u}}function re(e,t,n){t===null?e.first=n:(t.next=n,t.e.next=n&&n.e),n!==null&&(n.prev=t,n.e.prev=t&&t.e)}function vn(e){var t,n,r="";if(typeof e=="string"||typeof e=="number")r+=e;else if(typeof e=="object")if(Array.isArray(e)){var l=e.length;for(t=0;t{const t=un(e);if(typeof t=="function")return t})}function Cr(e){T===null&&Ut(),cn(()=>()=>un(e))}function qr(e){var t=e.l;return t.u??(t.u={a:[],b:[],m:[]})}const Rr="5";typeof window<"u"&&(window.__svelte||(window.__svelte={v:new Set})).v.add(Rr);Un();function Or(e,t){w(t,!h(t))}var Ir=L(' '),Sr=L(' '),Dr=L(' '),Pr=L(' '),Fr=L(' ',1),Lr=L('
'),Mr=L('
');function vt(e,t){const n="__array__";let r=Object.entries(t.node),l=ie(()=>Array.isArray(t.node)),a=ie(()=>typeof t.node=="object"&&t.node!==null),u=t.key==n,_=j(!1);var f=Mr(),i=O(f);i.__click=[Or,_];var o=O(i);{var v=g=>{var p=Ir(),m=O(p);_e(()=>be(m,h(_)?"▼":"▶")),q(g,p)},s=g=>{var p=Sr();q(g,p)};W(o,g=>{h(a)?g(v):g(s,!1)})}var c=H(o,2),d=O(c);{var A=g=>{var p=Dr();_e(()=>Dt(p,t.node)),q(g,p)},x=g=>{var p=Fr(),m=de(p),I=O(m),k=H(m,2);{var y=S=>{var U=Pr();_e(()=>Dt(U,t.node)),q(S,U)};W(k,S=>{h(a)||S(y)})}_e(()=>be(I,t.key)),q(g,p)};W(d,g=>{u?g(A):g(x,!1)})}var N=H(i,2);{var R=g=>{var p=Lr(),m=O(p);{var I=y=>{var S=It(),U=de(S);je(U,17,()=>t.node,Ve,(V,K,we,ue)=>{vt(V,{key:n,get node(){return h(K)}})}),q(y,S)},k=y=>{var S=It(),U=de(S);je(U,17,()=>r,Ve,(V,K,we,ue)=>{let oe=()=>h(K)[0],ae=()=>h(K)[1];vt(V,{get key(){return oe()},get node(){return ae()}})}),q(y,S)};W(m,y=>{h(l)?y(I):y(k,!1)})}q(g,p)};W(N,g=>{h(a)&&h(_)&&g(R)})}q(e,f)}fn(["click"]);async function Br(e,t,n,r){(await fetch(t,{method:"POST"})).ok&&(n(),r())}async function Ur(e,t,n,r){(await fetch(`${t}/stop`,{method:"POST"})).ok&&(n(),r())}async function Vr(e,t,n,r){(await fetch(`${t}/step`,{method:"POST"})).ok&&(n(),r())}var jr=L('⏭ '),Hr=L(" ◉",1),Yr=L('
'),Kr=L('
',1),Wr=L('⏵ ⏹
',1),Gr=L('Waiting for request...
'),Xr=L('
');function Zr(e,t){Vt(t,!0);const n="";let r=j(C([])),l=ie(()=>h(r).length>0),a=j(""),u=j(""),_=j(""),f=j(0),i=j(0),o=ie(()=>h(_)!=""),v=j(C([])),s=j(0),c=j(0),d=j(!1);cn(async()=>{w(s,C(setInterval(N,1e3)))}),Cr(async()=>{clearInterval(h(s))}),Zt(()=>{document.title=h(a)?`Caddy Inspect - ${h(u).toUpperCase()} ${h(a)}`:"Caddy Inspect"});function A(I){return I.split("_").map(k=>k.charAt(0).toUpperCase()+k.slice(1).toLowerCase()).join(" ")}function x(){w(r,C([])),w(a,""),w(u,""),w(c,0),w(_,""),w(f,0),w(i,0),w(v,C([]))}async function N(){const I=await fetch(`${n}/request`);if(!I.ok)return;const k=await I.json();if(!k.has_request){x();return}if(h(c)==k.id)return;const{caddyfile:y,...S}=k.request;w(c,C(k.id)),w(a,C(k.request.url)),w(u,C(k.request.method)),w(d,C(k.has_response)),y&&(w(_,C(y.file)),w(f,C(y.line)),w(i,C(y.source_line_start)),w(v,C(y.source))),w(r,C(Object.entries(S))),window.focus()}var R=Xr(),g=H(O(R),4);{var p=I=>{var k=Wr(),y=de(k),S=O(y),U=O(S);U.__click=[Br,n,x,N];var V=H(S,2);{var K=Z=>{var ne=jr(),fe=O(ne);fe.__click=[Vr,n,x,N],q(Z,ne)};W(V,Z=>{h(d)||Z(K)})}var we=H(V,2),ue=O(we);ue.__click=[Ur,n,x,N];var oe=H(y,2),ae=O(oe);{var Ne=Z=>{var ne=Kr(),fe=de(ne),ze=O(fe),Je=O(ze),_n=H(fe,2);je(_n,21,()=>h(v),Ve,(Qe,$e,et)=>{var tt=Yr(),xt=O(tt),dn=O(xt);{var pn=nt=>{var Tt=Hr(),wn=de(Tt);{var yn=ve=>{var rt=at("↑");q(ve,rt)},En=ve=>{var rt=at("↓");q(ve,rt)};W(wn,ve=>{h(d)?ve(yn):ve(En,!1)})}q(nt,Tt)};W(dn,nt=>{et+h(i)==h(f)&&nt(pn)})}var kt=H(xt,2),hn=O(kt),gn=H(kt,2),mn=O(gn);_e(()=>{Ar(tt,Tr({"code-line":!0,highlight:et+h(i)==h(f)}),"svelte-4p6snr"),be(hn,et+h(i)),be(mn,h($e))}),q(Qe,tt)},Qe=>{var $e=at(" ");q(Qe,$e)}),_e(()=>be(Je,`${h(_)??""}:${h(f)??""}`)),q(Z,ne)};W(ae,Z=>{h(o)&&Z(Ne)})}var Ce=H(ae,2);je(Ce,17,()=>h(r),Ve,(Z,ne)=>{let fe=()=>h(ne)[0],ze=()=>h(ne)[1];const Je=ie(()=>A(fe()));vt(Z,{get key(){return h(Je)},get node(){return ze()}})}),q(I,k)},m=I=>{var k=Gr();q(I,k)};W(g,I=>{h(l)?I(p):I(m,!1)})}q(e,R),jt()}fn(["click"]);function zr(e){Zr(e,{})}gr(zr,{target:document.getElementById("app")});
2 |
--------------------------------------------------------------------------------
/static/assets/logo-dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/static/assets/logo-light.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/static/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Caddy Inspect
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/store.go:
--------------------------------------------------------------------------------
1 | package inspect
2 |
3 | import (
4 | "fmt"
5 | "sync"
6 | )
7 |
8 | var configMap = storeMap{m: map[string]snippetDetails{}}
9 |
10 | func configKey(file string, line int) string { return fmt.Sprintf("%s:%d", file, line) }
11 |
12 | type storeMap struct {
13 | m map[string]snippetDetails
14 | sync.RWMutex
15 | }
16 |
17 | func (s *storeMap) set(key string, val snippetDetails) {
18 | s.Lock()
19 | defer s.Unlock()
20 |
21 | s.m[key] = val
22 | }
23 |
24 | func (s *storeMap) get(key string) snippetDetails {
25 | s.RLock()
26 | defer s.RUnlock()
27 |
28 | return s.m[key]
29 | }
30 |
--------------------------------------------------------------------------------
/xcaddy.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | xcaddy build --with github.com/abiosoft/caddy-inspect=.
4 |
--------------------------------------------------------------------------------