diff --git a/package-lock.json b/package-lock.json index 81bb14b..0ce5fcd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,9 @@ "version": "1.14.1", "license": "MIT", "devDependencies": { - "@discoveryjs/discovery": "1.0.0-beta.83", - "@discoveryjs/json-ext": "^0.5.7", + "@discoveryjs/discovery": "1.0.0-beta.87", "css-tree": "^2.3.1", - "esbuild": "^0.20.1", + "esbuild": "^0.24.0", "eslint": "^8.50.0", "jszip": "^3.10.1", "mime": "^3.0.0" @@ -28,15 +27,16 @@ } }, "node_modules/@discoveryjs/discovery": { - "version": "1.0.0-beta.83", - "resolved": "https://registry.npmjs.org/@discoveryjs/discovery/-/discovery-1.0.0-beta.83.tgz", - "integrity": "sha512-dLJ0wvIpj/6Cnn3WezTEhbSsGumM65O00+6UVZ0Dg3IUBDIPyDdCaT0cnhSo1y/rWuPKO/oq9TCdTVwxbrK16Q==", + "version": "1.0.0-beta.87", + "resolved": "https://registry.npmjs.org/@discoveryjs/discovery/-/discovery-1.0.0-beta.87.tgz", + "integrity": "sha512-h4KZLyjICXSOgaP4m8hpCaTVhmVOPe4K8xpKQz3RzVczRXhpsBxoVK+c5JdLQkdr9W/nvnhIs3tncUQhsiE56w==", "dev": true, + "license": "MIT", "dependencies": { - "@discoveryjs/json-ext": "^0.5.7", + "@discoveryjs/json-ext": "^0.6.1", "codemirror": "^5.65.2", "hitext": "^1.0.0-beta.1", - "jora": "1.0.0-beta.10", + "jora": "1.0.0-beta.13", "marked": "^4.3.0" }, "engines": { @@ -44,12 +44,13 @@ } }, "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.1.tgz", + "integrity": "sha512-boghen8F0Q8D+0/Q1/1r6DUEieUJ8w2a1gIknExMSHBsJFOr2+0KUfHiVYBvucPwl3+RU5PFBK833FjFCh3BhA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10.0.0" + "node": ">=14.17.0" } }, "node_modules/@discoveryjs/natural-compare": { @@ -57,376 +58,417 @@ "resolved": "https://registry.npmjs.org/@discoveryjs/natural-compare/-/natural-compare-1.1.0.tgz", "integrity": "sha512-yuctPJs5lRXoI8LkpVZGAV6n+DKOuEsfpfcIDQ8ZjWHwazqk1QjBc4jMlof0UlZHyUqv4dwsOTooMiAmtzvwXA==", "dev": true, + "license": "MIT", "engines": { "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.1.tgz", - "integrity": "sha512-m55cpeupQ2DbuRGQMMZDzbv9J9PgVelPjlcmM5kxHnrBdBx6REaEd7LamYV7Dm8N7rCyR/XwU6rVP8ploKtIkA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.1.tgz", - "integrity": "sha512-4j0+G27/2ZXGWR5okcJi7pQYhmkVgb4D7UKwxcqrjhvp5TKWx3cUjgB1CGj1mfdmJBQ9VnUGgUhign+FPF2Zgw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.1.tgz", - "integrity": "sha512-hCnXNF0HM6AjowP+Zou0ZJMWWa1VkD77BXe959zERgGJBBxB+sV+J9f/rcjeg2c5bsukD/n17RKWXGFCO5dD5A==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.1.tgz", - "integrity": "sha512-MSfZMBoAsnhpS+2yMFYIQUPs8Z19ajwfuaSZx+tSl09xrHZCjbeXXMsUF/0oq7ojxYEpsSo4c0SfjxOYXRbpaA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.1.tgz", - "integrity": "sha512-Ylk6rzgMD8klUklGPzS414UQLa5NPXZD5tf8JmQU8GQrj6BrFA/Ic9tb2zRe1kOZyCbGl+e8VMbDRazCEBqPvA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.1.tgz", - "integrity": "sha512-pFIfj7U2w5sMp52wTY1XVOdoxw+GDwy9FsK3OFz4BpMAjvZVs0dT1VXs8aQm22nhwoIWUmIRaE+4xow8xfIDZA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.1.tgz", - "integrity": "sha512-UyW1WZvHDuM4xDz0jWun4qtQFauNdXjXOtIy7SYdf7pbxSWWVlqhnR/T2TpX6LX5NI62spt0a3ldIIEkPM6RHw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.1.tgz", - "integrity": "sha512-itPwCw5C+Jh/c624vcDd9kRCCZVpzpQn8dtwoYIt2TJF3S9xJLiRohnnNrKwREvcZYx0n8sCSbvGH349XkcQeg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.1.tgz", - "integrity": "sha512-LojC28v3+IhIbfQ+Vu4Ut5n3wKcgTu6POKIHN9Wpt0HnfgUGlBuyDDQR4jWZUZFyYLiz4RBBBmfU6sNfn6RhLw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.1.tgz", - "integrity": "sha512-cX8WdlF6Cnvw/DO9/X7XLH2J6CkBnz7Twjpk56cshk9sjYVcuh4sXQBy5bmTwzBjNVZze2yaV1vtcJS04LbN8w==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.1.tgz", - "integrity": "sha512-4H/sQCy1mnnGkUt/xszaLlYJVTz3W9ep52xEefGtd6yXDQbz/5fZE5dFLUgsPdbUOQANcVUa5iO6g3nyy5BJiw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.1.tgz", - "integrity": "sha512-c0jgtB+sRHCciVXlyjDcWb2FUuzlGVRwGXgI+3WqKOIuoo8AmZAddzeOHeYLtD+dmtHw3B4Xo9wAUdjlfW5yYA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.1.tgz", - "integrity": "sha512-TgFyCfIxSujyuqdZKDZ3yTwWiGv+KnlOeXXitCQ+trDODJ+ZtGOzLkSWngynP0HZnTsDyBbPy7GWVXWaEl6lhA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", "cpu": [ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.1.tgz", - "integrity": "sha512-b+yuD1IUeL+Y93PmFZDZFIElwbmFfIKLKlYI8M6tRyzE6u7oEP7onGk0vZRh8wfVGC2dZoy0EqX1V8qok4qHaw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.1.tgz", - "integrity": "sha512-wpDlpE0oRKZwX+GfomcALcouqjjV8MIX8DyTrxfyCfXxoKQSDm45CZr9fanJ4F6ckD4yDEPT98SrjvLwIqUCgg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.1.tgz", - "integrity": "sha512-5BepC2Au80EohQ2dBpyTquqGCES7++p7G+7lXe1bAIvMdXm4YYcEfZtQrP4gaoZ96Wv1Ute61CEHFU7h4FMueQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.1.tgz", - "integrity": "sha512-5gRPk7pKuaIB+tmH+yKd2aQTRpqlf1E4f/mC+tawIm/CGJemZcHZpp2ic8oD83nKgUPMEd0fNanrnFljiruuyA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.1.tgz", - "integrity": "sha512-4fL68JdrLV2nVW2AaWZBv3XEm3Ae3NZn/7qy2KGAt3dexAgSVT+Hc97JKSZnqezgMlv9x6KV0ZkZY7UO5cNLCg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.1.tgz", - "integrity": "sha512-GhRuXlvRE+twf2ES+8REbeCb/zeikNqwD3+6S5y5/x+DYbAQUNl0HNBs4RQJqrechS4v4MruEr8ZtAin/hK5iw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.1.tgz", - "integrity": "sha512-ZnWEyCM0G1Ex6JtsygvC3KUUrlDXqOihw8RicRuQAzw+c4f1D66YlPNNV3rkjVW90zXVsHwZYWbJh3v+oQFM9Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.1.tgz", - "integrity": "sha512-QZ6gXue0vVQY2Oon9WyLFCdSuYbXSoxaZrPuJ4c20j6ICedfsDilNPYfHLlMH7vGfU5DQR0czHLmJvH4Nzis/A==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.1.tgz", - "integrity": "sha512-HzcJa1NcSWTAU0MJIxOho8JftNp9YALui3o+Ny7hCh0v5f90nprly1U3Sj1Ldj/CvKKdvvFsCRvDkpsEMp4DNw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.1.tgz", - "integrity": "sha512-0MBh53o6XtI6ctDnRMeQ+xoCN8kD2qI1rY1KgF/xdWQwoFeKou7puvDfV8/Wv4Ctx2rRpET/gGdz3YlNtNACSA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -760,41 +802,43 @@ } }, "node_modules/esbuild": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.1.tgz", - "integrity": "sha512-OJwEgrpWm/PCMsLVWXKqvcjme3bHNpOgN7Tb6cQnR5n0TPbQx1/Xrn7rqM+wn17bYeT6MGB5sn1Bh5YiGi70nA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.1", - "@esbuild/android-arm": "0.20.1", - "@esbuild/android-arm64": "0.20.1", - "@esbuild/android-x64": "0.20.1", - "@esbuild/darwin-arm64": "0.20.1", - "@esbuild/darwin-x64": "0.20.1", - "@esbuild/freebsd-arm64": "0.20.1", - "@esbuild/freebsd-x64": "0.20.1", - "@esbuild/linux-arm": "0.20.1", - "@esbuild/linux-arm64": "0.20.1", - "@esbuild/linux-ia32": "0.20.1", - "@esbuild/linux-loong64": "0.20.1", - "@esbuild/linux-mips64el": "0.20.1", - "@esbuild/linux-ppc64": "0.20.1", - "@esbuild/linux-riscv64": "0.20.1", - "@esbuild/linux-s390x": "0.20.1", - "@esbuild/linux-x64": "0.20.1", - "@esbuild/netbsd-x64": "0.20.1", - "@esbuild/openbsd-x64": "0.20.1", - "@esbuild/sunos-x64": "0.20.1", - "@esbuild/win32-arm64": "0.20.1", - "@esbuild/win32-ia32": "0.20.1", - "@esbuild/win32-x64": "0.20.1" + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" } }, "node_modules/escape-string-regexp": { @@ -1230,12 +1274,13 @@ "dev": true }, "node_modules/jora": { - "version": "1.0.0-beta.10", - "resolved": "https://registry.npmjs.org/jora/-/jora-1.0.0-beta.10.tgz", - "integrity": "sha512-4uksWcMtbXItSF9P7LLdvDnF0woVmnGrpQ63Fi5Asj3QbE0y4TsIPVTL0gXNGPU5sA66b12ltERJQ/l5PtbnuA==", + "version": "1.0.0-beta.13", + "resolved": "https://registry.npmjs.org/jora/-/jora-1.0.0-beta.13.tgz", + "integrity": "sha512-MzIUKA+t5IUwMsCzhkHCcL4M2xWxNN+3nntFm8q1LEF7jdWWGAL3lAHpnymFmsbxlctrdK8z5B1mx5z5SPDlkQ==", "dev": true, + "license": "MIT", "dependencies": { - "@discoveryjs/natural-compare": "^1.0.0" + "@discoveryjs/natural-compare": "^1.1.0" }, "engines": { "node": "^10.12.0 || ^12.20.0 || ^14.13.0 || >=15.0.0" @@ -1765,22 +1810,22 @@ "dev": true }, "@discoveryjs/discovery": { - "version": "1.0.0-beta.83", - "resolved": "https://registry.npmjs.org/@discoveryjs/discovery/-/discovery-1.0.0-beta.83.tgz", - "integrity": "sha512-dLJ0wvIpj/6Cnn3WezTEhbSsGumM65O00+6UVZ0Dg3IUBDIPyDdCaT0cnhSo1y/rWuPKO/oq9TCdTVwxbrK16Q==", + "version": "1.0.0-beta.87", + "resolved": "https://registry.npmjs.org/@discoveryjs/discovery/-/discovery-1.0.0-beta.87.tgz", + "integrity": "sha512-h4KZLyjICXSOgaP4m8hpCaTVhmVOPe4K8xpKQz3RzVczRXhpsBxoVK+c5JdLQkdr9W/nvnhIs3tncUQhsiE56w==", "dev": true, "requires": { - "@discoveryjs/json-ext": "^0.5.7", + "@discoveryjs/json-ext": "^0.6.1", "codemirror": "^5.65.2", "hitext": "^1.0.0-beta.1", - "jora": "1.0.0-beta.10", + "jora": "1.0.0-beta.13", "marked": "^4.3.0" } }, "@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.1.tgz", + "integrity": "sha512-boghen8F0Q8D+0/Q1/1r6DUEieUJ8w2a1gIknExMSHBsJFOr2+0KUfHiVYBvucPwl3+RU5PFBK833FjFCh3BhA==", "dev": true }, "@discoveryjs/natural-compare": { @@ -1790,163 +1835,170 @@ "dev": true }, "@esbuild/aix-ppc64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.1.tgz", - "integrity": "sha512-m55cpeupQ2DbuRGQMMZDzbv9J9PgVelPjlcmM5kxHnrBdBx6REaEd7LamYV7Dm8N7rCyR/XwU6rVP8ploKtIkA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", "dev": true, "optional": true }, "@esbuild/android-arm": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.1.tgz", - "integrity": "sha512-4j0+G27/2ZXGWR5okcJi7pQYhmkVgb4D7UKwxcqrjhvp5TKWx3cUjgB1CGj1mfdmJBQ9VnUGgUhign+FPF2Zgw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", "dev": true, "optional": true }, "@esbuild/android-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.1.tgz", - "integrity": "sha512-hCnXNF0HM6AjowP+Zou0ZJMWWa1VkD77BXe959zERgGJBBxB+sV+J9f/rcjeg2c5bsukD/n17RKWXGFCO5dD5A==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", "dev": true, "optional": true }, "@esbuild/android-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.1.tgz", - "integrity": "sha512-MSfZMBoAsnhpS+2yMFYIQUPs8Z19ajwfuaSZx+tSl09xrHZCjbeXXMsUF/0oq7ojxYEpsSo4c0SfjxOYXRbpaA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", "dev": true, "optional": true }, "@esbuild/darwin-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.1.tgz", - "integrity": "sha512-Ylk6rzgMD8klUklGPzS414UQLa5NPXZD5tf8JmQU8GQrj6BrFA/Ic9tb2zRe1kOZyCbGl+e8VMbDRazCEBqPvA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", "dev": true, "optional": true }, "@esbuild/darwin-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.1.tgz", - "integrity": "sha512-pFIfj7U2w5sMp52wTY1XVOdoxw+GDwy9FsK3OFz4BpMAjvZVs0dT1VXs8aQm22nhwoIWUmIRaE+4xow8xfIDZA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", "dev": true, "optional": true }, "@esbuild/freebsd-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.1.tgz", - "integrity": "sha512-UyW1WZvHDuM4xDz0jWun4qtQFauNdXjXOtIy7SYdf7pbxSWWVlqhnR/T2TpX6LX5NI62spt0a3ldIIEkPM6RHw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", "dev": true, "optional": true }, "@esbuild/freebsd-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.1.tgz", - "integrity": "sha512-itPwCw5C+Jh/c624vcDd9kRCCZVpzpQn8dtwoYIt2TJF3S9xJLiRohnnNrKwREvcZYx0n8sCSbvGH349XkcQeg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", "dev": true, "optional": true }, "@esbuild/linux-arm": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.1.tgz", - "integrity": "sha512-LojC28v3+IhIbfQ+Vu4Ut5n3wKcgTu6POKIHN9Wpt0HnfgUGlBuyDDQR4jWZUZFyYLiz4RBBBmfU6sNfn6RhLw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", "dev": true, "optional": true }, "@esbuild/linux-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.1.tgz", - "integrity": "sha512-cX8WdlF6Cnvw/DO9/X7XLH2J6CkBnz7Twjpk56cshk9sjYVcuh4sXQBy5bmTwzBjNVZze2yaV1vtcJS04LbN8w==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", "dev": true, "optional": true }, "@esbuild/linux-ia32": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.1.tgz", - "integrity": "sha512-4H/sQCy1mnnGkUt/xszaLlYJVTz3W9ep52xEefGtd6yXDQbz/5fZE5dFLUgsPdbUOQANcVUa5iO6g3nyy5BJiw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", "dev": true, "optional": true }, "@esbuild/linux-loong64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.1.tgz", - "integrity": "sha512-c0jgtB+sRHCciVXlyjDcWb2FUuzlGVRwGXgI+3WqKOIuoo8AmZAddzeOHeYLtD+dmtHw3B4Xo9wAUdjlfW5yYA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", "dev": true, "optional": true }, "@esbuild/linux-mips64el": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.1.tgz", - "integrity": "sha512-TgFyCfIxSujyuqdZKDZ3yTwWiGv+KnlOeXXitCQ+trDODJ+ZtGOzLkSWngynP0HZnTsDyBbPy7GWVXWaEl6lhA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", "dev": true, "optional": true }, "@esbuild/linux-ppc64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.1.tgz", - "integrity": "sha512-b+yuD1IUeL+Y93PmFZDZFIElwbmFfIKLKlYI8M6tRyzE6u7oEP7onGk0vZRh8wfVGC2dZoy0EqX1V8qok4qHaw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", "dev": true, "optional": true }, "@esbuild/linux-riscv64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.1.tgz", - "integrity": "sha512-wpDlpE0oRKZwX+GfomcALcouqjjV8MIX8DyTrxfyCfXxoKQSDm45CZr9fanJ4F6ckD4yDEPT98SrjvLwIqUCgg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", "dev": true, "optional": true }, "@esbuild/linux-s390x": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.1.tgz", - "integrity": "sha512-5BepC2Au80EohQ2dBpyTquqGCES7++p7G+7lXe1bAIvMdXm4YYcEfZtQrP4gaoZ96Wv1Ute61CEHFU7h4FMueQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", "dev": true, "optional": true }, "@esbuild/linux-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.1.tgz", - "integrity": "sha512-5gRPk7pKuaIB+tmH+yKd2aQTRpqlf1E4f/mC+tawIm/CGJemZcHZpp2ic8oD83nKgUPMEd0fNanrnFljiruuyA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", "dev": true, "optional": true }, "@esbuild/netbsd-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.1.tgz", - "integrity": "sha512-4fL68JdrLV2nVW2AaWZBv3XEm3Ae3NZn/7qy2KGAt3dexAgSVT+Hc97JKSZnqezgMlv9x6KV0ZkZY7UO5cNLCg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", "dev": true, "optional": true }, "@esbuild/openbsd-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.1.tgz", - "integrity": "sha512-GhRuXlvRE+twf2ES+8REbeCb/zeikNqwD3+6S5y5/x+DYbAQUNl0HNBs4RQJqrechS4v4MruEr8ZtAin/hK5iw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", "dev": true, "optional": true }, "@esbuild/sunos-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.1.tgz", - "integrity": "sha512-ZnWEyCM0G1Ex6JtsygvC3KUUrlDXqOihw8RicRuQAzw+c4f1D66YlPNNV3rkjVW90zXVsHwZYWbJh3v+oQFM9Q==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", "dev": true, "optional": true }, "@esbuild/win32-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.1.tgz", - "integrity": "sha512-QZ6gXue0vVQY2Oon9WyLFCdSuYbXSoxaZrPuJ4c20j6ICedfsDilNPYfHLlMH7vGfU5DQR0czHLmJvH4Nzis/A==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", "dev": true, "optional": true }, "@esbuild/win32-ia32": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.1.tgz", - "integrity": "sha512-HzcJa1NcSWTAU0MJIxOho8JftNp9YALui3o+Ny7hCh0v5f90nprly1U3Sj1Ldj/CvKKdvvFsCRvDkpsEMp4DNw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", "dev": true, "optional": true }, "@esbuild/win32-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.1.tgz", - "integrity": "sha512-0MBh53o6XtI6ctDnRMeQ+xoCN8kD2qI1rY1KgF/xdWQwoFeKou7puvDfV8/Wv4Ctx2rRpET/gGdz3YlNtNACSA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", "dev": true, "optional": true }, @@ -2194,34 +2246,35 @@ } }, "esbuild": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.1.tgz", - "integrity": "sha512-OJwEgrpWm/PCMsLVWXKqvcjme3bHNpOgN7Tb6cQnR5n0TPbQx1/Xrn7rqM+wn17bYeT6MGB5sn1Bh5YiGi70nA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", "dev": true, "requires": { - "@esbuild/aix-ppc64": "0.20.1", - "@esbuild/android-arm": "0.20.1", - "@esbuild/android-arm64": "0.20.1", - "@esbuild/android-x64": "0.20.1", - "@esbuild/darwin-arm64": "0.20.1", - "@esbuild/darwin-x64": "0.20.1", - "@esbuild/freebsd-arm64": "0.20.1", - "@esbuild/freebsd-x64": "0.20.1", - "@esbuild/linux-arm": "0.20.1", - "@esbuild/linux-arm64": "0.20.1", - "@esbuild/linux-ia32": "0.20.1", - "@esbuild/linux-loong64": "0.20.1", - "@esbuild/linux-mips64el": "0.20.1", - "@esbuild/linux-ppc64": "0.20.1", - "@esbuild/linux-riscv64": "0.20.1", - "@esbuild/linux-s390x": "0.20.1", - "@esbuild/linux-x64": "0.20.1", - "@esbuild/netbsd-x64": "0.20.1", - "@esbuild/openbsd-x64": "0.20.1", - "@esbuild/sunos-x64": "0.20.1", - "@esbuild/win32-arm64": "0.20.1", - "@esbuild/win32-ia32": "0.20.1", - "@esbuild/win32-x64": "0.20.1" + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" } }, "escape-string-regexp": { @@ -2557,12 +2610,12 @@ "dev": true }, "jora": { - "version": "1.0.0-beta.10", - "resolved": "https://registry.npmjs.org/jora/-/jora-1.0.0-beta.10.tgz", - "integrity": "sha512-4uksWcMtbXItSF9P7LLdvDnF0woVmnGrpQ63Fi5Asj3QbE0y4TsIPVTL0gXNGPU5sA66b12ltERJQ/l5PtbnuA==", + "version": "1.0.0-beta.13", + "resolved": "https://registry.npmjs.org/jora/-/jora-1.0.0-beta.13.tgz", + "integrity": "sha512-MzIUKA+t5IUwMsCzhkHCcL4M2xWxNN+3nntFm8q1LEF7jdWWGAL3lAHpnymFmsbxlctrdK8z5B1mx5z5SPDlkQ==", "dev": true, "requires": { - "@discoveryjs/natural-compare": "^1.0.0" + "@discoveryjs/natural-compare": "^1.1.0" } }, "js-yaml": { diff --git a/package.json b/package.json index 53931f0..cc1c61f 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,9 @@ "build": "NODE_ENV=production node ./scripts/build.js && node ./scripts/zip.js" }, "devDependencies": { - "@discoveryjs/discovery": "1.0.0-beta.83", - "@discoveryjs/json-ext": "^0.5.7", + "@discoveryjs/discovery": "1.0.0-beta.87", "css-tree": "^2.3.1", - "esbuild": "^0.20.1", + "esbuild": "^0.24.0", "eslint": "^8.50.0", "jszip": "^3.10.1", "mime": "^3.0.0" diff --git a/scripts/build.js b/scripts/build.js index c437f5a..f39706b 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -49,24 +49,20 @@ async function build(browser) { fs.rmSync(outdir, { recursive: true, force: true }); // rm -rf fs.mkdirSync(outdir, { recursive: true }); - fs.writeFileSync(outdir + '/manifest.json', manifest(browser)); + fs.writeFileSync(path.join(outdir, 'manifest.json'), manifest(browser)); + copyFile(path.join(indir, 'sandbox.html'), outdir); copyFiles(path.join(indir, 'icons'), outdir); // build bundle - const result = await esbuild.build({ + await esbuild.build({ entryPoints: [ - path.join(indir, 'background.js'), - path.join(indir, 'content/discovery.css'), - path.join(indir, 'content/preloader.css'), - path.join(indir, 'content/discovery.js'), - path.join(indir, 'content/discovery-esm.js'), - path.join(indir, 'content/init.js') + { in: path.join(indir, 'content/init.js'), out: 'init' }, + path.join(indir, 'sandbox.js') ], format: 'esm', bundle: true, minify: true, - write: false, outdir, define: { global: 'window' @@ -77,16 +73,6 @@ async function build(browser) { '.md': 'text' } }); - - for (const file of result.outputFiles) { - const content = path.extname(file.path) === '.css' - ? processCss(file.text, outdir, 'assets') - : file.contents; - - const filePath = path.join(outdir, path.basename(file.path)); - - fs.writeFileSync(filePath, content); - } } const buildAll = async function() { @@ -131,6 +117,10 @@ const buildAll = async function() { } })(); +function copyFile(filepath, dest) { + fs.copyFileSync(filepath, path.join(dest, path.basename(filepath))); +} + function copyFiles(src, dest) { fs.mkdirSync(dest, { recursive: true }); @@ -139,6 +129,6 @@ function copyFiles(src, dest) { copyFiles(path.join(src, p), path.join(dest, path.basename(src))) ); } else { - fs.copyFileSync(src, path.join(dest, path.basename(src))); + copyFile(src, dest); } } diff --git a/src/content/discovery-esm.js b/src/content/discovery-esm.js deleted file mode 100644 index 1203a99..0000000 --- a/src/content/discovery-esm.js +++ /dev/null @@ -1 +0,0 @@ -export { initDiscovery } from '../discovery'; diff --git a/src/content/discovery.css b/src/content/discovery.css deleted file mode 100644 index f609168..0000000 --- a/src/content/discovery.css +++ /dev/null @@ -1 +0,0 @@ -@import '../discovery/discovery.css'; diff --git a/src/content/discovery.js b/src/content/discovery.js deleted file mode 100644 index c2e1a55..0000000 --- a/src/content/discovery.js +++ /dev/null @@ -1,7 +0,0 @@ -import { initDiscovery } from '../discovery'; - -initDiscovery(...window.__discoveryOptions) // eslint-disable-line no-underscore-dangle - .then(() => { - window.__discoveryPreloader.el.remove(); // eslint-disable-line no-underscore-dangle - }); - diff --git a/src/content/init.js b/src/content/init.js index 2737267..5e206cd 100644 --- a/src/content/init.js +++ b/src/content/init.js @@ -1,70 +1,23 @@ -import { rollbackContainerStyles } from '@discoveryjs/discovery/src/core/utils/container-styles'; -import { preloader as createPreloader } from '@discoveryjs/discovery/src/preloader.js'; -import parseChunked from '@discoveryjs/json-ext/src/parse-chunked'; +import { applyContainerStyles, rollbackContainerStyles } from '@discoveryjs/discovery/src/core/utils/container-styles.js'; +import { connectToEmbedApp } from '@discoveryjs/discovery/src/extensions/embed-host.js'; +import copyText from '@discoveryjs/discovery/lib/core/utils/copy-text.js'; +import { downloadAsFile } from '../discovery/download-as-file'; -let loaded = document.readyState === 'complete'; +let documentFullyLoaded = document.readyState === 'complete'; let loadedTimer; let disabledElements = []; let pre = null; +let iframe = null; let preCursor; let prevCursorValue = ''; -let preloader = null; -let pushChunk = () => {}; +let dataStreamController = null; +let stylesApplied = false; let totalSize = 0; let firstSlice = ''; const firstSliceMaxSize = 100 * 1000; -const chunkBuffer = []; -const getChunk = () => { - if (chunkBuffer.length) { - return chunkBuffer.shift(); - } - - return new Promise(resolve => { - pushChunk = chunk => { - resolve(chunk); - pushChunk = chunk => chunkBuffer.push(chunk); - }; - }); -}; -const data = parseChunked(async function*() { - const loadStartTime = Date.now(); - const getState = done => ({ - stage: 'receive', - progress: { - done, - elapsed: Date.now() - loadStartTime, - units: 'bytes', - completed: totalSize - } - }); - - while (true) { - const chunk = await getChunk(); - - if (!chunk) { - break; - } - - yield chunk; - totalSize += chunk.length; - - if (firstSlice.length < firstSliceMaxSize) { - const left = firstSliceMaxSize - firstSlice.length; - firstSlice += left > chunk.length ? chunk : chunk.slice(0, left); - } - - if (preloader !== null) { - await preloader.progressbar.setState(getState(false)); - } - } - - if (preloader !== null) { - preloader.progressbar.setState(getState(true)); - } -}); -function raiseBailout() { - return Object.assign(new Error('Rollback'), { rollback: true }); +function raiseBailout(reason) { + return Object.assign(new Error('Rollback'), { bailout: reason }); } const flushData = (settings) => { @@ -84,9 +37,9 @@ const flushData = (settings) => { : preCursor.nextSibling; if (!chunkNode) { - if (isFirstChunk && (loaded || pre.nextSibling)) { + if (isFirstChunk && (documentFullyLoaded || pre.nextSibling)) { // bailout: first
 is empty
-                throw raiseBailout();
+                throw raiseBailout('Empty input');
             }
 
             break;
@@ -96,29 +49,36 @@ const flushData = (settings) => {
             if (isFirstChunk) {
                 if (/^\s*[{[]/.test(chunkNode.nodeValue)) {
                     // probably JSON, accept an object or an array only to reduce false positive
-                    preloader = createPreloader({
-                        container: document.body,
-                        styles: [{ type: 'link', href: chrome.runtime.getURL('preloader.css') }],
-                        darkmode: settings.darkmode
-                    });
-                    preloader.el.classList.add('discovery');
-                    preloader.progressbar.setState({ stage: 'request' });
+                    if (dataStreamController === null) {
+                        if (iframe === null) {
+                            pre.before(getIframe(settings));
+                        }
+
+                        return;
+                    }
                 } else {
                     // bailout: not a JSON or a non-object / non-array value
-                    throw raiseBailout();
+                    throw raiseBailout('Not a JSON or a non-object / non-array value');
                 }
             }
 
-            pushChunk(
-                chunkNode === preCursor
-                    // slice a new content from a chunk node in case a content
-                    // was appended to an existing text node
-                    ? chunkNode.nodeValue.slice(prevCursorValue.length)
-                    : chunkNode.nodeValue
-            );
+            const chunk = chunkNode === preCursor
+                // slice a new content from a chunk node in case a content
+                // was appended to an existing text node
+                ? chunkNode.nodeValue.slice(prevCursorValue.length)
+                : chunkNode.nodeValue;
+
+            totalSize += chunk.length;
+
+            if (firstSlice.length < firstSliceMaxSize) {
+                const left = firstSliceMaxSize - firstSlice.length;
+                firstSlice += left > chunk.length ? chunk : chunk.slice(0, left);
+            }
+
+            dataStreamController.enqueue(chunk);
         } else {
             // bailout: not a text node -> a complex markup is not a JSON
-            throw raiseBailout();
+            throw raiseBailout('Input not a text');
         }
 
         preCursor = chunkNode;
@@ -127,13 +87,15 @@ const flushData = (settings) => {
 };
 
 function rollbackPageChanges(error) {
-    chunkBuffer.length = 0; // clean up buffer
     cancelAnimationFrame(loadedTimer);
     rollbackContainerStyles(document.body);
 
-    if (preloader !== null) {
-        preloader.el.remove();
-        preloader = null;
+    dataStreamController?.close();
+    dataStreamController = null;
+
+    if (iframe !== null) {
+        iframe.remove();
+        iframe = null;
     }
 
     // it might to take a lot of time to render large text,
@@ -147,8 +109,8 @@ function rollbackPageChanges(error) {
         }
     });
 
-    if (!error.rollback) {
-        console.error('[JsonDiscovery] Failed to parse JSON', error); // eslint-disable-line no-console
+    if (error.bailout) {
+        console.warn('[JsonDiscovery] Bailout reason:', error.bailout); // eslint-disable-line no-console
     }
 }
 
@@ -189,8 +151,122 @@ function disableElement(element, remove = false) {
     element.hidden = true;
 }
 
+function getIframe(settings) {
+    if (iframe !== null) {
+        return iframe;
+    }
+
+    iframe = document.createElement('iframe');
+    iframe.className = 'discovery';
+    iframe.setAttribute('sandbox', 'allow-scripts');
+    iframe.src = chrome.runtime.getURL('sandbox.html');
+    iframe.style.cssText = 'position: fixed; inset: 0; border: 0; width: 100%; height: 100%; visibility: hidden';
+
+    // Check if scripts in the sandbox iframe work, otherwise rollback since we can't display anything.
+    // The first script in the sandbox iframe sends message, it should be delivered before onload event fires
+    {
+        let scriptsWorks = false;
+
+        window.addEventListener('message', e => {
+            if (e.data === 'json-discovery-sandbox-scripts-work') {
+                scriptsWorks = true;
+            }
+        }, { once: true });
+
+        iframe.onload = () => {
+            if (!scriptsWorks) {
+                rollbackPageChanges(raiseBailout('Scripts or postMessage() doesn\'t work in sandbox'));
+            } else {
+                // enable visibility on iframe load to avoid flash of white background when in dark mode
+                iframe.style.visibility = 'visible';
+            }
+        };
+    }
+
+    connectToEmbedApp(iframe, (app) => {
+        // settings
+        let darkmode = 'auto';
+
+        switch (settings.darkmode) {
+            case true:
+                darkmode = 'dark';
+                break;
+            case false:
+                darkmode = 'light';
+                break;
+        }
+
+        app.setDarkmode(darkmode);
+        app.defineAction('getSettings', () => settings);
+        app.defineAction('setSettings', settings => {
+            chrome.storage.sync.set(settings);
+        });
+
+        app.defineAction('copyToClipboard', () => copyText(pre.textContent));
+        app.defineAction('downloadAsFile', () => {
+            // FIXME: bad for large files
+            downloadAsFile(pre.textContent);
+        });
+
+        app.defineAction('permalink', () => window.location.toString());
+
+        app.defineAction('getRaw', () => ({
+            firstSliceText: firstSlice,
+            firstSliceSize: firstSlice.length,
+            fullSize: totalSize
+        }));
+        app.defineAction('getRawFull', () => ({
+            json: pre.textContent
+        }));
+
+        app.on('darkmodeChanged', async event => {
+            const settings = await getSettings();
+            let darkmode = 'auto';
+
+            switch (event.value) {
+                case 'light':
+                    darkmode = false;
+                    break;
+                case 'dark':
+                    darkmode = true;
+                    break;
+            }
+
+            chrome.storage.sync.set({ ...settings, darkmode });
+        });
+
+        // upload data
+        app.uploadData(new ReadableStream({
+            start(controller_) {
+                dataStreamController = controller_;
+            },
+            cancel() {
+                dataStreamController = null;
+            }
+        }));
+
+        // sync location
+        // Note: should be last since lead to renders
+        app.setRouterPreventLocationUpdate(true);
+        app.setPageHash(location.hash);
+        addEventListener('hashchange', () => app.setPageHash(location.hash), false);
+        app.on('pageHashChanged', (newPageHash, replace) => {
+            if (replace) {
+                location.replace(newPageHash);
+            } else {
+                location.hash = newPageHash;
+            }
+        });
+
+        // check load and appearance
+        getSettings().then(checkLoaded);
+    });
+
+    return iframe;
+}
+
 async function checkLoaded(settings) {
-    if (pre === null) {
+    if (pre === null && !stylesApplied) {
         const firstElement = document.body?.firstElementChild;
 
         pre = isPre(firstElement) || isPre(firstElement?.nextElementSibling);
@@ -212,11 +288,16 @@ async function checkLoaded(settings) {
         }
     }
 
-    if (!settings) {
+    if (!settings || pre === null) {
         return;
     }
 
-    if (!loaded) {
+    if (!stylesApplied) {
+        stylesApplied = true;
+        applyContainerStyles(document.body, settings);
+    }
+
+    if (!documentFullyLoaded) {
         flushData(settings);
         loadedTimer = requestAnimationFrame(() =>
             checkLoaded(settings).catch(rollbackPageChanges)
@@ -226,55 +307,16 @@ async function checkLoaded(settings) {
 
     if (pre !== null) {
         flushData(settings);
-        pushChunk(null); // end of input
-
-        const [{ initDiscovery }, json] = await Promise.all([
-            import(chrome.runtime.getURL('discovery-esm.js')),
-            data
-        ]);
-
-        const discoveryOptions = [
-            {
-                node: document.body,
-                raw: Object.defineProperties({}, {
-                    firstSlice: {
-                        value: totalSize < firstSliceMaxSize * 2 ? null : firstSlice
-                    },
-                    size: {
-                        value: totalSize
-                    },
-                    json: totalSize <= firstSliceMaxSize ? { value: firstSlice } : {
-                        configurable: true,
-                        get() {
-                            return Object.defineProperty(this, 'json', {
-                                value: pre.textContent
-                            }).json;
-                        }
-                    }
-                }),
-                settings,
-                version: chrome.runtime.getManifest().version,
-                styles: [chrome.runtime.getURL('index.css')],
-                progressbar: preloader.progressbar
-            }, json
-        ];
-
-        // In case of sandboxed CSP pages await import will fail
-        // so here we send message to bg which executes discovery initiation via chrome API
-        if (typeof initDiscovery !== 'function') {
-            window.__discoveryPreloader = preloader; // eslint-disable-line no-underscore-dangle
-            window.__discoveryOptions = discoveryOptions; // eslint-disable-line no-underscore-dangle
-
-            await chrome.runtime.sendMessage({ type: 'initDiscovery' });
-        } else {
-            await initDiscovery(...discoveryOptions);
 
-            preloader.el.remove();
-        }
+        dataStreamController?.close();
+        dataStreamController = null;
     }
 }
 
-window.addEventListener('DOMContentLoaded', () => loaded = true, false);
+window.addEventListener('DOMContentLoaded', () => {
+    documentFullyLoaded = true;
+    checkLoaded();
+}, { once: true });
 checkLoaded();
 getSettings()
     .then(checkLoaded)
@@ -285,13 +327,9 @@ getSettings()
  * @returns {Promise}
  */
 function getSettings() {
-    return new Promise(resolve => {
-        chrome.storage.sync.get({
-            expandLevel: 3,
-            darkmode: 'auto',
-            whatsnew: {}
-        }, settings => {
-            resolve(settings);
-        });
+    return chrome.storage.sync.get({
+        expandLevel: 3,
+        darkmode: 'auto',
+        whatsnew: {}
     });
 }
diff --git a/src/content/preloader.css b/src/content/preloader.css
deleted file mode 100644
index 06742de..0000000
--- a/src/content/preloader.css
+++ /dev/null
@@ -1,6 +0,0 @@
-@import '@discoveryjs/discovery/src/preloader.css';
-:host {
-    top: 0;
-    left: 0;
-    z-index: 1;
-}
diff --git a/src/discovery/copy-to-clipboard.js b/src/discovery/copy-to-clipboard.js
index 5f9ec8c..5bc0a50 100644
--- a/src/discovery/copy-to-clipboard.js
+++ b/src/discovery/copy-to-clipboard.js
@@ -1,12 +1,8 @@
-import { flashMessage } from './flash-messages';
-import copyText from '@discoveryjs/discovery/src/core/utils/copy-text';
-
 export const copyToClipboardButton = {
     view: 'button',
     content: 'text:"Copy to clipboard"',
-    onClick(_, { json }) {
-        copyText(json).then(() => {
-            flashMessage('JSON copied to clipboard', 'success');
-        });
+    async onClick(_, __, host) {
+        await host.actions.copyToClipboard();
+        host.actions.flashMessage('JSON copied to clipboard', 'success');
     }
 };
diff --git a/src/discovery/discovery.css b/src/discovery/discovery.css
index ff8d40a..ae74930 100644
--- a/src/discovery/discovery.css
+++ b/src/discovery/discovery.css
@@ -1,4 +1,4 @@
-@import '@discoveryjs/discovery/src/lib.css';
+@import '@discoveryjs/discovery/lib/lib.css';
 @import './pages/raw.css';
 @import './pages/settings.css';
 @import './pages/whatsnew.css';
@@ -8,6 +8,7 @@
     position: fixed;
     inset: 0;
     z-index: 1000;
+    transition: none;
 }
 .discovery-root {
     --discovery-page-padding-right: 12px;
@@ -40,8 +41,21 @@
     border-top-left-radius: 0;
     border-bottom-left-radius: 0;
 }
-.view-page-header .view-button svg {
+.view-page-header .view-button.collapse-all:empty::before,
+.view-page-header .view-button.expand-all:empty::before {
+    content: '';
+    width: 16px;
+    height: 16px;
     margin: -4px 0;
+    display: inline-block;
+    mask: url('./img/collapse-all.svg');
+    mask-size: 16px;
+    background-color: currentColor;
+    visibility: visible;
+}
+
+.view-page-header .view-button.expand-all::before {
+    mask-image: url('./img/expand-all.svg');
 }
 
 /* Safari quirk mode fix */
diff --git a/src/discovery/download-as-file.js b/src/discovery/download-as-file.js
index 5410958..665ada8 100644
--- a/src/discovery/download-as-file.js
+++ b/src/discovery/download-as-file.js
@@ -14,7 +14,7 @@ export function downloadAsFile(text) {
 export const downloadAsFileButton = {
     view: 'button',
     content: 'text:"Download as file"',
-    onClick(_, { json }) {
-        downloadAsFile(json);
+    onClick(_, __, host) {
+        host.actions.downloadAsFile();
     }
 };
diff --git a/src/discovery/flash-messages.js b/src/discovery/flash-messages.js
index dc305be..890b591 100644
--- a/src/discovery/flash-messages.js
+++ b/src/discovery/flash-messages.js
@@ -1,27 +1,20 @@
 import { utils } from '@discoveryjs/discovery';
 
-const flashMessagesContainer = utils.createElement('div', 'flash-messages-container');
-let renderEl;
-
 export default function(host) {
+    const flashMessagesContainer = utils.createElement('div', 'flash-messages-container');
+
     host.dom.container.append(flashMessagesContainer);
-    renderEl = (config, data, context) => {
+
+    host.action.define('flashMessage', async(text, type) => {
         const fragment = document.createDocumentFragment();
 
-        return host.view.render(fragment, config, data, context)
-            .then(() => fragment.firstChild);
-    };
-}
+        await host.view.render(fragment, {
+            view: `alert-${type}`,
+            content: 'text'
+        }, text);
 
-export function flashMessage(text, type) {
-    if (typeof renderEl !== 'function') {
-        return;
-    }
+        const el = fragment.firstChild;
 
-    renderEl({
-        view: `alert-${type}`,
-        content: 'text'
-    }, text).then((el) => {
         flashMessagesContainer.append(el);
         setTimeout(() => el.classList.add('ready-to-remove'), 1250);
         setTimeout(() => el.remove(), 1500);
diff --git a/src/discovery/img/collapse-all.svg b/src/discovery/img/collapse-all.svg
new file mode 100644
index 0000000..0232cdb
--- /dev/null
+++ b/src/discovery/img/collapse-all.svg
@@ -0,0 +1,3 @@
+
+    
+
diff --git a/src/discovery/img/expand-all.svg b/src/discovery/img/expand-all.svg
new file mode 100644
index 0000000..3928a16
--- /dev/null
+++ b/src/discovery/img/expand-all.svg
@@ -0,0 +1,3 @@
+
+    
+
diff --git a/src/discovery/index.js b/src/discovery/index.js
index 09419f0..a49eede 100644
--- a/src/discovery/index.js
+++ b/src/discovery/index.js
@@ -1,55 +1,30 @@
-import { Widget, router } from '@discoveryjs/discovery';
+import './discovery.css';
+import { version } from '../../package.json';
+import { App } from '@discoveryjs/discovery';
+import joraHelpers from './jora-helpers';
 import flashMessages from './flash-messages';
 import navbar from './navbar';
 import * as pages from './pages';
 
 /**
  * Discovery initialization
- * @param {Object} options
- * @param {Object} data
- * @returns {Discovery}
  */
-export function initDiscovery(options, data) {
-    const { settings, version, progressbar, raw } = options;
-    const { darkmode = 'auto' } = settings;
-    const discovery = new Widget({
-        defaultPage: null,
-        container: options.node,
+export function initDiscovery() {
+    const discovery = new App({
+        styles: [{ type: 'link', href: 'sandbox.css' }],
+        embed: true,
         inspector: true,
-        darkmode,
-        darkmodePersistent: false,
-        styles: [{ type: 'link', href: chrome.runtime.getURL('discovery.css') }]
+        darkmodePersistent: true,
+        extensions: [
+            flashMessages,
+            navbar,
+            pages
+        ],
+        setup: ({ addQueryHelpers }) => {
+            addQueryHelpers(joraHelpers);
+        }
     });
 
-    discovery.raw = raw; // TODO: move to context?
-    discovery.apply(router);
-    discovery.apply(flashMessages);
-    discovery.apply(navbar);
-    discovery.apply(pages);
-
-    discovery.setPrepare((_, { addQueryHelpers }) => {
-        addQueryHelpers({
-            weight(current, prec = 1) {
-                const unit = ['bytes', 'kB', 'MB', 'GB'];
-
-                while (current > 1000) {
-                    current = current / 1000;
-                    unit.shift();
-                }
-
-                return current.toFixed(prec).replace(/\.0+$/, '') + unit[0];
-            }
-        });
-    });
-
-    return discovery.setDataProgress(
-        data,
-        {
-            name: options.title,
-            settings,
-            version,
-            createdAt: new Date().toISOString() // TODO fix in discovery
-        },
-        progressbar
-    );
+    discovery.nav.remove('index-page');
+    discovery.version = version;
 }
diff --git a/src/discovery/jora-helpers.js b/src/discovery/jora-helpers.js
new file mode 100644
index 0000000..fe36f0a
--- /dev/null
+++ b/src/discovery/jora-helpers.js
@@ -0,0 +1,12 @@
+export default {
+    weight(current, prec = 1) {
+        const unit = ['bytes', 'kB', 'MB', 'GB'];
+
+        while (current > 1000) {
+            current = current / 1000;
+            unit.shift();
+        }
+
+        return current.toFixed(prec).replace(/\.0+$/, '') + unit[0];
+    }
+};
diff --git a/src/discovery/navbar.js b/src/discovery/navbar.js
index 4750bc9..750800e 100644
--- a/src/discovery/navbar.js
+++ b/src/discovery/navbar.js
@@ -1,12 +1,9 @@
-import { navButtons } from '@discoveryjs/discovery';
-import copyText from '@discoveryjs/discovery/src/core/utils/copy-text';
-import { downloadAsFile } from './download-as-file';
-import { flashMessage } from './flash-messages';
+import copyText from '@discoveryjs/discovery/lib/core/utils/copy-text.js';
 import { showWhatsNew, setWhatsnewViewed } from './pages/whatsnew';
 
 export default host => {
     host.nav.append({
-        when: () => showWhatsNew(host.context) && host.pageId !== 'whatsnew',
+        when: () => showWhatsNew(host.version) && host.pageId !== 'whatsnew',
         content: 'text:"What\'s new"',
         onClick: () => {
             host.setPage('whatsnew');
@@ -15,23 +12,19 @@ export default host => {
     });
     host.nav.append({
         content: 'text:"Copy URL"',
-        onClick: () =>
-            copyText(window.location.toString()) &
-            flashMessage('URL copied to clipboard', 'success')
+        async onClick() {
+            copyText(await host.action.call('permalink'));
+            host.action.call('flashMessage', 'URL copied to clipboard', 'success');
+        }
     });
     host.nav.append({
         when: () => host.pageId !== 'default',
         content: 'text:"Default view"',
-        onClick: () => {
+        onClick() {
             host.setPage('default');
-            history.replaceState(null, null, ' ');
+            history.replaceState(null, null, ' '); // ????
         }
     });
-    host.nav.append({
-        when: () => host.pageId !== 'discovery',
-        content: 'text:"Discover"',
-        onClick: () => host.setPage('discovery')
-    });
     host.nav.append({
         when: () => host.pageId !== 'raw',
         content: 'text:"JSON"',
@@ -40,23 +33,34 @@ export default host => {
             el.title = 'Show JSON as is';
         }
     });
-    host.apply(navButtons.inspect);
     host.nav.menu.append({
         content: 'text:"Download JSON as file"',
-        onClick: (_, { hide }) => hide() & downloadAsFile(host.raw.json)
+        onClick(_, { hide }) {
+            hide();
+            host.action.call('downloadAsFile');
+        }
     });
     host.nav.menu.append({
         content: 'text:"Copy JSON to clipboard"',
-        onClick: (_, { hide }) => hide() &
-            copyText(host.raw.json) &
-            flashMessage('JSON copied to clipboard', 'success')
+        async onClick(_, { hide }) {
+            hide();
+            await host.action.call('copyToClipboard');
+            host.action.call('flashMessage', 'JSON copied to clipboard', 'success');
+        }
     });
     host.nav.menu.append({
         content: 'text:"Settings"',
-        onClick: (_, { hide }) => hide() & host.setPage('settings')
+        onClick(_, { hide }) {
+            hide();
+            host.setPage('settings');
+        }
     });
     host.nav.menu.append({
         content: 'text:"What\'s new"',
-        onClick: (_, { hide }) => hide() & host.setPage('whatsnew') & setWhatsnewViewed(host.context)
+        onClick(_, { hide }) {
+            hide();
+            host.setPage('whatsnew');
+            setWhatsnewViewed(host.context);
+        }
     });
 };
diff --git a/src/discovery/pages/default.js b/src/discovery/pages/default.js
index 2676499..3b94123 100644
--- a/src/discovery/pages/default.js
+++ b/src/discovery/pages/default.js
@@ -12,35 +12,31 @@ export default host => {
                 downloadAsFileButton,
                 {
                     view: 'block',
+                    data: '"getSettings".callAction()',
                     content: [
+                        function(el, config, data, context) {
+                            context.expandLevel = data.expandLevel;
+                        },
                         {
                             view: 'button',
                             className: 'collapse-all',
-                            content: 'text:"-"',
+                            tooltip: 'text:"Collapse all"',
                             onClick(el, data, { onChange }) {
                                 onChange(1, 'expandLevel');
                             },
                             postRender(el, config, data, context) {
                                 context.onChange = config.onChange;
-                                el.title = 'Collapse all';
-                                el.innerHTML = '' +
-                                    '' +
-                                    '';
                             }
                         },
                         {
                             view: 'button',
                             className: 'expand-all',
-                            content: 'text:"+"',
+                            tooltip: 'text:"Expand all"',
                             onClick(el, data, { onChange }) {
                                 onChange(100, 'expandLevel');
                             },
                             postRender(el, config, data, context) {
                                 context.onChange = config.onChange;
-                                el.title = 'Expand all';
-                                el.innerHTML = '' +
-                                    '' +
-                                    '';
                             }
                         }
                     ]
@@ -49,7 +45,8 @@ export default host => {
         },
         content: {
             view: 'struct',
-            expanded: '=+(#.expandLevel or #.settings.expandLevel)'
+            expanded: '=+(#.expandLevel or "getSettings".callAction().expandLevel)',
+            data: '#.data'
         }
     });
 };
diff --git a/src/discovery/pages/raw.js b/src/discovery/pages/raw.js
index f25101e..5c70282 100644
--- a/src/discovery/pages/raw.js
+++ b/src/discovery/pages/raw.js
@@ -2,34 +2,46 @@ import { copyToClipboardButton } from '../copy-to-clipboard';
 import { downloadAsFileButton } from '../download-as-file';
 
 export default host => {
-    host.view.define('raw', (el, _, raw) => {
+    host.view.define('raw', async function(el) {
         const contentEl = el.appendChild(document.createElement('pre'));
 
         contentEl.className = 'content';
 
-        if (raw.firstSlice) {
-            contentEl.append(raw.firstSlice);
-            host.view.render(el, {
+        const {
+            firstSliceText,
+            firstSliceSize,
+            fullSize
+        } = await host.action.call('getRaw');
+
+        contentEl.append(firstSliceText);
+
+        if (firstSliceSize < fullSize) {
+            this.render(el, {
                 view: 'alert-warning',
                 className: 'too-big-json',
                 content: [
-                    'text:`JSON is too big (${size.weight()} bytes), only first ${firstSlice.size().weight()} is shown. Output the entire JSON may cause to browser\'s tab freezing for a while. `',
+                    'text:`JSON is too big (${fullSize.weight()} bytes), only first ${firstSliceSize.weight()} is shown. Output the entire JSON may cause to browser\'s tab freezing for a while. `',
                     {
                         view: 'button',
                         content: 'text:"Show all"',
                         onClick(el) {
                             const alertEl = el.parentNode;
+
                             alertEl.textContent = 'Output entire JSON...';
-                            setTimeout(() => {
-                                contentEl.append(raw.json.slice(raw.firstSlice.length));
+
+                            setTimeout(async() => {
+                                const { json } = await host.action.call('getRawFull');
+
+                                contentEl.textContent = json;
                                 alertEl.remove();
                             }, 50);
                         }
                     }
                 ]
-            }, raw);
-        } else {
-            contentEl.append(raw.json);
+            }, {
+                firstSliceSize,
+                fullSize
+            });
         }
     });
 
diff --git a/src/discovery/pages/settings.js b/src/discovery/pages/settings.js
index 43bbe64..24df0a1 100644
--- a/src/discovery/pages/settings.js
+++ b/src/discovery/pages/settings.js
@@ -74,8 +74,12 @@ export default host => {
         }
     ].map(content => ({ view: 'fieldset', content }));
 
-    host.page.define('settings', function(el, data, context) {
-        const { settings } = context;
+    host.page.define('settings', async function(el, data) {
+        if (!data) {
+            return;
+        }
+
+        const settings = await host.query('"getSettings".callAction()', data);
 
         host.view.render(el, [
             'h1:"JsonDiscovery settings"',
@@ -103,16 +107,11 @@ export default host => {
         const { valid, errors } = validate(settings);
 
         if (valid) {
-            if (typeof chrome !== 'undefined') {
-                chrome.storage.sync.set(settings);
-            } else if (typeof safari !== 'undefined') {
-                safari.extension.dispatchMessage('setSettings', settings);
-            }
+            host.query(`"setSettings".callAction(${JSON.stringify(settings)})`, host.data);
 
-            host.context.settings = settings;
-            host.flashMessage('Options saved.', 'success');
+            host.action.call('flashMessage', 'Options saved.', 'success');
         } else {
-            host.flashMessage(errors.join(' '), 'danger');
+            host.action.call('flashMessage', errors.join(' '), 'danger');
         }
     }
 
diff --git a/src/discovery/pages/whatsnew.js b/src/discovery/pages/whatsnew.js
index d3197e2..1fe153b 100644
--- a/src/discovery/pages/whatsnew.js
+++ b/src/discovery/pages/whatsnew.js
@@ -8,12 +8,9 @@ export const setWhatsnewViewed = ({ version }) => {
     }
 };
 export const showWhatsNew = context => {
-    const { version } = context;
+    const { version } = context || {};
     return !version ? false : !(
-        context &&
-        context.settings &&
-        context.settings.whatsnew &&
-        context.settings.whatsnew[version]
+        context?.settings?.whatsnew?.[version]
     );
 };
 
diff --git a/src/manifest.js b/src/manifest.js
index d41359e..f633279 100644
--- a/src/manifest.js
+++ b/src/manifest.js
@@ -8,7 +8,7 @@ module.exports = function(browser = 'chrome') {
     manifest.version = packageJson.version;
 
     if (browser === 'firefox') {
-        manifest.applications = {
+        manifest.browser_specific_settings = { // eslint-disable-line camelcase
             gecko: {
                 id: 'jsondiscovery@exdis.me',
                 strict_min_version: '57.0' // eslint-disable-line camelcase
diff --git a/src/manifest.json b/src/manifest.json
index 562a4dc..3e4171d 100644
--- a/src/manifest.json
+++ b/src/manifest.json
@@ -4,31 +4,40 @@
     "short_name": "JsonDiscovery",
     "description": "Browser extension that changes the way you're viewing JSON",
     "author": "exdis",
-    "manifest_version": 2,
+    "manifest_version": 3,
     "icons": {
         "16": "icons/16.png",
         "48": "icons/48.png",
         "128": "icons/128.png"
     },
     "permissions": [
-        "",
         "storage"
     ],
+    "host_permissions": [
+        "*://*/*"
+    ],
     "content_scripts": [{
         "js": ["init.js"],
         "run_at": "document_start",
         "matches": [""]
     }],
-    "background": {
-        "scripts": ["background.js"]
+    "sandbox": {
+        "pages": [
+            "sandbox.html"
+        ]
     },
     "web_accessible_resources": [
-        "discovery.css",
-        "discovery.js",
-        "discovery-esm.js",
-        "preloader.css",
-        "icons/*",
-        "assets/*"
+        {
+            "resources": [
+                "sandbox.html",
+                "sandbox.js",
+                "icons/*",
+                "assets/*"
+            ],
+            "matches": [
+                ""
+            ]
+        }
     ],
     "offline_enabled": true
 }
diff --git a/src/sandbox.html b/src/sandbox.html
new file mode 100644
index 0000000..4495db1
--- /dev/null
+++ b/src/sandbox.html
@@ -0,0 +1,21 @@
+
+
+
+
+    
+    
+
+
diff --git a/src/sandbox.js b/src/sandbox.js
new file mode 100644
index 0000000..8cbacd4
--- /dev/null
+++ b/src/sandbox.js
@@ -0,0 +1,3 @@
+import { initDiscovery } from './discovery';
+
+initDiscovery();