├── pnpm-workspace.yaml ├── .gitignore ├── workspace └── extension │ ├── .gitignore │ ├── static │ ├── register.html │ ├── icons │ │ ├── default-128.png │ │ ├── default-16.png │ │ ├── default-24.png │ │ ├── default-48.png │ │ ├── default-96.png │ │ ├── disabled-16.png │ │ ├── disabled-24.png │ │ ├── disabled-48.png │ │ ├── disabled-96.png │ │ ├── disabled-128.png │ │ ├── svelte-dark.svg │ │ ├── svelte-default.svg │ │ ├── svelte.svg │ │ └── svelte-disabled.svg │ ├── register.js │ ├── manifest.json │ └── background.js │ ├── src │ ├── client │ │ ├── runtime.js │ │ ├── highlight.js │ │ ├── utils.js │ │ ├── core.js │ │ └── svelte-4.js │ ├── entry.ts │ ├── lib │ │ ├── components │ │ │ ├── Relative.svelte │ │ │ ├── Toolbar.svelte │ │ │ ├── Divider.svelte │ │ │ ├── Indexer.svelte │ │ │ ├── Button.svelte │ │ │ └── Resizable.svelte │ │ ├── nodes │ │ │ ├── Ellipsis.svelte │ │ │ ├── Iteration.svelte │ │ │ ├── Slot.svelte │ │ │ ├── Block.svelte │ │ │ ├── ElementAttributes.svelte │ │ │ ├── Element.svelte │ │ │ └── Node.svelte │ │ ├── panel │ │ │ ├── core.svelte.ts │ │ │ ├── Editable.svelte │ │ │ └── PropertyList.svelte │ │ ├── state.svelte.ts │ │ └── runtime.svelte.ts │ ├── routes │ │ ├── ProfileButton.svelte │ │ ├── Inspector.svelte │ │ ├── ConnectMessage.svelte │ │ ├── Breadcrumbs.svelte │ │ ├── ProfilerFrame.svelte │ │ ├── VisibilitySelection.svelte │ │ ├── SearchBox.svelte │ │ └── Profiler.svelte │ ├── app.css │ ├── app.d.ts │ └── App.svelte │ ├── index.html │ ├── svelte.config.js │ ├── rollup.config.js │ ├── vite.config.ts │ ├── package.json │ └── tsconfig.json ├── .github ├── assets │ ├── screenshot-1.1.0.png │ └── screenshot-2.0.0.png ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ ├── quality.yaml │ └── release.yaml ├── .prettierrc ├── package.json ├── LICENSE ├── README.md └── pnpm-lock.yaml /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'workspace/*' 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | .DS_Store 4 | .cache 5 | .env 6 | -------------------------------------------------------------------------------- /workspace/extension/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | 3 | # generated files 4 | static/courier.js 5 | -------------------------------------------------------------------------------- /.github/assets/screenshot-1.1.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveltejs/svelte-devtools/master/.github/assets/screenshot-1.1.0.png -------------------------------------------------------------------------------- /.github/assets/screenshot-2.0.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveltejs/svelte-devtools/master/.github/assets/screenshot-2.0.0.png -------------------------------------------------------------------------------- /workspace/extension/static/register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /workspace/extension/static/icons/default-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveltejs/svelte-devtools/master/workspace/extension/static/icons/default-128.png -------------------------------------------------------------------------------- /workspace/extension/static/icons/default-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveltejs/svelte-devtools/master/workspace/extension/static/icons/default-16.png -------------------------------------------------------------------------------- /workspace/extension/static/icons/default-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveltejs/svelte-devtools/master/workspace/extension/static/icons/default-24.png -------------------------------------------------------------------------------- /workspace/extension/static/icons/default-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveltejs/svelte-devtools/master/workspace/extension/static/icons/default-48.png -------------------------------------------------------------------------------- /workspace/extension/static/icons/default-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveltejs/svelte-devtools/master/workspace/extension/static/icons/default-96.png -------------------------------------------------------------------------------- /workspace/extension/static/icons/disabled-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveltejs/svelte-devtools/master/workspace/extension/static/icons/disabled-16.png -------------------------------------------------------------------------------- /workspace/extension/static/icons/disabled-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveltejs/svelte-devtools/master/workspace/extension/static/icons/disabled-24.png -------------------------------------------------------------------------------- /workspace/extension/static/icons/disabled-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveltejs/svelte-devtools/master/workspace/extension/static/icons/disabled-48.png -------------------------------------------------------------------------------- /workspace/extension/static/icons/disabled-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveltejs/svelte-devtools/master/workspace/extension/static/icons/disabled-96.png -------------------------------------------------------------------------------- /workspace/extension/static/icons/disabled-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveltejs/svelte-devtools/master/workspace/extension/static/icons/disabled-128.png -------------------------------------------------------------------------------- /workspace/extension/src/client/runtime.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} type 3 | * @param {Record} [payload] 4 | */ 5 | export function send(type, payload) { 6 | window.postMessage({ source: 'svelte-devtools', type, payload }); 7 | } 8 | -------------------------------------------------------------------------------- /workspace/extension/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /workspace/extension/src/entry.ts: -------------------------------------------------------------------------------- 1 | import './app.css'; 2 | import App from './App.svelte'; 3 | import { mount } from 'svelte'; 4 | 5 | if (chrome.devtools.panels.themeName === 'dark') { 6 | document.body.classList.add('dark'); 7 | } else { 8 | document.body.classList.remove('dark'); 9 | } 10 | 11 | export default mount(App, { 12 | target: document.querySelector('#app')!, 13 | }); 14 | -------------------------------------------------------------------------------- /workspace/extension/svelte.config.js: -------------------------------------------------------------------------------- 1 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; 2 | 3 | export default { 4 | compilerOptions: { 5 | runes: true, 6 | }, 7 | 8 | preprocess: vitePreprocess(), 9 | 10 | onwarn(warning, handler) { 11 | if (warning.message.includes('A11y')) return; 12 | !warning.message.includes('chrome') && handler(warning); 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /workspace/extension/src/lib/components/Relative.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 |
10 | {@render children()} 11 |
12 | 13 | 21 | -------------------------------------------------------------------------------- /workspace/extension/rollup.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'rollup'; 2 | 3 | export default defineConfig([ 4 | { 5 | input: 'static/background.js', 6 | output: { 7 | file: 'build/background.js', 8 | }, 9 | }, 10 | { 11 | input: 'src/client/core.js', 12 | output: [ 13 | { file: 'static/courier.js', format: 'iife' }, 14 | { file: 'build/courier.js', format: 'iife' }, 15 | ], 16 | }, 17 | ]); 18 | -------------------------------------------------------------------------------- /workspace/extension/static/register.js: -------------------------------------------------------------------------------- 1 | chrome.devtools.panels.create( 2 | 'Svelte', 3 | `icons/svelte-${chrome.devtools.panels.themeName}.svg`, 4 | 'index.html', 5 | // (panel) => { 6 | // panel.onShown.addListener((win) => 7 | // chrome.devtools.inspectedWindow.eval('$0', (payload) => 8 | // win.postMessage({ source: 'svelte-devtools', type: 'bridge::ext/inspect', payload }), 9 | // ), 10 | // ); 11 | // }, 12 | ); 13 | -------------------------------------------------------------------------------- /workspace/extension/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'node:path'; 2 | import { svelte } from '@sveltejs/vite-plugin-svelte'; 3 | import { defineConfig } from 'vite'; 4 | 5 | export default defineConfig(() => { 6 | return { 7 | plugins: [svelte()], 8 | 9 | build: { 10 | cssTarget: 'chrome111', 11 | outDir: 'build', 12 | }, 13 | 14 | publicDir: 'static', 15 | 16 | resolve: { 17 | alias: { 18 | $lib: resolve(__dirname, 'src/lib'), 19 | }, 20 | }, 21 | }; 22 | }); 23 | -------------------------------------------------------------------------------- /workspace/extension/src/lib/components/Toolbar.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | 13 | 27 | -------------------------------------------------------------------------------- /workspace/extension/src/lib/nodes/Ellipsis.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 27 | -------------------------------------------------------------------------------- /workspace/extension/src/lib/nodes/Iteration.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 19 | 20 | {#if expanded} 21 | {@render children()} 22 | {/if} 23 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "semi": true, 4 | "singleQuote": true, 5 | "tabWidth": 2, 6 | "useTabs": true, 7 | "overrides": [ 8 | { 9 | "files": ["pnpm-lock.yaml"], 10 | "options": { 11 | "rangeEnd": 0 12 | } 13 | }, 14 | 15 | { 16 | "files": ["package.json"], 17 | "options": { 18 | "plugins": ["prettier-plugin-sort-package-json"] 19 | } 20 | }, 21 | { 22 | "files": ["*.md"], 23 | "options": { 24 | "tabWidth": 4 25 | } 26 | }, 27 | { 28 | "files": ["*.svelte"], 29 | "options": { 30 | "plugins": ["prettier-plugin-svelte"] 31 | } 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /workspace/extension/src/routes/ProfileButton.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "scripts": { 5 | "clean": "git add * && git clean -dfx -e node_modules", 6 | "check": "pnpm --filter \"./workspace/*\" check", 7 | "format": "pnpm --filter \"./workspace/*\" format" 8 | }, 9 | "packageManager": "pnpm@8.15.6", 10 | "devDependencies": { 11 | "@sveltejs/vite-plugin-svelte": "next", 12 | "@types/node": "^20.16.6", 13 | "prettier": "^3.3.3", 14 | "prettier-plugin-sort-package-json": "^0.2.0", 15 | "prettier-plugin-svelte": "^3.2.6", 16 | "svelte": "5.0.0-next.141", 17 | "svelte-check": "^4.0.2", 18 | "typescript": "^5.6.2", 19 | "vite": "^5.4.7" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /workspace/extension/src/lib/panel/core.svelte.ts: -------------------------------------------------------------------------------- 1 | import { app } from '$lib/state.svelte'; 2 | 3 | export const errors = $state<{ [keys: string]: string | false }>({}); 4 | 5 | export function inject(keys: string[], value: any) { 6 | const uuid = app.selected?.id; 7 | if (!uuid) return; 8 | 9 | const accessors = `[${keys.map((k) => `'${k}'`).join(', ')}]`; 10 | chrome.devtools.inspectedWindow.eval( 11 | `window['#SvelteDevTools'].inject('${uuid}', ${accessors}, ${value})`, 12 | (_, error) => { 13 | const id = `${uuid}+${keys.join('.')}`; 14 | errors[id] = error?.isException && error.value.slice(0, error.value.indexOf('\n')); 15 | }, 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /workspace/extension/src/lib/components/Divider.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 |
11 | 12 | 31 | -------------------------------------------------------------------------------- /workspace/extension/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "scripts": { 5 | "dev": "pnpm run --parallel \"/^dev:.*/\"", 6 | "dev:app": "vite build -wd --minify=false --sourcemap=inline", 7 | "dev:scripts": "rollup -cw", 8 | "build": "rollup -c && vite build", 9 | "bundle:zip": "cd build && zip -r svelte-devtools.zip *", 10 | "bundle:tar": "cd build && tar -czf svelte-devtools.tar.gz *", 11 | "format": "prettier -w .", 12 | "check": "pnpm run --parallel \"/^check:.*/\"", 13 | "check:style": "prettier -c .", 14 | "check:svelte": "svelte-check --tsconfig ./tsconfig.json" 15 | }, 16 | "devDependencies": { 17 | "@types/chrome": "^0.0.266", 18 | "rollup": "^4.22.4" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Environment** 24 | 25 | - Browser with version [e.g. chrome 22, firefox 70.0.1] 26 | - Devtools version [e.g. 1.3.1, built from master] 27 | - Svelte version [e.g. 3.15.0] 28 | 29 | **Additional context** 30 | Add any other context about the problem here. 31 | -------------------------------------------------------------------------------- /workspace/extension/src/lib/components/Indexer.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 |

10 | {#if i === -1 || app.query.length < 2} 11 | {text} 12 | {:else} 13 | {#if i !== 0}{text.slice(0, i)}{/if} 14 | {text.slice(i, i + app.query.length)} 15 | {#if i + app.query.length < text.length} 16 | {text.slice(i + app.query.length)} 17 | {/if} 18 | {/if} 19 |

20 | 21 | 32 | -------------------------------------------------------------------------------- /workspace/extension/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "bundler", 6 | "resolveJsonModule": true, 7 | 8 | "checkJs": true, 9 | "strict": true, 10 | "composite": true, 11 | "noEmit": true, 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "noImplicitReturns": true, 15 | 16 | "skipLibCheck": true, 17 | "isolatedModules": true, 18 | "useDefineForClassFields": true, 19 | "forceConsistentCasingInFileNames": true, 20 | 21 | "paths": { 22 | "$lib": ["./src/lib"], 23 | "$lib/*": ["./src/lib/*"] 24 | } 25 | }, 26 | "include": [ 27 | "vite.config.ts", 28 | "src/**/*.d.ts", 29 | "src/**/*.ts", 30 | "src/**/*.js", 31 | "src/**/*.svelte", 32 | "static/**/*.js" 33 | ], 34 | "exclude": ["static/courier.js"] 35 | } 36 | -------------------------------------------------------------------------------- /workspace/extension/src/routes/Inspector.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 23 | -------------------------------------------------------------------------------- /workspace/extension/src/lib/nodes/Slot.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 23 | {#if expanded} 24 | {@render children()} 25 | 26 |
27 | 28 |
29 | {/if} 30 | -------------------------------------------------------------------------------- /.github/workflows/quality.yaml: -------------------------------------------------------------------------------- 1 | name: QA 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | 8 | jobs: 9 | check: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: pnpm/action-setup@v4 15 | - uses: actions/setup-node@v4 16 | with: 17 | node-version: 20 18 | cache: pnpm 19 | 20 | - run: pnpm install 21 | - run: pnpm check 22 | 23 | # test: 24 | # runs-on: ${{ matrix.os }} 25 | # strategy: 26 | # matrix: 27 | # node-version: [16, 18, 20] 28 | # os: [ubuntu-latest, windows-latest, macOS-latest] 29 | 30 | # steps: 31 | # - uses: actions/checkout@v3 32 | # - uses: pnpm/action-setup@v2 33 | # - uses: actions/setup-node@v3 34 | # with: 35 | # node-version: ${{ matrix.node-version }} 36 | # cache: pnpm 37 | 38 | # - run: pnpm install 39 | # - run: pnpm test 40 | -------------------------------------------------------------------------------- /workspace/extension/src/lib/nodes/Block.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 27 | {#if expanded} 28 | {@render children()} 29 | 30 |
31 | 32 |
33 | {/if} 34 | 35 | 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Christian Timothy Johnson 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 | -------------------------------------------------------------------------------- /workspace/extension/src/lib/state.svelte.ts: -------------------------------------------------------------------------------- 1 | type Overwrite = Omit & B; 2 | 3 | export type DebugNode = Overwrite< 4 | SvelteBlockDetail, 5 | { 6 | expanded: boolean; 7 | detail: { 8 | attributes?: Array<{ 9 | key: string; 10 | value: string; 11 | bounded?: boolean; 12 | flash?: boolean; 13 | }>; 14 | listeners?: Array<{ 15 | event: any; 16 | handler: any; 17 | modifiers: any; 18 | }>; 19 | ctx: any; 20 | source: string; 21 | nodeValue: string; 22 | }; 23 | 24 | tagName: string; 25 | parent: DebugNode; 26 | children: DebugNode[]; 27 | dom?: HTMLLIElement; 28 | } 29 | >; 30 | 31 | export const app = $state({ 32 | nodes: {} as { [key: string]: DebugNode }, 33 | get root() { 34 | const nodes = Object.values(this.nodes); 35 | return nodes.filter((node) => !node.parent); 36 | }, 37 | 38 | selected: undefined as undefined | DebugNode, 39 | hovered: undefined as undefined | DebugNode, 40 | 41 | inspecting: false, 42 | query: '', 43 | }); 44 | 45 | export const visibility = $state<{ [key: string]: boolean }>({ 46 | component: true, 47 | element: true, 48 | block: true, 49 | iteration: true, 50 | slot: true, 51 | text: true, 52 | anchor: false, 53 | }); 54 | -------------------------------------------------------------------------------- /workspace/extension/static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "Svelte DevTools", 4 | "version": "2.2.2", 5 | "description": "Browser DevTools extension for debugging Svelte applications.", 6 | "icons": { 7 | "16": "icons/default-16.png", 8 | "24": "icons/default-24.png", 9 | "48": "icons/default-48.png", 10 | "96": "icons/default-96.png", 11 | "128": "icons/default-128.png" 12 | }, 13 | 14 | "action": { 15 | "default_icon": { 16 | "16": "icons/disabled-16.png", 17 | "24": "icons/disabled-24.png", 18 | "48": "icons/disabled-48.png", 19 | "96": "icons/disabled-96.png", 20 | "128": "icons/disabled-128.png" 21 | } 22 | }, 23 | "background": { 24 | "scripts": ["background.js"], 25 | "service_worker": "background.js" 26 | }, 27 | "content_scripts": [ 28 | { 29 | "matches": [""], 30 | "js": ["courier.js"], 31 | "run_at": "document_start", 32 | "world": "MAIN" 33 | } 34 | ], 35 | "devtools_page": "register.html", 36 | "host_permissions": ["*://*/*"], 37 | "permissions": ["activeTab", "scripting"], 38 | "web_accessible_resources": [{ "matches": ["*://*/*"], "resources": ["courier.js"] }], 39 | 40 | "minimum_chrome_version": "121", 41 | "browser_specific_settings": { 42 | "gecko": { 43 | "id": "firefox-devtools@svelte.dev", 44 | "strict_min_version": "121.0", 45 | "update_url": "https://svelte.dev/devtools/updates.json" 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /workspace/extension/src/routes/ConnectMessage.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 |

Svelte DevTools

7 |

8 | No Svelte app detected 9 | 10 | 11 |

12 | 13 |
14 |

Not working? Did you...

15 |
    16 |
  • Build with dev mode enabled?
  • 17 |
  • Use Svelte version ^4.0.0?
  • 18 |
19 |
20 |
21 | 22 | 67 | -------------------------------------------------------------------------------- /workspace/extension/src/routes/Breadcrumbs.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | {#if breadcrumbs.length} 16 |
    17 | {#each breadcrumbs as node} 18 | {#if visibility[node.type]} 19 | 27 | {/if} 28 | {/each} 29 |
30 | {/if} 31 | 32 | 66 | -------------------------------------------------------------------------------- /workspace/extension/src/routes/ProfilerFrame.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 | {#if children?.length} 21 |
    22 | {#each children as frame} 23 |
  • 24 | 28 | 29 | onclick(frame)} /> 30 |
  • 31 | {/each} 32 |
33 | {/if} 34 | 35 | 73 | -------------------------------------------------------------------------------- /workspace/extension/src/lib/nodes/ElementAttributes.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 | {#each attributes as { key, value, bounded } (key)} 21 | {@const prefix = bounded ? 'bind:' : ''} 22 | 23 |   24 | 25 | 26 | 27 | 28 | = 29 | 30 | 31 | 32 | 33 | {/each} 34 | 35 | {#each listeners as { event, handler, modifiers }} 36 | {@const suffix = modifiers?.length ? `|${modifiers.join('|')}` : ''} 37 | 38 |   39 | 40 | 41 | 42 | {/each} 43 | 44 | 68 | -------------------------------------------------------------------------------- /workspace/extension/src/lib/components/Button.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 16 | 17 | 84 | -------------------------------------------------------------------------------- /workspace/extension/src/lib/components/Resizable.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | (resizing = false)} 15 | onmousemove={({ pageX: x, pageY: y }) => { 16 | if (!resizing) return; 17 | size = axis === 'x' ? window.innerWidth - x : window.innerHeight - y; 18 | }} 19 | /> 20 | 21 | 27 | 28 | 94 | -------------------------------------------------------------------------------- /workspace/extension/static/icons/svelte-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /workspace/extension/static/icons/svelte-default.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /workspace/extension/src/routes/VisibilitySelection.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 18 | 19 | {#if opened} 20 |
21 | {#each Object.keys(visibility) as key} 22 | 26 | {/each} 27 |
28 | {/if} 29 |
30 | 31 | 81 | -------------------------------------------------------------------------------- /workspace/extension/src/client/highlight.js: -------------------------------------------------------------------------------- 1 | const dom = { 2 | area: document.createElement('div'), 3 | x: document.createElement('div'), 4 | y: document.createElement('div'), 5 | }; 6 | 7 | /** @param {Pick} [node] */ 8 | export function highlight(node) { 9 | if (!node || node.type !== 'element' || !node.detail) { 10 | dom.area.remove(); 11 | dom.x.remove(); 12 | dom.y.remove(); 13 | return; 14 | } 15 | 16 | const { clientWidth, scrollHeight } = document.documentElement; 17 | const style = window.getComputedStyle(node.detail); 18 | const rect = node.detail.getBoundingClientRect(); 19 | 20 | // TODO: handle sticky position 21 | const position = style.position === 'fixed' ? 'fixed' : 'absolute'; 22 | const offset = style.position !== 'fixed' ? window.scrollY : 0; 23 | 24 | dom.area.style.setProperty('z-index', '65536'); 25 | dom.area.style.setProperty('background-color', 'rgba(0, 136, 204, 0.2)'); 26 | dom.area.style.setProperty('position', position); 27 | dom.area.style.setProperty('top', `${offset + rect.top}px`); 28 | dom.area.style.setProperty('left', `${rect.left}px`); 29 | dom.area.style.setProperty('width', `${rect.width}px`); 30 | dom.area.style.setProperty('height', `${rect.height}px`); 31 | 32 | dom.x.style.setProperty('z-index', '65536'); 33 | dom.x.style.setProperty('border', '0px dashed rgb(0, 136, 204)'); 34 | dom.x.style.setProperty('border-width', '1px 0px'); 35 | dom.x.style.setProperty('position', position); 36 | dom.x.style.setProperty('top', `${offset + rect.top}px`); 37 | dom.x.style.setProperty('width', `${clientWidth}px`); 38 | dom.x.style.setProperty('height', `${rect.height}px`); 39 | 40 | dom.y.style.setProperty('z-index', '65536'); 41 | dom.y.style.setProperty('border', '0px dashed rgb(0, 136, 204)'); 42 | dom.y.style.setProperty('border-width', '0px 1px'); 43 | dom.y.style.setProperty('position', 'absolute'); 44 | dom.y.style.setProperty('left', `${rect.left}px`); 45 | dom.y.style.setProperty('width', `${rect.width}px`); 46 | dom.y.style.setProperty('height', `${scrollHeight}px`); 47 | 48 | document.body.appendChild(dom.area); 49 | document.body.appendChild(dom.x); 50 | document.body.appendChild(dom.y); 51 | } 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Svelte DevTools 2 | 3 | 4 | 5 | 6 | Chrome Web Store 7 | 8 | 9 | 10 | Svelte DevTools is a browser extension for the [Svelte](https://svelte.dev/) framework. It allows you to inspect the Svelte state and component hierarchies in the Developer Tools. 11 | 12 | After installing you will see a new tab in Developer Tools. This tab displays a tree of Svelte components, HTMLx blocks, and DOM elements that were rendered on the page. By selecting one of the nodes in the tree, you can inspect and edit its current state in the panel to the right. 13 | 14 | > For Firefox users, you can install the [`.xpi` file of the latest version from the GitHub releases page](https://github.com/sveltejs/svelte-devtools/releases/latest). Note that if you grab the `.zip` file, you will need to load it as a temporary extension and enable "Always Allow on localhost" in the extension settings. 15 | 16 | ![2.0.0 Screenshot](./.github/assets/screenshot-2.0.0.png '2.0.0 Screenshot') 17 | 18 | ## Requirements 19 | 20 | The `svelte-devtools` extension requires the following to be true: 21 | 22 | - Chrome or Firefox version 121 or higher 23 | - Application running Svelte version `^4.0.0` 24 | - Application compiled with `dev: true` ([SvelteKit](https://kit.svelte.dev/) does this automatically for you) 25 | 26 | ## Development 27 | 28 | Clone this repository and setup the environment with `pnpm` 29 | 30 | ```sh 31 | git clone https://github.com/sveltejs/svelte-devtools.git 32 | cd svelte-devtools 33 | pnpm install 34 | 35 | cd workspace/extension 36 | pnpm dev 37 | ``` 38 | 39 | To work on the extension, run the `dev` script from `workspace/extension` directory 40 | 41 | ```sh 42 | cd workspace/extension 43 | pnpm dev 44 | ``` 45 | 46 | This will build the extension and create a directory called `build`. Steps may vary depending on the browser you are using, but generally: 47 | 48 | 1. Navigate to the extensions settings page 49 | 2. Turn on the 'Developer mode' switch 50 | 3. Click 'Load Unpacked' and select the `build` directory 51 | 52 | ## Acknowledgements 53 | 54 | - This extension was initially created and developed by [RedHatter](https://github.com/RedHatter) 55 | -------------------------------------------------------------------------------- /workspace/extension/src/lib/nodes/Element.svelte: -------------------------------------------------------------------------------- 1 | 47 | 48 | {#snippet close()} 49 | </ 50 | 51 | 52 | 53 | > 54 | {/snippet} 55 | 56 |
(expanded = !empty && !expanded)} 61 | > 62 | < 63 | 64 | 65 | 66 | 67 | 68 | {#if empty} 69 |  /> 70 | {:else} 71 | > 72 | {#if !expanded} 73 | (expanded = true)} /> 74 | 75 | {@render close()} 76 | {/if} 77 | {/if} 78 |
79 | 80 | {#if expanded} 81 | {@render children()} 82 | 83 |
84 | {@render close()} 85 |
86 | {/if} 87 | 88 | 97 | -------------------------------------------------------------------------------- /workspace/extension/src/lib/panel/Editable.svelte: -------------------------------------------------------------------------------- 1 | 45 | 46 | {#if editing} 47 | 48 | { 52 | // @ts-expect-error - target and value exists 53 | update(target.value); 54 | }} 55 | onkeydown={(event) => { 56 | const { key, target } = event; 57 | if (key === 'Escape') { 58 | event.preventDefault(); 59 | editing = false; 60 | return; 61 | } 62 | if (key !== 'Enter') return; 63 | // @ts-expect-error - target and value exists 64 | update(target.value); 65 | }} 66 | /> 67 | {:else} 68 | 69 | (editing = !readonly)}> 70 | {type === 'string' ? `"${value}"` : `${value}`} 71 | 72 | {/if} 73 | 74 | 106 | -------------------------------------------------------------------------------- /workspace/extension/src/routes/SearchBox.svelte: -------------------------------------------------------------------------------- 1 | 33 | 34 |
event.preventDefault()}> 35 | 36 | 37 | 41 | 42 | 43 | { 47 | if (key === 'Enter') submit[shiftKey ? 'prev' : 'next'](); 48 | }} 49 | /> 50 | 51 | {#if results.length && position > -1} 52 | {position + 1} of {results.length} 53 | {/if} 54 | 55 | 58 | 61 |
62 | 63 | 99 | -------------------------------------------------------------------------------- /workspace/extension/src/app.css: -------------------------------------------------------------------------------- 1 | :root { 2 | tab-size: 2; 3 | 4 | --background: rgb(255, 255, 255); 5 | --color: rgb(74, 74, 79); 6 | 7 | --t-duration: 240ms; 8 | } 9 | 10 | *, 11 | *::before, 12 | *::after { 13 | box-sizing: border-box; 14 | } 15 | 16 | html { 17 | height: 100%; 18 | } 19 | 20 | body { 21 | display: flex; 22 | margin: 0; 23 | height: 100%; 24 | background: var(--background); 25 | color: var(--color); 26 | font-family: monospace; 27 | } 28 | body.dark { 29 | --color: rgb(177, 177, 179); 30 | --background: rgb(36, 36, 36); 31 | scrollbar-color: rgb(115, 115, 115) rgb(60, 60, 61); 32 | } 33 | 34 | /* dark mode scrollbar */ 35 | body.dark ::-webkit-scrollbar { 36 | width: 0.75rem; 37 | height: 0.5rem; 38 | background-color: transparent; 39 | } 40 | body.dark ::-webkit-scrollbar-thumb { 41 | background-color: rgb(51, 51, 51); 42 | border: 1px solid rgba(255, 255, 255, 0.2); 43 | border-radius: 0.25rem; 44 | } 45 | 46 | /* basic resets */ 47 | ul { 48 | margin: 0; 49 | padding: 0; 50 | list-style: none; 51 | } 52 | 53 | /* expandable arrows */ 54 | .expandable::before { 55 | content: ''; 56 | position: absolute; 57 | top: 0; 58 | left: calc(var(--indent, 6px) - 6px); 59 | 60 | border-top: 0.375rem solid rgba(135, 135, 137, 0.9); 61 | border-right: 0.25rem solid transparent; 62 | border-left: 0.25rem solid transparent; 63 | transition-duration: var(--t-duration); 64 | transform: translate3d(0%, calc(50% + 0.25rem + var(--y-pad, 0px)), 0) rotate(-90deg); 65 | /* transform: translate3d(-150%, calc(50% + 0.25rem + var(--y-pad, 0px)), 0) rotate(-90deg); */ 66 | } 67 | .expandable.expanded::before { 68 | transform: translate3d(0%, calc(50% + 0.25rem + var(--y-pad, 0px)), 0) rotate(0deg); 69 | /* transform: translate3d(-150%, calc(50% + 0.25rem + var(--y-pad, 0px)), 0) rotate(0deg); */ 70 | } 71 | 72 | /* tooltip pseudo-elements */ 73 | [data-tooltip]:hover::after, 74 | [data-tooltip]:hover::before { 75 | opacity: 1; 76 | pointer-events: auto; 77 | } 78 | [data-tooltip]::before { 79 | content: ''; 80 | opacity: 0; 81 | pointer-events: none; 82 | 83 | position: absolute; 84 | bottom: -0.2rem; 85 | left: 2.5rem; 86 | border-right: 0.5rem solid transparent; 87 | border-bottom: 0.5rem solid rgb(48, 64, 81); 88 | border-left: 0.5rem solid transparent; 89 | transition: opacity 0.2s; 90 | } 91 | [data-tooltip]::after { 92 | content: attr(data-tooltip); 93 | opacity: 0; 94 | pointer-events: none; 95 | z-index: 1; 96 | position: absolute; 97 | bottom: 0; 98 | left: 0; 99 | 100 | display: flex; 101 | padding: 0.25rem 0.375rem; 102 | border-radius: 0.25rem; 103 | transition: opacity 0.2s; 104 | transform: translateY(100%); 105 | 106 | background-color: rgb(48, 64, 81); 107 | color: white; 108 | } 109 | -------------------------------------------------------------------------------- /workspace/extension/static/icons/svelte.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /workspace/extension/static/icons/svelte-disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /workspace/extension/src/client/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {unknown} value 3 | * @returns {any} 4 | */ 5 | function clone(value, seen = new Map()) { 6 | switch (typeof value) { 7 | case 'function': 8 | return { __is: 'function', source: value.toString(), name: value.name }; 9 | case 'symbol': 10 | return { __is: 'symbol', name: value.toString() }; 11 | case 'object': { 12 | if (value === window || value === null) return null; 13 | if (Array.isArray(value)) return value.map((o) => clone(o, seen)); 14 | if (seen.has(value)) return {}; 15 | 16 | /** @type {Record} */ 17 | const o = {}; 18 | seen.set(value, o); 19 | 20 | const descriptors = Object.getOwnPropertyDescriptors(value); 21 | for (const [key, v] of Object.entries(value)) { 22 | const { get, writable } = descriptors[key]; 23 | const readonly = !writable || get !== undefined; 24 | o[key] = { key, value: clone(v, seen), readonly }; 25 | } 26 | return o; 27 | } 28 | default: 29 | return value; 30 | } 31 | } 32 | 33 | /** @param {SvelteBlockDetail} node */ 34 | export function serialize(node) { 35 | const res = /** @type {SvelteBlockDetail} */ ({ 36 | id: node.id, 37 | type: node.type, 38 | tagName: node.tagName, 39 | detail: {}, 40 | }); 41 | switch (node.type) { 42 | case 'component': { 43 | const { $$: internal = {} } = node.detail; 44 | const captured = node.detail.$capture_state?.() || {}; 45 | const bindings = Object.values(internal.bound || {}).map( 46 | /** @param {Function} f */ (f) => f.name, 47 | ); 48 | const props = Object.keys(internal.props || {}).flatMap((key) => { 49 | const value = clone(captured[key]); 50 | delete captured[key]; // deduplicate for ctx 51 | if (value === undefined) return []; 52 | 53 | const bounded = bindings.some((f) => f.includes(key)); 54 | return { key, value, bounded }; 55 | }); 56 | 57 | res.detail = { 58 | attributes: props, 59 | listeners: Object.entries(internal.callbacks || {}).flatMap(([event, value]) => 60 | value.map(/** @param {Function} f */ (f) => ({ event, handler: f.toString() })), 61 | ), 62 | ctx: Object.entries(captured).map(([key, v]) => ({ key, value: clone(v) })), 63 | }; 64 | break; 65 | } 66 | 67 | case 'element': { 68 | /** @type {Attr[]} from {NamedNodeMap} */ 69 | const attributes = Array.from(node.detail.attributes || []); 70 | 71 | /** @type {NonNullable} */ 72 | const listeners = node.detail.__listeners || []; 73 | 74 | res.detail = { 75 | attributes: attributes.map(({ name: key, value }) => ({ key, value, readonly: true })), 76 | listeners: listeners.map((o) => ({ ...o, handler: o.handler.toString() })), 77 | }; 78 | break; 79 | } 80 | 81 | case 'text': { 82 | res.detail = { 83 | nodeValue: node.detail.nodeValue, 84 | }; 85 | break; 86 | } 87 | 88 | case 'iteration': 89 | case 'block': { 90 | const { ctx, source } = node.detail; 91 | const cloned = Object.entries(clone(ctx)); 92 | res.detail = { 93 | ctx: cloned.map(([key, value]) => ({ key, value, readonly: true })), 94 | source: source.slice(source.indexOf('{'), source.indexOf('}') + 1), 95 | }; 96 | break; 97 | } 98 | } 99 | 100 | return res; 101 | } 102 | -------------------------------------------------------------------------------- /workspace/extension/src/app.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | interface SvelteDevInternal { 5 | version: string; 6 | } 7 | 8 | declare global { 9 | type SvelteComponentDetail = { 10 | id: string; 11 | options: { 12 | $$inline?: boolean; 13 | hydrate?: boolean; 14 | target?: Element; 15 | props?: Record; 16 | }; 17 | tagName: string; 18 | component: { 19 | $$: { 20 | fragment: { 21 | c(): void; 22 | d(detaching: boolean): void; 23 | h(): void; 24 | l(nodes: any[]): void; 25 | m(target: Node, anchor: Node): void; 26 | p(changed: boolean, ctx: any): void; 27 | }; 28 | }; 29 | $$events_def?: {}; 30 | $$prop_def?: {}; 31 | $$slot_def?: {}; 32 | $capture_state(): any; 33 | }; 34 | }; 35 | 36 | type SvelteBlockDetail = { 37 | id: string; // crypto.randomUUID(); 38 | source: string; 39 | type: 40 | | 'anchor' 41 | | 'block' 42 | | 'catch' 43 | | 'component' 44 | | 'each' 45 | | 'element' 46 | | 'else' 47 | | 'if' 48 | | 'iteration' 49 | | 'key' 50 | | 'pending' 51 | | 'slot' 52 | | 'text' 53 | | 'then'; 54 | 55 | detail?: any; 56 | tagName?: string; 57 | 58 | children: SvelteBlockDetail[]; 59 | /** `type: 'element' | 'component'` */ 60 | parent?: SvelteBlockDetail; 61 | /** like `parent` but `type: 'component'` */ 62 | container?: SvelteBlockDetail; 63 | 64 | block: SvelteComponentDetail['component']['$$']['fragment']; 65 | ctx: Array; // TODO: do we need this typed? 66 | }; 67 | 68 | type SvelteListenerDetail = { 69 | node: Node & { 70 | __listeners?: Omit[]; 71 | }; 72 | event: string; 73 | handler: EventListenerOrEventListenerObject; 74 | modifiers: Array<'capture' | 'preventDefault' | 'stopPropagation' | 'stopImmediatePropagation'>; 75 | }; 76 | 77 | interface DocumentEventMap { 78 | SvelteRegisterComponent: CustomEvent; 79 | 80 | SvelteRegisterBlock: CustomEvent; 81 | 82 | SvelteDOMInsert: CustomEvent; 83 | SvelteDOMRemove: CustomEvent; 84 | 85 | SvelteDOMAddEventListener: CustomEvent; 86 | SvelteDOMRemoveEventListener: CustomEvent; 87 | 88 | SvelteDOMSetAttribute: CustomEvent< 89 | SvelteDevInternal & { 90 | node: Element; 91 | attribute: string; 92 | value?: string; 93 | } 94 | >; 95 | SvelteDOMRemoveAttribute: CustomEvent< 96 | SvelteDevInternal & { 97 | node: Element; 98 | attribute: string; 99 | } 100 | >; 101 | SvelteDOMSetProperty: CustomEvent< 102 | SvelteDevInternal & { 103 | node: Element; 104 | property: string; 105 | value?: any; 106 | } 107 | >; 108 | SvelteDOMSetDataset: CustomEvent< 109 | SvelteDevInternal & { 110 | node: HTMLElement; 111 | property: string; 112 | value?: any; 113 | } 114 | >; 115 | SvelteDOMSetData: CustomEvent< 116 | SvelteDevInternal & { 117 | node: Text; 118 | data: unknown; 119 | } 120 | >; 121 | 122 | SvelteDevTools: CustomEvent<{ 123 | type: string; 124 | payload: string; 125 | }>; 126 | } 127 | } 128 | 129 | export {}; 130 | -------------------------------------------------------------------------------- /workspace/extension/src/client/core.js: -------------------------------------------------------------------------------- 1 | import { highlight } from './highlight.js'; 2 | import { send } from './runtime.js'; 3 | import { index as v4 } from './svelte-4.js'; 4 | import { serialize } from './utils.js'; 5 | 6 | // @ts-ignore - https://developer.chrome.com/docs/extensions/how-to/devtools/extend-devtools#selected-element 7 | window['#SvelteDevTools'] = { 8 | /** 9 | * @param {string} id 10 | * @param {string[]} keys 11 | * @param {any} value 12 | */ 13 | inject(id, keys, value) { 14 | const { detail: component } = v4.map.get(id) || {}; 15 | if (component) { 16 | const [prop, ...rest] = keys; 17 | const original = component.$capture_state()[prop]; 18 | if (typeof original === 'object') { 19 | let ref = original; 20 | for (let i = 0; i < rest.length - 1; i += 1) { 21 | ref = ref[rest[i]]; 22 | } 23 | ref[rest[rest.length - 1]] = value; 24 | component.$inject_state({ [prop]: original }); 25 | } else { 26 | component.$inject_state({ [prop]: value }); 27 | } 28 | } 29 | }, 30 | }; 31 | 32 | const previous = { 33 | /** @type {HTMLElement | null} */ 34 | target: null, 35 | style: { 36 | cursor: '', 37 | background: '', 38 | outline: '', 39 | }, 40 | 41 | clear() { 42 | if (this.target) { 43 | for (const key in this.style) { 44 | // @ts-expect-error - trust me TS 45 | this.target.style[key] = this.style[key]; 46 | } 47 | } 48 | this.target = null; 49 | }, 50 | }; 51 | 52 | const inspect = { 53 | /** @param {MouseEvent} event */ 54 | handle({ target }) { 55 | const same = previous.target && previous.target === target; 56 | const html = target instanceof HTMLElement; 57 | if (same || !html) return; 58 | 59 | if (previous.target) previous.clear(); 60 | previous.target = target; 61 | previous.style = { 62 | cursor: target.style.cursor, 63 | background: target.style.background, 64 | outline: target.style.outline, 65 | }; 66 | target.style.cursor = 'pointer'; 67 | target.style.background = 'rgba(0, 136, 204, 0.2)'; 68 | target.style.outline = '1px dashed rgb(0, 136, 204)'; 69 | }, 70 | /** @param {MouseEvent} event */ 71 | click(event) { 72 | event.preventDefault(); 73 | document.removeEventListener('mousemove', inspect.handle, true); 74 | const node = v4.map.get(/** @type {Node} */ (event.target)); 75 | if (node) send('bridge::ext/inspect', { node: serialize(node) }); 76 | previous.clear(); 77 | }, 78 | }; 79 | 80 | window.addEventListener('message', ({ data, source }) => { 81 | // only accept messages from our application or script 82 | if (source !== window || data?.source !== 'svelte-devtools') return; 83 | 84 | if (data.type === 'bridge::ext/select') { 85 | const node = v4.map.get(data.payload); 86 | // @ts-expect-error - saved for `devtools.inspect()` 87 | if (node) window.$n = node.detail; 88 | } else if (data.type === 'bridge::ext/highlight') { 89 | const node = v4.map.get(data.payload); 90 | return highlight(node); 91 | } else if (data.type === 'bridge::ext/inspect') { 92 | switch (data.payload) { 93 | case 'start': { 94 | document.addEventListener('mousemove', inspect.handle, true); 95 | document.addEventListener('click', inspect.click, { 96 | capture: true, 97 | once: true, 98 | }); 99 | break; 100 | } 101 | default: { 102 | document.removeEventListener('mousemove', inspect.handle, true); 103 | document.removeEventListener('click', inspect.click, true); 104 | previous.clear(); 105 | } 106 | } 107 | } 108 | }); 109 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: REL 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | 8 | jobs: 9 | manifest: 10 | runs-on: ubuntu-latest 11 | outputs: 12 | version: ${{ steps.extract.outputs.version }} 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | - id: extract 17 | working-directory: ./workspace/extension/static 18 | run: echo "version=$(jq -r '.version' manifest.json)" >> $GITHUB_OUTPUT 19 | 20 | bundle: 21 | runs-on: ubuntu-latest 22 | needs: manifest 23 | 24 | steps: 25 | - uses: actions/checkout@v4 26 | - uses: pnpm/action-setup@v4 27 | - uses: actions/setup-node@v4 28 | with: 29 | node-version: 20 30 | cache: pnpm 31 | 32 | - run: pnpm install 33 | - working-directory: ./workspace/extension 34 | run: pnpm build && cd build && zip -r svelte-devtools * 35 | 36 | - uses: actions/upload-artifact@v4 37 | with: 38 | name: extension-${{ github.sha }} 39 | path: workspace/extension/build/svelte-devtools.zip 40 | 41 | - if: github.event_name == 'pull_request' 42 | env: 43 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 44 | PR_NUMBER: ${{ github.event.number }} 45 | WORKFLOW: ${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts 46 | run: | 47 | url=https://github.com/$WORKFLOW/$(gh api repos/$WORKFLOW --jq '.artifacts[0].id') 48 | commented=$(gh pr view $PR_NUMBER --json comments --jq '.comments[].author.login | select(. | contains("github-actions"))') 49 | body="Try the changes in this PR by [side-loading the built extension]($url). :rocket:" 50 | 51 | if [ -z "$commented" ]; then 52 | gh pr comment $PR_NUMBER --body "$body" 53 | else 54 | gh pr comment $PR_NUMBER --edit-last --body "$body" 55 | fi 56 | 57 | publish: 58 | runs-on: ubuntu-latest 59 | needs: [manifest, bundle] 60 | 61 | if: | 62 | github.repository == 'sveltejs/svelte-devtools' && 63 | github.event_name == 'push' && github.ref == 'refs/heads/master' && 64 | startsWith(github.event.head_commit.message, format('~ v{0}', needs.manifest.outputs.version)) 65 | 66 | steps: 67 | - uses: actions/checkout@v4 68 | - uses: pnpm/action-setup@v4 69 | - uses: actions/download-artifact@v4 70 | 71 | - working-directory: extension-${{ github.sha }} 72 | run: | 73 | pnpm dlx web-ext sign --channel unlisted \ 74 | --api-key ${{ secrets.WEB_EXT_API_KEY }} \ 75 | --api-secret ${{ secrets.WEB_EXT_API_SECRET }} 76 | 77 | - working-directory: extension-${{ github.sha }} 78 | env: 79 | GH_TOKEN: ${{ github.token }} 80 | run: | # https://cli.github.com/manual/gh_release_create 81 | gh release create v${{ needs.manifest.outputs.version }} \ 82 | svelte-devtools.zip web-ext-artifacts/*.xpi#svelte-devtools.xpi \ 83 | --title ${{ needs.manifest.outputs.version }} \ 84 | --draft --generate-notes --notes ' 85 | Built from ${{ github.event.head_commit.id }} at https://github.com/sveltejs/svelte-devtools/actions/runs/${{ github.run_id }} 86 | - Chrome Web Store: https://chrome.google.com/webstore/detail/svelte-devtools/kfidecgcdjjfpeckbblhmfkhmlgecoff 87 | - Firefox Signed Add-on: https://github.com/sveltejs/svelte-devtools/releases/download/v${{ needs.manifest.outputs.version }}/svelte-devtools.xpi 88 | ---' 89 | 90 | # TODO: publish to Chrome Web Store 91 | -------------------------------------------------------------------------------- /workspace/extension/src/routes/Profiler.svelte: -------------------------------------------------------------------------------- 1 | 30 | 31 | 32 | {#if top} 33 | 38 | {:else} 39 | 40 | {/if} 41 | 54 | 55 |
56 | {#if children.length} 57 | { 61 | if (selected === frame) top = frame; 62 | else selected = frame; 63 | }} 64 | /> 65 | {:else} 66 |

Nothing to display. Perform an action or refresh the page.

67 | {/if} 68 |
69 | {#if selected} 70 | 71 |
72 |
73 | Tag name 74 | {selected.node.tagName} 75 | (#{selected.node.id}) 76 |
77 |
78 | Start 79 | {round(selected.start)}ms 80 |
81 |
82 | Operation 83 | {selected.type} 84 |
85 |
86 | Block type 87 | {selected.node.type} 88 |
89 |
90 | End 91 | {round(selected.end)}ms 92 |
93 |
94 | Duration 95 | 96 | {round(selected.children.reduce((acc, o) => acc - o.duration, selected.duration))}ms 97 | 98 | of 99 | {round(selected.duration)}ms 100 |
101 |
102 |
103 | {/if} 104 | 105 | 134 | -------------------------------------------------------------------------------- /workspace/extension/src/lib/runtime.svelte.ts: -------------------------------------------------------------------------------- 1 | import { type DebugNode, app } from './state.svelte'; 2 | 3 | const tabId = chrome.devtools.inspectedWindow.tabId; 4 | let port = chrome.runtime.connect({ name: `${tabId}` }); 5 | 6 | port.postMessage({ source: 'svelte-devtools', tabId, type: 'bypass::ext/init' }); 7 | 8 | export const background = { 9 | send(type: `bridge::${'ext' | 'page'}/${string}` | 'bypass::ext/page->refresh', payload?: any) { 10 | try { 11 | port.postMessage({ source: 'svelte-devtools', tabId, type, payload }); 12 | } catch { 13 | // https://developer.chrome.com/docs/extensions/develop/concepts/messaging#port-lifetime 14 | // chrome aggressively disconnects the port, not much we can do other than to reconnect 15 | port = chrome.runtime.connect({ name: `${tabId}` }); 16 | background.send(type, payload); // retry immediately 17 | } 18 | }, 19 | }; 20 | 21 | function resolveEventBubble(node: any) { 22 | if (!node.detail || !node.detail.listeners) return; 23 | 24 | for (const listener of node.detail.listeners) { 25 | if (!listener.handler.includes('bubble($$self, event)')) continue; 26 | 27 | listener.handler = () => { 28 | let target = node; 29 | while ((target = target.parent)) if (target.type === 'component') break; 30 | 31 | const listeners = target.detail.listeners; 32 | if (!listeners) return null; 33 | 34 | const parentListener = listeners.find((o: any) => o.event === listener.event); 35 | if (!parentListener) return null; 36 | 37 | const handler = parentListener.handler; 38 | if (!handler) return null; 39 | 40 | return `// From parent\n${handler}`; 41 | }; 42 | } 43 | } 44 | 45 | port.onMessage.addListener(({ type, payload }) => { 46 | switch (type) { 47 | case 'bridge::ext/clear': { 48 | app.nodes = {}; 49 | app.selected = undefined; 50 | app.hovered = undefined; 51 | break; 52 | } 53 | 54 | case 'bridge::ext/inspect': { 55 | if (typeof payload === 'string') break; 56 | app.selected = app.nodes[payload.node.id]; 57 | app.inspecting = false; 58 | break; 59 | } 60 | 61 | case 'bridge::courier/node->add': { 62 | const { node, target, anchor } = payload as { 63 | node: DebugNode; 64 | target: string; 65 | anchor: string; 66 | }; 67 | 68 | node.parent = app.nodes[target]; 69 | node.children = []; 70 | node.expanded = false; 71 | resolveEventBubble(node); 72 | 73 | app.nodes[node.id] = node; 74 | if (!node.parent) break; 75 | 76 | const siblings = node.parent.children; 77 | const index = siblings.findIndex((n) => n.id === anchor); 78 | if (index === -1) siblings.push(node); 79 | else siblings.splice(index, 0, node); 80 | 81 | break; 82 | } 83 | 84 | case 'bridge::courier/node->remove': { 85 | const node = payload.node as SvelteBlockDetail; 86 | const current = app.nodes[node.id]; 87 | if (current) delete app.nodes[current.id]; 88 | if (!current?.parent) break; 89 | 90 | const index = current.parent.children.findIndex((o) => o.id === current.id); 91 | current.parent.children.splice(index, 1); 92 | break; 93 | } 94 | 95 | case 'bridge::courier/node->update': { 96 | const node = payload.node as SvelteBlockDetail; 97 | const current = app.nodes[node.id]; 98 | if (!current) break; 99 | Object.assign(current, node); 100 | resolveEventBubble(current); 101 | break; 102 | } 103 | 104 | // case 'bridge::courier/profile->update': { 105 | // resolveFrame(frame); 106 | // profileFrame.set(frame); 107 | // break; 108 | 109 | // function resolveFrame(frame) { 110 | // frame.children.forEach(resolveFrame); 111 | 112 | // if (!frame.node) return; 113 | 114 | // frame.node = app.nodes.get(frame.node) || { 115 | // type: 'Unknown', 116 | // tagName: 'Unknown', 117 | // }; 118 | // } 119 | // } 120 | } 121 | }); 122 | -------------------------------------------------------------------------------- /workspace/extension/src/lib/panel/PropertyList.svelte: -------------------------------------------------------------------------------- 1 | 21 | 22 | {#if entries.length} 23 |
    24 | {#each entries as { key, value, readonly = false } (key)} 25 | {@const keys = [...parents, key]} 26 | {@const type = typeof value} 27 | 28 | 29 |
  • { 42 | event.stopPropagation(); 43 | expanded[key] = !expanded[key]; 44 | }} 45 | > 46 | {key}: 47 |   48 | 49 | {#if type === 'string'} 50 | inject(keys, updated)} 55 | /> 56 | {:else if value == null || value !== value} 57 | inject(keys, updated)} 62 | /> 63 | {:else if type === 'number' || type === 'boolean'} 64 | inject(keys, updated)} 69 | /> 70 | {:else if Array.isArray(value)} 71 | Array [{value.length || ''}] 72 | 73 | {#if value.length && expanded[key]} 74 | {@const entries = value.map((v, i) => ({ key: `${i}`, value: v, readonly }))} 75 | 76 | 77 | {/if} 78 | {:else if type === 'object'} 79 | {#if value.__is === 'function'} 80 | function {value.name || ''}() 81 | {#if expanded[key]}
    {value.source}
    {/if} 82 | {:else if value.__is === 'symbol'} 83 | {value.name || 'Symbol()'} 84 | {:else if Object.keys(value).length} 85 | Object {…} 86 | 87 | {#if expanded[key]} 88 | 89 | {/if} 90 | {:else} 91 | Object { } 92 | {/if} 93 | {/if} 94 |
  • 95 | {/each} 96 |
97 | {:else} 98 |
None
99 | {/if} 100 | 101 | 141 | -------------------------------------------------------------------------------- /workspace/extension/src/lib/nodes/Node.svelte: -------------------------------------------------------------------------------- 1 | 25 | 26 | {#snippet expand(children: (typeof node)['children'], level: number)} 27 | {#each children as child (child.id)} 28 | 29 | {/each} 30 | {/snippet} 31 | 32 | {#if visibility[node.type]} 33 | 34 |
  • Object.assign(prev, node)} 43 | onclick={(event) => { 44 | event.stopPropagation(); 45 | app.selected = node; 46 | }} 47 | onmousemove={(event) => { 48 | event.stopPropagation(); 49 | if (app.hovered?.id === node.id) return; 50 | background.send('bridge::ext/highlight', node.id); 51 | app.hovered = node; 52 | }} 53 | > 54 | {#if node.type === 'component' || node.type === 'element'} 55 | 62 |
      {@render expand(node.children, depth + 1)}
    63 |
    64 | {:else if node.type === 'block'} 65 | 66 |
      {@render expand(node.children, depth + 1)}
    67 |
    68 | {:else if node.type === 'iteration'} 69 | 70 |
      {@render expand(node.children, depth + 1)}
    71 |
    72 | {:else if node.type === 'slot'} 73 | 74 |
      {@render expand(node.children, depth + 1)}
    75 |
    76 | {:else if node.type === 'text'} 77 |
    78 | 79 |
    80 | {:else if node.type === 'anchor'} 81 |
    #anchor
    82 | {/if} 83 |
  • 84 | {:else} 85 | {@render expand(node.children, depth)} 86 | {/if} 87 | 88 | 162 | -------------------------------------------------------------------------------- /workspace/extension/static/background.js: -------------------------------------------------------------------------------- 1 | /** @type {Map} */ 2 | const ports = new Map(); 3 | 4 | chrome.runtime.onConnect.addListener((port) => { 5 | if (port.sender?.url !== chrome.runtime.getURL('/index.html')) { 6 | console.error(`Unexpected connection from ${port.sender?.url || ''}`); 7 | return port.disconnect(); 8 | } 9 | 10 | // messages are from the devtools page and not content script (courier.js) 11 | port.onMessage.addListener((message, sender) => { 12 | switch (message.type) { 13 | case 'bypass::ext/init': { 14 | ports.set(message.tabId, sender); 15 | if (!chrome.tabs.onUpdated.hasListener(courier)) { 16 | chrome.tabs.onUpdated.addListener(courier); 17 | } 18 | break; 19 | } 20 | case 'bypass::ext/page->refresh': { 21 | chrome.tabs.reload(message.tabId, { bypassCache: true }); 22 | break; 23 | } 24 | 25 | default: // relay messages from devtools to tab 26 | chrome.tabs.sendMessage(message.tabId, message); 27 | } 28 | }); 29 | 30 | port.onDisconnect.addListener((disconnected) => { 31 | ports.delete(+disconnected.name); 32 | 33 | if (ports.size === 0) { 34 | chrome.tabs.onUpdated.removeListener(courier); 35 | } 36 | }); 37 | }); 38 | 39 | // relay messages from `chrome.scripting` to devtools page 40 | chrome.runtime.onMessage.addListener((message, sender) => { 41 | if (sender.id !== chrome.runtime.id) return; // unexpected sender 42 | 43 | if (message.type === 'bypass::ext/icon:set') { 44 | const selected = message.payload ? 'default' : 'disabled'; 45 | const icons = [16, 24, 48, 96, 128].map((s) => [s, `icons/${selected}-${s}.png`]); 46 | return chrome.action.setIcon({ path: Object.fromEntries(icons) }); 47 | } 48 | 49 | const port = sender.tab?.id && ports.get(sender.tab.id); 50 | if (port) return port.postMessage(message); 51 | }); 52 | 53 | /** @type {Parameters[0]} */ 54 | function courier(tabId, changed) { 55 | if (!ports.has(tabId) || changed.status !== 'loading') return; 56 | 57 | chrome.scripting.executeScript({ 58 | target: { tabId }, 59 | 60 | // ensures we're listening to the events before they're dispatched 61 | injectImmediately: true, 62 | 63 | // no lexical context, `func` is serialized and deserialized. 64 | // a limbo world where both `chrome` and `window` are defined 65 | // with many unexpected and out of the ordinary behaviors, do 66 | // minimal work here and delegate to `courier.js` in the page. 67 | // only a subset of APIs are available in this `chrome` limbo 68 | // - chrome.csi->f() 69 | // - chrome.dom.{openOrClosedShadowRoot->f()} 70 | // - chrome.extension.{ViewType, inIncognitoContext} 71 | // - chrome.i18n 72 | // - chrome.runtime 73 | func: () => { 74 | chrome.runtime.onMessage.addListener((message, sender) => { 75 | if (sender.id !== chrome.runtime.id) return; // unexpected sender 76 | window.postMessage(message); // relay to content script (courier.js) 77 | }); 78 | 79 | window.addEventListener('message', ({ source, data }) => { 80 | // only accept messages from our application or script 81 | if (source === window && data?.source === 'svelte-devtools') { 82 | chrome.runtime.sendMessage(data); 83 | } 84 | }); 85 | 86 | window.addEventListener('unload', () => { 87 | chrome.runtime.sendMessage({ type: 'bridge::ext/clear' }); 88 | }); 89 | }, 90 | }); 91 | } 92 | 93 | chrome.tabs.onActivated.addListener(({ tabId }) => sensor(tabId)); 94 | chrome.tabs.onUpdated.addListener( 95 | (tabId, changed) => changed.status === 'complete' && sensor(tabId), 96 | ); 97 | 98 | /** @param {number} tabId */ 99 | async function sensor(tabId) { 100 | try { 101 | // add SvelteDevTools event listener 102 | await chrome.scripting.executeScript({ 103 | target: { tabId }, 104 | func: () => { 105 | document.addEventListener('SvelteDevTools', ({ detail }) => { 106 | chrome.runtime.sendMessage(detail); 107 | }); 108 | }, 109 | }); 110 | // capture data to send to listener 111 | await chrome.scripting.executeScript({ 112 | target: { tabId }, 113 | world: 'MAIN', 114 | func: () => { 115 | // @ts-ignore - injected if the website is using svelte 116 | const [major] = [...(window.__svelte?.v ?? [])]; 117 | 118 | document.dispatchEvent( 119 | new CustomEvent('SvelteDevTools', { 120 | detail: { type: 'bypass::ext/icon:set', payload: major }, 121 | }), 122 | ); 123 | }, 124 | }); 125 | } catch { 126 | // for internal URLs like `chrome://` or `edge://` and extension gallery 127 | // https://chromium.googlesource.com/chromium/src/+/ee77a52baa1f8a98d15f9749996f90e9d3200f2d/chrome/common/extensions/chrome_extensions_client.cc#131 128 | const icons = [16, 24, 48, 96, 128].map((s) => [s, `icons/disabled-${s}.png`]); 129 | chrome.action.setIcon({ path: Object.fromEntries(icons) }); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /workspace/extension/src/App.svelte: -------------------------------------------------------------------------------- 1 | 48 | 49 | { 51 | const { target, key } = event; 52 | if (target !== document.body || !app.selected) return; 53 | 54 | if (key === 'Enter') { 55 | app.selected.expanded = !app.selected.expanded; 56 | } else if (key === 'ArrowRight') { 57 | event.preventDefault(); 58 | if (!app.selected) app.selected = app.root[0]; 59 | app.selected.expanded = true; 60 | } else if (key === 'ArrowLeft') { 61 | event.preventDefault(); 62 | if (app.selected.expanded) { 63 | app.selected.expanded = false; 64 | return; 65 | } 66 | do app.selected = app.selected.parent ?? app.selected; 67 | while (!visibility[app.selected.type]); 68 | } else if (key === 'ArrowUp') { 69 | event.preventDefault(); 70 | let nodes = (app.selected.parent?.children || app.root).filter((n) => visibility[n.type]); 71 | let sibling = nodes[nodes.findIndex((o) => o.id === app.selected?.id) - 1]; 72 | while (sibling?.expanded) { 73 | nodes = sibling.children.filter((n) => visibility[n.type]); 74 | sibling = nodes[nodes.length - 1]; 75 | } 76 | app.selected = sibling ?? app.selected.parent ?? app.selected; 77 | } else if (key === 'ArrowDown') { 78 | event.preventDefault(); 79 | const children = app.selected.children.filter((n) => visibility[n.type]); 80 | 81 | if (!app.selected.expanded || children.length === 0) { 82 | let next = app.selected; 83 | let current = app.selected; 84 | do { 85 | const nodes = current.parent ? current.parent.children : app.root; 86 | const siblings = nodes.filter((n) => visibility[n.type]); 87 | const index = siblings.findIndex((o) => o.id === current.id); 88 | next = siblings[index + 1]; 89 | current = current.parent; 90 | } while (!next && current); 91 | 92 | app.selected = next ?? app.selected; 93 | } else { 94 | app.selected = children[0]; 95 | } 96 | } 97 | }} 98 | /> 99 | 100 | {#if app.root.length} 101 |
    102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 125 | 126 | 127 |
      event.currentTarget === event.target && reset()} 130 | onmouseleave={reset} 131 | > 132 | {#each app.root as node (node.id)} 133 | 134 | {/each} 135 |
    136 | 137 | 138 |
    139 | 140 | 141 | 142 | {@const events = app.selected?.detail.listeners?.map((l) => { 143 | const suffix = l.modifiers?.length ? `|${l.modifiers.join('|')}` : ''; 144 | const value = { __is: 'function', source: l.handler }; 145 | return { key: l.event + suffix, value }; 146 | })} 147 | 148 | {#if app.selected?.type === 'component'} 149 |

    Props

    150 | 151 | 152 | 153 | 154 |

    Events

    155 | 156 | 157 | 158 | 159 |

    State

    160 | 161 | {:else if app.selected?.type === 'block' || app.selected?.type === 'iteration'} 162 |

    State

    163 | 164 | {:else if app.selected?.type === 'element'} 165 |

    Attributes

    166 | 167 | 168 | 169 | 170 |

    Events

    171 | 172 | {/if} 173 |
    174 | {:else} 175 | 176 | {/if} 177 | 178 | 195 | -------------------------------------------------------------------------------- /workspace/extension/src/client/svelte-4.js: -------------------------------------------------------------------------------- 1 | import { send } from './runtime.js'; 2 | import { serialize } from './utils.js'; 3 | 4 | /** @type {undefined | SvelteBlockDetail} */ 5 | let current_block; 6 | 7 | export const index = { 8 | /** @type {Map} */ 9 | map: new Map(), 10 | 11 | /** @param {{ node: SvelteBlockDetail; target?: Node; anchor?: Node }} opts */ 12 | add({ node, target: source, anchor }) { 13 | this.map.set(node.id, node); 14 | this.map.set(node.detail, node); 15 | 16 | let target = source && this.map.get(source); 17 | if (!target || target.container != node.container) { 18 | target = node.container; 19 | } 20 | node.parent = target; 21 | 22 | const sibling = anchor && this.map.get(anchor); 23 | if (target) { 24 | const idx = target.children.findIndex((n) => n === sibling); 25 | if (idx === -1) target.children.push(node); 26 | else target.children.splice(idx, 0, node); 27 | } 28 | 29 | send('bridge::courier/node->add', { 30 | node: serialize(node), 31 | target: node.parent?.id, 32 | anchor: sibling?.id, 33 | }); 34 | }, 35 | 36 | /** @param {{ node: SvelteBlockDetail; target?: Node; anchor?: Node }} opts */ 37 | update({ node }) { 38 | send('bridge::courier/node->update', { 39 | node: serialize(node), 40 | }); 41 | }, 42 | 43 | /** @param {SvelteBlockDetail} node */ 44 | remove(node) { 45 | if (!node) return; 46 | 47 | this.map.delete(node.id); 48 | this.map.delete(node.detail); 49 | 50 | if (node.parent) { 51 | node.parent.children = node.parent.children.filter((n) => n !== node); 52 | node.parent = undefined; 53 | } 54 | 55 | send('bridge::courier/node->remove', { 56 | node: serialize(node), 57 | }); 58 | }, 59 | }; 60 | 61 | document.addEventListener('SvelteRegisterComponent', ({ detail }) => { 62 | const { component, tagName } = detail; 63 | 64 | const node = index.map.get(component.$$.fragment); 65 | if (node) { 66 | index.map.delete(component.$$.fragment); 67 | 68 | node.detail = component; 69 | node.tagName = tagName; 70 | 71 | index.update({ node }); 72 | } else { 73 | // @ts-expect-error - component special case 74 | index.map.set(component.$$.fragment, { 75 | type: 'component', 76 | detail: component, 77 | tagName, 78 | }); 79 | } 80 | }); 81 | 82 | /** @type {any} */ 83 | let last_promise; 84 | document.addEventListener('SvelteRegisterBlock', ({ detail }) => { 85 | const { type, id, block, ...rest } = detail; 86 | const current_node_id = crypto.randomUUID(); 87 | 88 | if (block.m) { 89 | const original = block.m; 90 | block.m = (target, anchor) => { 91 | const parent = current_block; 92 | 93 | // @ts-expect-error - only the necessities 94 | const node = /** @type {SvelteBlockDetail} */ ({ 95 | id: current_node_id, 96 | type: 'block', 97 | detail: rest, 98 | tagName: type === 'pending' ? 'await' : type, 99 | container: parent, 100 | children: [], 101 | }); 102 | 103 | switch (type) { 104 | case 'then': 105 | case 'catch': 106 | if (!node.container) node.container = last_promise; 107 | break; 108 | 109 | case 'slot': 110 | node.type = 'slot'; 111 | break; 112 | 113 | case 'component': { 114 | const component = index.map.get(block); 115 | if (component) { 116 | index.map.delete(block); 117 | Object.assign(node, component); 118 | } else { 119 | node.type = 'component'; 120 | node.tagName = 'Unknown'; 121 | node.detail = {}; 122 | index.map.set(block, node); 123 | } 124 | 125 | Promise.resolve().then(() => { 126 | const invalidate = node.detail.$$?.bound || {}; 127 | Object.keys(invalidate).length && index.update({ node }); 128 | }); 129 | break; 130 | } 131 | } 132 | 133 | if (type === 'each') { 134 | let group = parent && index.map.get(parent.id + id); 135 | if (!group) { 136 | // @ts-expect-error - each block fallback 137 | group = /** @type {SvelteBlockDetail} */ ({ 138 | version: '', 139 | id: crypto.randomUUID(), 140 | type: 'block', 141 | tagName: 'each', 142 | container: parent, 143 | children: [], 144 | detail: { 145 | ctx: {}, 146 | source: detail.source, 147 | }, 148 | }); 149 | parent && index.map.set(parent.id + id, group); 150 | index.add({ node: group, target, anchor }); 151 | } 152 | 153 | node.container = group; 154 | node.type = 'iteration'; 155 | 156 | // @ts-expect-error - overloaded nodes 157 | index.add({ node, target: group, anchor }); 158 | } else { 159 | index.add({ node, target, anchor }); 160 | } 161 | 162 | current_block = node; 163 | 164 | original(target, anchor); 165 | 166 | current_block = parent; 167 | }; 168 | } 169 | 170 | if (block.p) { 171 | const original = block.p; 172 | block.p = (changed, ctx) => { 173 | const parent = current_block; 174 | current_block = index.map.get(current_node_id); 175 | current_block && index.update({ node: current_block }); 176 | 177 | original(changed, ctx); 178 | 179 | current_block = parent; 180 | }; 181 | } 182 | 183 | if (block.d) { 184 | const original = block.d; 185 | block.d = (detaching) => { 186 | const node = index.map.get(current_node_id); 187 | if (node) { 188 | if (node.tagName === 'await') { 189 | last_promise = node.container; 190 | } 191 | index.remove(node); 192 | } 193 | 194 | original(detaching); 195 | }; 196 | } 197 | }); 198 | 199 | document.addEventListener('SvelteDOMInsert', ({ detail }) => { 200 | deep_insert(detail); // { node, target, anchor } 201 | 202 | /** @param {Omit} opts */ 203 | function deep_insert({ node: element, target, anchor }) { 204 | const type = 205 | element.nodeType === Node.ELEMENT_NODE 206 | ? 'element' 207 | : element.nodeValue && element.nodeValue !== ' ' 208 | ? 'text' 209 | : 'anchor'; 210 | 211 | index.add({ 212 | anchor, 213 | target, 214 | // @ts-expect-error - missing properties are irrelevant 215 | node: { 216 | id: crypto.randomUUID(), 217 | type, 218 | detail: element, 219 | tagName: element.nodeName.toLowerCase(), 220 | container: current_block, 221 | children: [], 222 | }, 223 | }); 224 | 225 | element.childNodes.forEach((child) => { 226 | !index.map.has(child) && deep_insert({ node: child, target: element }); 227 | }); 228 | } 229 | }); 230 | 231 | document.addEventListener('SvelteDOMRemove', ({ detail }) => { 232 | const node = index.map.get(detail.node); 233 | if (node) index.remove(node); 234 | }); 235 | 236 | document.addEventListener('SvelteDOMAddEventListener', ({ detail }) => { 237 | const { node, ...rest } = detail; 238 | node.__listeners = node.__listeners || []; 239 | node.__listeners.push(rest); 240 | }); 241 | 242 | document.addEventListener('SvelteDOMRemoveEventListener', ({ detail }) => { 243 | const { node, event, handler, modifiers } = detail; 244 | if (!node.__listeners || node.__listeners.length) return; 245 | node.__listeners = node.__listeners.filter( 246 | (l) => l.event !== event || l.handler !== handler || l.modifiers !== modifiers, 247 | ); 248 | }); 249 | 250 | document.addEventListener('SvelteDOMSetData', ({ detail }) => { 251 | const node = index.map.get(detail.node); 252 | if (!node) return; 253 | if (node.type === 'anchor') node.type = 'text'; 254 | index.update({ node }); 255 | }); 256 | 257 | document.addEventListener('SvelteDOMSetProperty', ({ detail }) => { 258 | const node = index.map.get(detail.node); 259 | if (!node) return; 260 | if (node.type === 'anchor') node.type = 'text'; 261 | index.update({ node }); 262 | }); 263 | 264 | document.addEventListener('SvelteDOMSetAttribute', ({ detail }) => { 265 | const node = index.map.get(detail.node); 266 | if (!node) return; 267 | if (node.type === 'anchor') node.type = 'text'; 268 | index.update({ node }); 269 | }); 270 | 271 | document.addEventListener('SvelteDOMRemoveAttribute', ({ detail }) => { 272 | const node = index.map.get(detail.node); 273 | if (!node) return; 274 | if (node.type === 'anchor') node.type = 'text'; 275 | index.update({ node }); 276 | }); 277 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | devDependencies: 11 | '@sveltejs/vite-plugin-svelte': 12 | specifier: next 13 | version: 4.0.0-next.7(svelte@5.0.0-next.141)(vite@5.4.7) 14 | '@types/node': 15 | specifier: ^20.16.6 16 | version: 20.16.6 17 | prettier: 18 | specifier: ^3.3.3 19 | version: 3.3.3 20 | prettier-plugin-sort-package-json: 21 | specifier: ^0.2.0 22 | version: 0.2.0(prettier@3.3.3) 23 | prettier-plugin-svelte: 24 | specifier: ^3.2.6 25 | version: 3.2.6(prettier@3.3.3)(svelte@5.0.0-next.141) 26 | svelte: 27 | specifier: 5.0.0-next.141 28 | version: 5.0.0-next.141 29 | svelte-check: 30 | specifier: ^4.0.2 31 | version: 4.0.2(svelte@5.0.0-next.141)(typescript@5.6.2) 32 | typescript: 33 | specifier: ^5.6.2 34 | version: 5.6.2 35 | vite: 36 | specifier: ^5.4.7 37 | version: 5.4.7(@types/node@20.16.6) 38 | 39 | workspace/extension: 40 | devDependencies: 41 | '@types/chrome': 42 | specifier: ^0.0.266 43 | version: 0.0.266 44 | rollup: 45 | specifier: ^4.22.4 46 | version: 4.22.4 47 | 48 | packages: 49 | 50 | /@ampproject/remapping@2.3.0: 51 | resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} 52 | engines: {node: '>=6.0.0'} 53 | dependencies: 54 | '@jridgewell/gen-mapping': 0.3.5 55 | '@jridgewell/trace-mapping': 0.3.25 56 | dev: true 57 | 58 | /@esbuild/aix-ppc64@0.21.5: 59 | resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} 60 | engines: {node: '>=12'} 61 | cpu: [ppc64] 62 | os: [aix] 63 | requiresBuild: true 64 | dev: true 65 | optional: true 66 | 67 | /@esbuild/android-arm64@0.21.5: 68 | resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} 69 | engines: {node: '>=12'} 70 | cpu: [arm64] 71 | os: [android] 72 | requiresBuild: true 73 | dev: true 74 | optional: true 75 | 76 | /@esbuild/android-arm@0.21.5: 77 | resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} 78 | engines: {node: '>=12'} 79 | cpu: [arm] 80 | os: [android] 81 | requiresBuild: true 82 | dev: true 83 | optional: true 84 | 85 | /@esbuild/android-x64@0.21.5: 86 | resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} 87 | engines: {node: '>=12'} 88 | cpu: [x64] 89 | os: [android] 90 | requiresBuild: true 91 | dev: true 92 | optional: true 93 | 94 | /@esbuild/darwin-arm64@0.21.5: 95 | resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} 96 | engines: {node: '>=12'} 97 | cpu: [arm64] 98 | os: [darwin] 99 | requiresBuild: true 100 | dev: true 101 | optional: true 102 | 103 | /@esbuild/darwin-x64@0.21.5: 104 | resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} 105 | engines: {node: '>=12'} 106 | cpu: [x64] 107 | os: [darwin] 108 | requiresBuild: true 109 | dev: true 110 | optional: true 111 | 112 | /@esbuild/freebsd-arm64@0.21.5: 113 | resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} 114 | engines: {node: '>=12'} 115 | cpu: [arm64] 116 | os: [freebsd] 117 | requiresBuild: true 118 | dev: true 119 | optional: true 120 | 121 | /@esbuild/freebsd-x64@0.21.5: 122 | resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} 123 | engines: {node: '>=12'} 124 | cpu: [x64] 125 | os: [freebsd] 126 | requiresBuild: true 127 | dev: true 128 | optional: true 129 | 130 | /@esbuild/linux-arm64@0.21.5: 131 | resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} 132 | engines: {node: '>=12'} 133 | cpu: [arm64] 134 | os: [linux] 135 | requiresBuild: true 136 | dev: true 137 | optional: true 138 | 139 | /@esbuild/linux-arm@0.21.5: 140 | resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} 141 | engines: {node: '>=12'} 142 | cpu: [arm] 143 | os: [linux] 144 | requiresBuild: true 145 | dev: true 146 | optional: true 147 | 148 | /@esbuild/linux-ia32@0.21.5: 149 | resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} 150 | engines: {node: '>=12'} 151 | cpu: [ia32] 152 | os: [linux] 153 | requiresBuild: true 154 | dev: true 155 | optional: true 156 | 157 | /@esbuild/linux-loong64@0.21.5: 158 | resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} 159 | engines: {node: '>=12'} 160 | cpu: [loong64] 161 | os: [linux] 162 | requiresBuild: true 163 | dev: true 164 | optional: true 165 | 166 | /@esbuild/linux-mips64el@0.21.5: 167 | resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} 168 | engines: {node: '>=12'} 169 | cpu: [mips64el] 170 | os: [linux] 171 | requiresBuild: true 172 | dev: true 173 | optional: true 174 | 175 | /@esbuild/linux-ppc64@0.21.5: 176 | resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} 177 | engines: {node: '>=12'} 178 | cpu: [ppc64] 179 | os: [linux] 180 | requiresBuild: true 181 | dev: true 182 | optional: true 183 | 184 | /@esbuild/linux-riscv64@0.21.5: 185 | resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} 186 | engines: {node: '>=12'} 187 | cpu: [riscv64] 188 | os: [linux] 189 | requiresBuild: true 190 | dev: true 191 | optional: true 192 | 193 | /@esbuild/linux-s390x@0.21.5: 194 | resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} 195 | engines: {node: '>=12'} 196 | cpu: [s390x] 197 | os: [linux] 198 | requiresBuild: true 199 | dev: true 200 | optional: true 201 | 202 | /@esbuild/linux-x64@0.21.5: 203 | resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} 204 | engines: {node: '>=12'} 205 | cpu: [x64] 206 | os: [linux] 207 | requiresBuild: true 208 | dev: true 209 | optional: true 210 | 211 | /@esbuild/netbsd-x64@0.21.5: 212 | resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} 213 | engines: {node: '>=12'} 214 | cpu: [x64] 215 | os: [netbsd] 216 | requiresBuild: true 217 | dev: true 218 | optional: true 219 | 220 | /@esbuild/openbsd-x64@0.21.5: 221 | resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} 222 | engines: {node: '>=12'} 223 | cpu: [x64] 224 | os: [openbsd] 225 | requiresBuild: true 226 | dev: true 227 | optional: true 228 | 229 | /@esbuild/sunos-x64@0.21.5: 230 | resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} 231 | engines: {node: '>=12'} 232 | cpu: [x64] 233 | os: [sunos] 234 | requiresBuild: true 235 | dev: true 236 | optional: true 237 | 238 | /@esbuild/win32-arm64@0.21.5: 239 | resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} 240 | engines: {node: '>=12'} 241 | cpu: [arm64] 242 | os: [win32] 243 | requiresBuild: true 244 | dev: true 245 | optional: true 246 | 247 | /@esbuild/win32-ia32@0.21.5: 248 | resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} 249 | engines: {node: '>=12'} 250 | cpu: [ia32] 251 | os: [win32] 252 | requiresBuild: true 253 | dev: true 254 | optional: true 255 | 256 | /@esbuild/win32-x64@0.21.5: 257 | resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} 258 | engines: {node: '>=12'} 259 | cpu: [x64] 260 | os: [win32] 261 | requiresBuild: true 262 | dev: true 263 | optional: true 264 | 265 | /@jridgewell/gen-mapping@0.3.5: 266 | resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} 267 | engines: {node: '>=6.0.0'} 268 | dependencies: 269 | '@jridgewell/set-array': 1.2.1 270 | '@jridgewell/sourcemap-codec': 1.5.0 271 | '@jridgewell/trace-mapping': 0.3.25 272 | dev: true 273 | 274 | /@jridgewell/resolve-uri@3.1.2: 275 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 276 | engines: {node: '>=6.0.0'} 277 | dev: true 278 | 279 | /@jridgewell/set-array@1.2.1: 280 | resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} 281 | engines: {node: '>=6.0.0'} 282 | dev: true 283 | 284 | /@jridgewell/sourcemap-codec@1.5.0: 285 | resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} 286 | dev: true 287 | 288 | /@jridgewell/trace-mapping@0.3.25: 289 | resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} 290 | dependencies: 291 | '@jridgewell/resolve-uri': 3.1.2 292 | '@jridgewell/sourcemap-codec': 1.5.0 293 | dev: true 294 | 295 | /@rollup/rollup-android-arm-eabi@4.22.4: 296 | resolution: {integrity: sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==} 297 | cpu: [arm] 298 | os: [android] 299 | requiresBuild: true 300 | dev: true 301 | optional: true 302 | 303 | /@rollup/rollup-android-arm64@4.22.4: 304 | resolution: {integrity: sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==} 305 | cpu: [arm64] 306 | os: [android] 307 | requiresBuild: true 308 | dev: true 309 | optional: true 310 | 311 | /@rollup/rollup-darwin-arm64@4.22.4: 312 | resolution: {integrity: sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==} 313 | cpu: [arm64] 314 | os: [darwin] 315 | requiresBuild: true 316 | dev: true 317 | optional: true 318 | 319 | /@rollup/rollup-darwin-x64@4.22.4: 320 | resolution: {integrity: sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==} 321 | cpu: [x64] 322 | os: [darwin] 323 | requiresBuild: true 324 | dev: true 325 | optional: true 326 | 327 | /@rollup/rollup-linux-arm-gnueabihf@4.22.4: 328 | resolution: {integrity: sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==} 329 | cpu: [arm] 330 | os: [linux] 331 | requiresBuild: true 332 | dev: true 333 | optional: true 334 | 335 | /@rollup/rollup-linux-arm-musleabihf@4.22.4: 336 | resolution: {integrity: sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==} 337 | cpu: [arm] 338 | os: [linux] 339 | requiresBuild: true 340 | dev: true 341 | optional: true 342 | 343 | /@rollup/rollup-linux-arm64-gnu@4.22.4: 344 | resolution: {integrity: sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==} 345 | cpu: [arm64] 346 | os: [linux] 347 | requiresBuild: true 348 | dev: true 349 | optional: true 350 | 351 | /@rollup/rollup-linux-arm64-musl@4.22.4: 352 | resolution: {integrity: sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==} 353 | cpu: [arm64] 354 | os: [linux] 355 | requiresBuild: true 356 | dev: true 357 | optional: true 358 | 359 | /@rollup/rollup-linux-powerpc64le-gnu@4.22.4: 360 | resolution: {integrity: sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==} 361 | cpu: [ppc64] 362 | os: [linux] 363 | requiresBuild: true 364 | dev: true 365 | optional: true 366 | 367 | /@rollup/rollup-linux-riscv64-gnu@4.22.4: 368 | resolution: {integrity: sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==} 369 | cpu: [riscv64] 370 | os: [linux] 371 | requiresBuild: true 372 | dev: true 373 | optional: true 374 | 375 | /@rollup/rollup-linux-s390x-gnu@4.22.4: 376 | resolution: {integrity: sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==} 377 | cpu: [s390x] 378 | os: [linux] 379 | requiresBuild: true 380 | dev: true 381 | optional: true 382 | 383 | /@rollup/rollup-linux-x64-gnu@4.22.4: 384 | resolution: {integrity: sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==} 385 | cpu: [x64] 386 | os: [linux] 387 | requiresBuild: true 388 | dev: true 389 | optional: true 390 | 391 | /@rollup/rollup-linux-x64-musl@4.22.4: 392 | resolution: {integrity: sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==} 393 | cpu: [x64] 394 | os: [linux] 395 | requiresBuild: true 396 | dev: true 397 | optional: true 398 | 399 | /@rollup/rollup-win32-arm64-msvc@4.22.4: 400 | resolution: {integrity: sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==} 401 | cpu: [arm64] 402 | os: [win32] 403 | requiresBuild: true 404 | dev: true 405 | optional: true 406 | 407 | /@rollup/rollup-win32-ia32-msvc@4.22.4: 408 | resolution: {integrity: sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==} 409 | cpu: [ia32] 410 | os: [win32] 411 | requiresBuild: true 412 | dev: true 413 | optional: true 414 | 415 | /@rollup/rollup-win32-x64-msvc@4.22.4: 416 | resolution: {integrity: sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==} 417 | cpu: [x64] 418 | os: [win32] 419 | requiresBuild: true 420 | dev: true 421 | optional: true 422 | 423 | /@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7)(svelte@5.0.0-next.141)(vite@5.4.7): 424 | resolution: {integrity: sha512-kuGJ2CZ5lAw3gKF8Kw0AfKtUJWbwdlDHY14K413B0MCyrzvQvsKTorwmwZcky0+QqY6RnVIZ/5FttB9bQmkLXg==} 425 | engines: {node: ^18.0.0 || ^20.0.0 || >=22} 426 | peerDependencies: 427 | '@sveltejs/vite-plugin-svelte': ^4.0.0-next.0||^4.0.0 428 | svelte: ^5.0.0-next.96 || ^5.0.0 429 | vite: ^5.0.0 430 | dependencies: 431 | '@sveltejs/vite-plugin-svelte': 4.0.0-next.7(svelte@5.0.0-next.141)(vite@5.4.7) 432 | debug: 4.3.7 433 | svelte: 5.0.0-next.141 434 | vite: 5.4.7(@types/node@20.16.6) 435 | transitivePeerDependencies: 436 | - supports-color 437 | dev: true 438 | 439 | /@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.141)(vite@5.4.7): 440 | resolution: {integrity: sha512-yMUnAqquoayvBDztk1rWUgdtvjv7YcHgopCAB7sWl9SQht8U/7lqwTlJU0ZTAY09pFFRe6bbakd7YoiyyIvJiA==} 441 | engines: {node: ^18.0.0 || ^20.0.0 || >=22} 442 | peerDependencies: 443 | svelte: ^5.0.0-next.96 || ^5.0.0 444 | vite: ^5.0.0 445 | dependencies: 446 | '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7)(svelte@5.0.0-next.141)(vite@5.4.7) 447 | debug: 4.3.7 448 | deepmerge: 4.3.1 449 | kleur: 4.1.5 450 | magic-string: 0.30.11 451 | svelte: 5.0.0-next.141 452 | vite: 5.4.7(@types/node@20.16.6) 453 | vitefu: 1.0.2(vite@5.4.7) 454 | transitivePeerDependencies: 455 | - supports-color 456 | dev: true 457 | 458 | /@types/chrome@0.0.266: 459 | resolution: {integrity: sha512-QSQWJTL7NjZElvq/6/E5C1+pHgEP8UAJzwoz7M4vSJ7AECt6NNehJ+tU6snnvuTqZOBjFCivvitYo5+8tNPmhg==} 460 | dependencies: 461 | '@types/filesystem': 0.0.36 462 | '@types/har-format': 1.2.15 463 | dev: true 464 | 465 | /@types/estree@1.0.5: 466 | resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} 467 | dev: true 468 | 469 | /@types/estree@1.0.6: 470 | resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} 471 | dev: true 472 | 473 | /@types/filesystem@0.0.36: 474 | resolution: {integrity: sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==} 475 | dependencies: 476 | '@types/filewriter': 0.0.33 477 | dev: true 478 | 479 | /@types/filewriter@0.0.33: 480 | resolution: {integrity: sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==} 481 | dev: true 482 | 483 | /@types/har-format@1.2.15: 484 | resolution: {integrity: sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA==} 485 | dev: true 486 | 487 | /@types/node@20.16.6: 488 | resolution: {integrity: sha512-T7PpxM/6yeDE+AdlVysT62BX6/bECZOmQAgiFg5NoBd5MQheZ3tzal7f1wvzfiEcmrcJNRi2zRr2nY2zF+0uqw==} 489 | dependencies: 490 | undici-types: 6.19.8 491 | dev: true 492 | 493 | /acorn-typescript@1.4.13(acorn@8.12.1): 494 | resolution: {integrity: sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==} 495 | peerDependencies: 496 | acorn: '>=8.9.0' 497 | dependencies: 498 | acorn: 8.12.1 499 | dev: true 500 | 501 | /acorn@8.12.1: 502 | resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} 503 | engines: {node: '>=0.4.0'} 504 | hasBin: true 505 | dev: true 506 | 507 | /anymatch@3.1.3: 508 | resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} 509 | engines: {node: '>= 8'} 510 | dependencies: 511 | normalize-path: 3.0.0 512 | picomatch: 2.3.1 513 | dev: true 514 | 515 | /aria-query@5.3.2: 516 | resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} 517 | engines: {node: '>= 0.4'} 518 | dev: true 519 | 520 | /axobject-query@4.1.0: 521 | resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} 522 | engines: {node: '>= 0.4'} 523 | dev: true 524 | 525 | /binary-extensions@2.3.0: 526 | resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} 527 | engines: {node: '>=8'} 528 | dev: true 529 | 530 | /braces@3.0.3: 531 | resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 532 | engines: {node: '>=8'} 533 | dependencies: 534 | fill-range: 7.1.1 535 | dev: true 536 | 537 | /chokidar@3.6.0: 538 | resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} 539 | engines: {node: '>= 8.10.0'} 540 | dependencies: 541 | anymatch: 3.1.3 542 | braces: 3.0.3 543 | glob-parent: 5.1.2 544 | is-binary-path: 2.1.0 545 | is-glob: 4.0.3 546 | normalize-path: 3.0.0 547 | readdirp: 3.6.0 548 | optionalDependencies: 549 | fsevents: 2.3.3 550 | dev: true 551 | 552 | /debug@4.3.7: 553 | resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} 554 | engines: {node: '>=6.0'} 555 | peerDependencies: 556 | supports-color: '*' 557 | peerDependenciesMeta: 558 | supports-color: 559 | optional: true 560 | dependencies: 561 | ms: 2.1.3 562 | dev: true 563 | 564 | /deepmerge@4.3.1: 565 | resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} 566 | engines: {node: '>=0.10.0'} 567 | dev: true 568 | 569 | /esbuild@0.21.5: 570 | resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} 571 | engines: {node: '>=12'} 572 | hasBin: true 573 | requiresBuild: true 574 | optionalDependencies: 575 | '@esbuild/aix-ppc64': 0.21.5 576 | '@esbuild/android-arm': 0.21.5 577 | '@esbuild/android-arm64': 0.21.5 578 | '@esbuild/android-x64': 0.21.5 579 | '@esbuild/darwin-arm64': 0.21.5 580 | '@esbuild/darwin-x64': 0.21.5 581 | '@esbuild/freebsd-arm64': 0.21.5 582 | '@esbuild/freebsd-x64': 0.21.5 583 | '@esbuild/linux-arm': 0.21.5 584 | '@esbuild/linux-arm64': 0.21.5 585 | '@esbuild/linux-ia32': 0.21.5 586 | '@esbuild/linux-loong64': 0.21.5 587 | '@esbuild/linux-mips64el': 0.21.5 588 | '@esbuild/linux-ppc64': 0.21.5 589 | '@esbuild/linux-riscv64': 0.21.5 590 | '@esbuild/linux-s390x': 0.21.5 591 | '@esbuild/linux-x64': 0.21.5 592 | '@esbuild/netbsd-x64': 0.21.5 593 | '@esbuild/openbsd-x64': 0.21.5 594 | '@esbuild/sunos-x64': 0.21.5 595 | '@esbuild/win32-arm64': 0.21.5 596 | '@esbuild/win32-ia32': 0.21.5 597 | '@esbuild/win32-x64': 0.21.5 598 | dev: true 599 | 600 | /esm-env@1.0.0: 601 | resolution: {integrity: sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==} 602 | dev: true 603 | 604 | /esrap@1.2.2: 605 | resolution: {integrity: sha512-F2pSJklxx1BlQIQgooczXCPHmcWpn6EsP5oo73LQfonG9fIlIENQ8vMmfGXeojP9MrkzUNAfyU5vdFlR9shHAw==} 606 | dependencies: 607 | '@jridgewell/sourcemap-codec': 1.5.0 608 | '@types/estree': 1.0.6 609 | dev: true 610 | 611 | /fdir@6.3.0: 612 | resolution: {integrity: sha512-QOnuT+BOtivR77wYvCWHfGt9s4Pz1VIMbD463vegT5MLqNXy8rYFT/lPVEqf/bhYeT6qmqrNHhsX+rWwe3rOCQ==} 613 | peerDependencies: 614 | picomatch: ^3 || ^4 615 | peerDependenciesMeta: 616 | picomatch: 617 | optional: true 618 | dev: true 619 | 620 | /fill-range@7.1.1: 621 | resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 622 | engines: {node: '>=8'} 623 | dependencies: 624 | to-regex-range: 5.0.1 625 | dev: true 626 | 627 | /fsevents@2.3.3: 628 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 629 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 630 | os: [darwin] 631 | requiresBuild: true 632 | dev: true 633 | optional: true 634 | 635 | /glob-parent@5.1.2: 636 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 637 | engines: {node: '>= 6'} 638 | dependencies: 639 | is-glob: 4.0.3 640 | dev: true 641 | 642 | /is-binary-path@2.1.0: 643 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} 644 | engines: {node: '>=8'} 645 | dependencies: 646 | binary-extensions: 2.3.0 647 | dev: true 648 | 649 | /is-extglob@2.1.1: 650 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 651 | engines: {node: '>=0.10.0'} 652 | dev: true 653 | 654 | /is-glob@4.0.3: 655 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 656 | engines: {node: '>=0.10.0'} 657 | dependencies: 658 | is-extglob: 2.1.1 659 | dev: true 660 | 661 | /is-number@7.0.0: 662 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 663 | engines: {node: '>=0.12.0'} 664 | dev: true 665 | 666 | /is-reference@3.0.2: 667 | resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==} 668 | dependencies: 669 | '@types/estree': 1.0.6 670 | dev: true 671 | 672 | /kleur@4.1.5: 673 | resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} 674 | engines: {node: '>=6'} 675 | dev: true 676 | 677 | /locate-character@3.0.0: 678 | resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} 679 | dev: true 680 | 681 | /magic-string@0.30.11: 682 | resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} 683 | dependencies: 684 | '@jridgewell/sourcemap-codec': 1.5.0 685 | dev: true 686 | 687 | /mri@1.2.0: 688 | resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} 689 | engines: {node: '>=4'} 690 | dev: true 691 | 692 | /ms@2.1.3: 693 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 694 | dev: true 695 | 696 | /nanoid@3.3.7: 697 | resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} 698 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 699 | hasBin: true 700 | dev: true 701 | 702 | /normalize-path@3.0.0: 703 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} 704 | engines: {node: '>=0.10.0'} 705 | dev: true 706 | 707 | /picocolors@1.1.0: 708 | resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} 709 | dev: true 710 | 711 | /picomatch@2.3.1: 712 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 713 | engines: {node: '>=8.6'} 714 | dev: true 715 | 716 | /postcss@8.4.47: 717 | resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} 718 | engines: {node: ^10 || ^12 || >=14} 719 | dependencies: 720 | nanoid: 3.3.7 721 | picocolors: 1.1.0 722 | source-map-js: 1.2.1 723 | dev: true 724 | 725 | /prettier-plugin-sort-package-json@0.2.0(prettier@3.3.3): 726 | resolution: {integrity: sha512-jg+CfEHpmXyMJxoBSQh+IObmCxqt7KyHOuXGQm9D4heeyipEhlTJZfiS9SlfhBKrtf/yA8WZwHmynUG9xfF/rA==} 727 | peerDependencies: 728 | prettier: ^3.0.0 729 | dependencies: 730 | prettier: 3.3.3 731 | dev: true 732 | 733 | /prettier-plugin-svelte@3.2.6(prettier@3.3.3)(svelte@5.0.0-next.141): 734 | resolution: {integrity: sha512-Y1XWLw7vXUQQZmgv1JAEiLcErqUniAF2wO7QJsw8BVMvpLET2dI5WpEIEJx1r11iHVdSMzQxivyfrH9On9t2IQ==} 735 | peerDependencies: 736 | prettier: ^3.0.0 737 | svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 738 | dependencies: 739 | prettier: 3.3.3 740 | svelte: 5.0.0-next.141 741 | dev: true 742 | 743 | /prettier@3.3.3: 744 | resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} 745 | engines: {node: '>=14'} 746 | hasBin: true 747 | dev: true 748 | 749 | /readdirp@3.6.0: 750 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 751 | engines: {node: '>=8.10.0'} 752 | dependencies: 753 | picomatch: 2.3.1 754 | dev: true 755 | 756 | /rollup@4.22.4: 757 | resolution: {integrity: sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==} 758 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 759 | hasBin: true 760 | dependencies: 761 | '@types/estree': 1.0.5 762 | optionalDependencies: 763 | '@rollup/rollup-android-arm-eabi': 4.22.4 764 | '@rollup/rollup-android-arm64': 4.22.4 765 | '@rollup/rollup-darwin-arm64': 4.22.4 766 | '@rollup/rollup-darwin-x64': 4.22.4 767 | '@rollup/rollup-linux-arm-gnueabihf': 4.22.4 768 | '@rollup/rollup-linux-arm-musleabihf': 4.22.4 769 | '@rollup/rollup-linux-arm64-gnu': 4.22.4 770 | '@rollup/rollup-linux-arm64-musl': 4.22.4 771 | '@rollup/rollup-linux-powerpc64le-gnu': 4.22.4 772 | '@rollup/rollup-linux-riscv64-gnu': 4.22.4 773 | '@rollup/rollup-linux-s390x-gnu': 4.22.4 774 | '@rollup/rollup-linux-x64-gnu': 4.22.4 775 | '@rollup/rollup-linux-x64-musl': 4.22.4 776 | '@rollup/rollup-win32-arm64-msvc': 4.22.4 777 | '@rollup/rollup-win32-ia32-msvc': 4.22.4 778 | '@rollup/rollup-win32-x64-msvc': 4.22.4 779 | fsevents: 2.3.3 780 | dev: true 781 | 782 | /sade@1.8.1: 783 | resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} 784 | engines: {node: '>=6'} 785 | dependencies: 786 | mri: 1.2.0 787 | dev: true 788 | 789 | /source-map-js@1.2.1: 790 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 791 | engines: {node: '>=0.10.0'} 792 | dev: true 793 | 794 | /svelte-check@4.0.2(svelte@5.0.0-next.141)(typescript@5.6.2): 795 | resolution: {integrity: sha512-w2yqcG9ELJe2RJCnAvB7v0OgkHhL3czzz/tVoxGFfO6y4mOrF6QHCDhXijeXzsU7LVKEwWS3Qd9tza4JBuDxqA==} 796 | engines: {node: '>= 18.0.0'} 797 | hasBin: true 798 | peerDependencies: 799 | svelte: ^4.0.0 || ^5.0.0-next.0 800 | typescript: '>=5.0.0' 801 | dependencies: 802 | '@jridgewell/trace-mapping': 0.3.25 803 | chokidar: 3.6.0 804 | fdir: 6.3.0 805 | picocolors: 1.1.0 806 | sade: 1.8.1 807 | svelte: 5.0.0-next.141 808 | typescript: 5.6.2 809 | transitivePeerDependencies: 810 | - picomatch 811 | dev: true 812 | 813 | /svelte@5.0.0-next.141: 814 | resolution: {integrity: sha512-zT74TUo0vOOrbxRfdlWXu+ac4O9lqPFG0YoZB3uOfrOewT1GKxKm0qwG/jo9bGvgZ++TSHjR7AtV091LY2FhBA==} 815 | engines: {node: '>=18'} 816 | dependencies: 817 | '@ampproject/remapping': 2.3.0 818 | '@jridgewell/sourcemap-codec': 1.5.0 819 | '@types/estree': 1.0.6 820 | acorn: 8.12.1 821 | acorn-typescript: 1.4.13(acorn@8.12.1) 822 | aria-query: 5.3.2 823 | axobject-query: 4.1.0 824 | esm-env: 1.0.0 825 | esrap: 1.2.2 826 | is-reference: 3.0.2 827 | locate-character: 3.0.0 828 | magic-string: 0.30.11 829 | zimmerframe: 1.1.2 830 | dev: true 831 | 832 | /to-regex-range@5.0.1: 833 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 834 | engines: {node: '>=8.0'} 835 | dependencies: 836 | is-number: 7.0.0 837 | dev: true 838 | 839 | /typescript@5.6.2: 840 | resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} 841 | engines: {node: '>=14.17'} 842 | hasBin: true 843 | dev: true 844 | 845 | /undici-types@6.19.8: 846 | resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} 847 | dev: true 848 | 849 | /vite@5.4.7(@types/node@20.16.6): 850 | resolution: {integrity: sha512-5l2zxqMEPVENgvzTuBpHer2awaetimj2BGkhBPdnwKbPNOlHsODU+oiazEZzLK7KhAnOrO+XGYJYn4ZlUhDtDQ==} 851 | engines: {node: ^18.0.0 || >=20.0.0} 852 | hasBin: true 853 | peerDependencies: 854 | '@types/node': ^18.0.0 || >=20.0.0 855 | less: '*' 856 | lightningcss: ^1.21.0 857 | sass: '*' 858 | sass-embedded: '*' 859 | stylus: '*' 860 | sugarss: '*' 861 | terser: ^5.4.0 862 | peerDependenciesMeta: 863 | '@types/node': 864 | optional: true 865 | less: 866 | optional: true 867 | lightningcss: 868 | optional: true 869 | sass: 870 | optional: true 871 | sass-embedded: 872 | optional: true 873 | stylus: 874 | optional: true 875 | sugarss: 876 | optional: true 877 | terser: 878 | optional: true 879 | dependencies: 880 | '@types/node': 20.16.6 881 | esbuild: 0.21.5 882 | postcss: 8.4.47 883 | rollup: 4.22.4 884 | optionalDependencies: 885 | fsevents: 2.3.3 886 | dev: true 887 | 888 | /vitefu@1.0.2(vite@5.4.7): 889 | resolution: {integrity: sha512-0/iAvbXyM3RiPPJ4lyD4w6Mjgtf4ejTK6TPvTNG3H32PLwuT0N/ZjJLiXug7ETE/LWtTeHw9WRv7uX/tIKYyKg==} 890 | peerDependencies: 891 | vite: ^3.0.0 || ^4.0.0 || ^5.0.0 892 | peerDependenciesMeta: 893 | vite: 894 | optional: true 895 | dependencies: 896 | vite: 5.4.7(@types/node@20.16.6) 897 | dev: true 898 | 899 | /zimmerframe@1.1.2: 900 | resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} 901 | dev: true 902 | --------------------------------------------------------------------------------