├── .editorconfig ├── .eslintignore ├── .eslintrc.cjs ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc ├── .typesafe-i18n.json ├── LICENSE ├── README.md ├── package.json ├── playwright.config.ts ├── pnpm-lock.yaml ├── src ├── app.d.ts ├── app.html ├── components │ ├── HeadHrefLangs.svelte │ ├── Header.svelte │ └── LocaleSwitcher.svelte ├── hooks.server.ts ├── i18n │ ├── de │ │ └── index.ts │ ├── en │ │ └── index.ts │ ├── formatters.ts │ ├── i18n-svelte.ts │ ├── i18n-types.ts │ ├── i18n-util.async.ts │ ├── i18n-util.sync.ts │ ├── i18n-util.ts │ ├── it │ │ └── index.ts │ └── ru │ │ └── index.ts ├── index.test.ts ├── lib │ └── index.ts ├── params │ ├── l_about.ts │ ├── l_english.ts │ ├── l_news.ts │ └── lang.ts ├── routes │ ├── +error.svelte │ ├── +layout.server.ts │ ├── +layout.svelte │ ├── +layout.ts │ ├── [[lang=lang]] │ │ ├── +layout.ts │ │ ├── +page.svelte │ │ ├── +page.ts │ │ ├── [l_about=l_about] │ │ │ ├── +page.svelte │ │ │ ├── +page.ts │ │ │ └── [l_english=l_english] │ │ │ │ ├── +page.svelte │ │ │ │ └── +page.ts │ │ └── [l_news=l_news] │ │ │ ├── +page.server.ts │ │ │ ├── +page.svelte │ │ │ ├── +page.ts │ │ │ └── [post] │ │ │ ├── +page.server.ts │ │ │ ├── +page.svelte │ │ │ └── +page.ts │ └── api │ │ └── spectators │ │ └── +server.ts ├── styles │ └── global.scss └── utils │ └── db.ts ├── static └── favicon.png ├── svelte.config.js ├── tests └── test.ts ├── tsconfig.json └── vite.config.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | indent_style = tab 7 | indent_size = 4 8 | charset = utf-8 9 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: [ 4 | 'eslint:recommended', 5 | 'plugin:@typescript-eslint/recommended', 6 | 'plugin:svelte/recommended', 7 | 'prettier' 8 | ], 9 | parser: '@typescript-eslint/parser', 10 | plugins: ['@typescript-eslint'], 11 | parserOptions: { 12 | sourceType: 'module', 13 | ecmaVersion: 2020, 14 | extraFileExtensions: ['.svelte'] 15 | }, 16 | env: { 17 | browser: true, 18 | es2017: true, 19 | node: true 20 | }, 21 | overrides: [ 22 | { 23 | files: ['*.svelte'], 24 | parser: 'svelte-eslint-parser', 25 | parserOptions: { 26 | parser: '@typescript-eslint/parser' 27 | } 28 | } 29 | ] 30 | }; 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /dist 5 | /.svelte-kit 6 | /package 7 | /coverage 8 | .env 9 | .env.* 10 | !.env.example 11 | vite.config.js.timestamp-* 12 | vite.config.ts.timestamp-* 13 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | resolution-mode=highest 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /dist 5 | /.svelte-kit 6 | /package 7 | /coverage 8 | .env 9 | .env.* 10 | !.env.example 11 | 12 | # Ignore files for PNPM, NPM and YARN 13 | pnpm-lock.yaml 14 | package-lock.json 15 | yarn.lock 16 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte"], 7 | "pluginSearchDirs": ["."], 8 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 9 | } 10 | -------------------------------------------------------------------------------- /.typesafe-i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseLocale": "en", 3 | "esmImports": true, 4 | "runAfterGenerator": "pnpm run format:i18n", 5 | "adapter": "svelte", 6 | "$schema": "https://unpkg.com/typesafe-i18n@5.24.3/schema/typesafe-i18n.json" 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 rinart73 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Localize URL [![NPM lite-youtube-embed package](https://img.shields.io/npm/v/sveltekit-localize-url.svg)](https://npmjs.org/package/sveltekit-localize-url) 2 | 3 | **SvelteKit library that handles URL localization and routing.** 4 | 5 | > **Note** 6 | > This package is WIP. It works but the future updates may introduce breaking changes to simplify setup or introduce features. 7 | 8 | ## Features 9 | 10 | - Supports 3 URL structure types: 11 | - Locale prefix except for the base locale (default) - `/about`, `/ru/o-nas`; 12 | - Prefix for every locale - `/en/about`, `/ru/o-nas`; 13 | - Separate domains for locales - `example.com/about`, `example.ru/o-nas`. 14 | - Validates current URL: 15 | - Redirects `/ru/about-us` to the corrected `/ru/o-nas`; 16 | - Supports partially localized pages - throws 404 if a page isn’t available for the requested locale. 17 | - Helps to build localized URLs; 18 | - Builds alternate URLs for the ` matchParam(param, localizedParam); 57 | ``` 58 | 59 | When SvelteKit will match params, it will decide that **all of following paths are valid**: 60 | 61 | - `/about-us` 62 | - `/en/about-us` 63 | - `/ru/about-us` 64 | - `/it/about-us` 65 | - `/o-nas` 66 | - `/en/o-nas` 67 | - `/ru/o-nas` 68 | - `/it/o-nas` 69 | 70 | However the `validateUrlLocale()` function that is called in the `src/routes/[[lang=lang]]/+layout.ts` will take care of that by taking registered params and using them to construct a correct path for the current locale. 71 | 72 | - If the paths match, proceed; 73 | - If the paths don't match, redirect to a corrected URL; 74 | - If it can't construct a path for the current locale, throw 404. 75 | 76 | So in the end we'll get the following: 77 | 78 | - `/about-us` - Correct; 79 | - `/ru/o-nas` - Correct; 80 | - `/o-nas`, `/en/about-us`, `/en/o-nas` - Redirect to `/about-us`; 81 | - `/ru/about-us` - Redirect to `/ru/o-nas`; 82 | - `/it/about-us`, `/it/o-nas` - 404 Not Found. 83 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sveltekit-localize-url", 3 | "description": "SvelteKit library that handles URL localization and routing.", 4 | "version": "0.1.2", 5 | "author": "Rinart73", 6 | "license": "MIT", 7 | "homepage": "https://github.com/rinart73/sveltekit-localize-url", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/rinart73/sveltekit-localize-url" 11 | }, 12 | "scripts": { 13 | "dev": "run-p dev:vite typesafe-i18n", 14 | "dev:vite": "vite dev", 15 | "build": "vite build && npm run package", 16 | "preview": "vite preview", 17 | "package": "svelte-kit sync && svelte-package && publint", 18 | "prepublishOnly": "npm run package", 19 | "test": "npm run npm run test:unit && test:integration", 20 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 21 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 22 | "lint": "prettier --plugin-search-dir . --check . && eslint .", 23 | "format": "prettier --plugin-search-dir . --write .", 24 | "format:i18n": "prettier --plugin-search-dir=. --write ./src/i18n", 25 | "test:integration": "playwright test", 26 | "test:unit": "vitest --coverage", 27 | "typesafe-i18n": "typesafe-i18n" 28 | }, 29 | "exports": { 30 | ".": { 31 | "types": "./dist/index.d.ts", 32 | "svelte": "./dist/index.js" 33 | } 34 | }, 35 | "files": [ 36 | "dist", 37 | "!dist/**/*.test.*", 38 | "!dist/**/*.spec.*" 39 | ], 40 | "peerDependencies": { 41 | "svelte": "^4.0.0" 42 | }, 43 | "devDependencies": { 44 | "@playwright/test": "^1.35.1", 45 | "@sveltejs/adapter-auto": "^2.1.0", 46 | "@sveltejs/kit": "^1.22.1", 47 | "@sveltejs/package": "^2.1.0", 48 | "@typescript-eslint/eslint-plugin": "^5.61.0", 49 | "@typescript-eslint/parser": "^5.61.0", 50 | "@vitest/coverage-v8": "^0.33.0", 51 | "eslint": "^8.44.0", 52 | "eslint-config-prettier": "^8.8.0", 53 | "eslint-plugin-svelte": "^2.32.2", 54 | "npm-run-all": "^4.1.5", 55 | "prettier": "^2.8.8", 56 | "prettier-plugin-svelte": "^2.10.1", 57 | "publint": "^0.1.16", 58 | "sass": "^1.63.6", 59 | "svelte": "^4.0.5", 60 | "svelte-check": "^3.4.5", 61 | "tslib": "^2.6.0", 62 | "typescript": "^5.1.6", 63 | "vite": "^4.4.2", 64 | "vitest": "^0.32.4", 65 | "typesafe-i18n": "^5.24.3" 66 | }, 67 | "svelte": "./dist/index.js", 68 | "types": "./dist/index.d.ts", 69 | "type": "module" 70 | } 71 | -------------------------------------------------------------------------------- /playwright.config.ts: -------------------------------------------------------------------------------- 1 | import type { PlaywrightTestConfig } from '@playwright/test'; 2 | 3 | const config: PlaywrightTestConfig = { 4 | webServer: { 5 | command: 'npm run build && npm run preview', 6 | port: 4173 7 | }, 8 | testDir: 'tests', 9 | testMatch: /(.+\.)?(test|spec)\.[jt]s/ 10 | }; 11 | 12 | export default config; 13 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | devDependencies: 8 | '@playwright/test': 9 | specifier: ^1.35.1 10 | version: 1.35.1 11 | '@sveltejs/adapter-auto': 12 | specifier: ^2.1.0 13 | version: 2.1.0(@sveltejs/kit@1.22.1) 14 | '@sveltejs/kit': 15 | specifier: ^1.22.1 16 | version: 1.22.1(svelte@4.0.5)(vite@4.4.2) 17 | '@sveltejs/package': 18 | specifier: ^2.1.0 19 | version: 2.1.0(svelte@4.0.5)(typescript@5.1.6) 20 | '@typescript-eslint/eslint-plugin': 21 | specifier: ^5.61.0 22 | version: 5.61.0(@typescript-eslint/parser@5.61.0)(eslint@8.44.0)(typescript@5.1.6) 23 | '@typescript-eslint/parser': 24 | specifier: ^5.61.0 25 | version: 5.61.0(eslint@8.44.0)(typescript@5.1.6) 26 | '@vitest/coverage-v8': 27 | specifier: ^0.33.0 28 | version: 0.33.0(vitest@0.32.4) 29 | eslint: 30 | specifier: ^8.44.0 31 | version: 8.44.0 32 | eslint-config-prettier: 33 | specifier: ^8.8.0 34 | version: 8.8.0(eslint@8.44.0) 35 | eslint-plugin-svelte: 36 | specifier: ^2.32.2 37 | version: 2.32.2(eslint@8.44.0)(svelte@4.0.5) 38 | npm-run-all: 39 | specifier: ^4.1.5 40 | version: 4.1.5 41 | prettier: 42 | specifier: ^2.8.8 43 | version: 2.8.8 44 | prettier-plugin-svelte: 45 | specifier: ^2.10.1 46 | version: 2.10.1(prettier@2.8.8)(svelte@4.0.5) 47 | publint: 48 | specifier: ^0.1.16 49 | version: 0.1.16 50 | sass: 51 | specifier: ^1.63.6 52 | version: 1.63.6 53 | svelte: 54 | specifier: ^4.0.5 55 | version: 4.0.5 56 | svelte-check: 57 | specifier: ^3.4.5 58 | version: 3.4.5(postcss@8.4.25)(sass@1.63.6)(svelte@4.0.5) 59 | tslib: 60 | specifier: ^2.6.0 61 | version: 2.6.0 62 | typesafe-i18n: 63 | specifier: ^5.24.3 64 | version: 5.24.3(typescript@5.1.6) 65 | typescript: 66 | specifier: ^5.1.6 67 | version: 5.1.6 68 | vite: 69 | specifier: ^4.4.2 70 | version: 4.4.2(@types/node@20.4.1)(sass@1.63.6) 71 | vitest: 72 | specifier: ^0.32.4 73 | version: 0.32.4(sass@1.63.6) 74 | 75 | packages: 76 | 77 | /@aashutoshrathi/word-wrap@1.2.6: 78 | resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} 79 | engines: {node: '>=0.10.0'} 80 | dev: true 81 | 82 | /@ampproject/remapping@2.2.1: 83 | resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} 84 | engines: {node: '>=6.0.0'} 85 | dependencies: 86 | '@jridgewell/gen-mapping': 0.3.3 87 | '@jridgewell/trace-mapping': 0.3.18 88 | dev: true 89 | 90 | /@bcoe/v8-coverage@0.2.3: 91 | resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} 92 | dev: true 93 | 94 | /@esbuild/android-arm64@0.18.11: 95 | resolution: {integrity: sha512-snieiq75Z1z5LJX9cduSAjUr7vEI1OdlzFPMw0HH5YI7qQHDd3qs+WZoMrWYDsfRJSq36lIA6mfZBkvL46KoIw==} 96 | engines: {node: '>=12'} 97 | cpu: [arm64] 98 | os: [android] 99 | requiresBuild: true 100 | dev: true 101 | optional: true 102 | 103 | /@esbuild/android-arm@0.18.11: 104 | resolution: {integrity: sha512-q4qlUf5ucwbUJZXF5tEQ8LF7y0Nk4P58hOsGk3ucY0oCwgQqAnqXVbUuahCddVHfrxmpyewRpiTHwVHIETYu7Q==} 105 | engines: {node: '>=12'} 106 | cpu: [arm] 107 | os: [android] 108 | requiresBuild: true 109 | dev: true 110 | optional: true 111 | 112 | /@esbuild/android-x64@0.18.11: 113 | resolution: {integrity: sha512-iPuoxQEV34+hTF6FT7om+Qwziv1U519lEOvekXO9zaMMlT9+XneAhKL32DW3H7okrCOBQ44BMihE8dclbZtTuw==} 114 | engines: {node: '>=12'} 115 | cpu: [x64] 116 | os: [android] 117 | requiresBuild: true 118 | dev: true 119 | optional: true 120 | 121 | /@esbuild/darwin-arm64@0.18.11: 122 | resolution: {integrity: sha512-Gm0QkI3k402OpfMKyQEEMG0RuW2LQsSmI6OeO4El2ojJMoF5NLYb3qMIjvbG/lbMeLOGiW6ooU8xqc+S0fgz2w==} 123 | engines: {node: '>=12'} 124 | cpu: [arm64] 125 | os: [darwin] 126 | requiresBuild: true 127 | dev: true 128 | optional: true 129 | 130 | /@esbuild/darwin-x64@0.18.11: 131 | resolution: {integrity: sha512-N15Vzy0YNHu6cfyDOjiyfJlRJCB/ngKOAvoBf1qybG3eOq0SL2Lutzz9N7DYUbb7Q23XtHPn6lMDF6uWbGv9Fw==} 132 | engines: {node: '>=12'} 133 | cpu: [x64] 134 | os: [darwin] 135 | requiresBuild: true 136 | dev: true 137 | optional: true 138 | 139 | /@esbuild/freebsd-arm64@0.18.11: 140 | resolution: {integrity: sha512-atEyuq6a3omEY5qAh5jIORWk8MzFnCpSTUruBgeyN9jZq1K/QI9uke0ATi3MHu4L8c59CnIi4+1jDKMuqmR71A==} 141 | engines: {node: '>=12'} 142 | cpu: [arm64] 143 | os: [freebsd] 144 | requiresBuild: true 145 | dev: true 146 | optional: true 147 | 148 | /@esbuild/freebsd-x64@0.18.11: 149 | resolution: {integrity: sha512-XtuPrEfBj/YYYnAAB7KcorzzpGTvOr/dTtXPGesRfmflqhA4LMF0Gh/n5+a9JBzPuJ+CGk17CA++Hmr1F/gI0Q==} 150 | engines: {node: '>=12'} 151 | cpu: [x64] 152 | os: [freebsd] 153 | requiresBuild: true 154 | dev: true 155 | optional: true 156 | 157 | /@esbuild/linux-arm64@0.18.11: 158 | resolution: {integrity: sha512-c6Vh2WS9VFKxKZ2TvJdA7gdy0n6eSy+yunBvv4aqNCEhSWVor1TU43wNRp2YLO9Vng2G+W94aRz+ILDSwAiYog==} 159 | engines: {node: '>=12'} 160 | cpu: [arm64] 161 | os: [linux] 162 | requiresBuild: true 163 | dev: true 164 | optional: true 165 | 166 | /@esbuild/linux-arm@0.18.11: 167 | resolution: {integrity: sha512-Idipz+Taso/toi2ETugShXjQ3S59b6m62KmLHkJlSq/cBejixmIydqrtM2XTvNCywFl3VC7SreSf6NV0i6sRyg==} 168 | engines: {node: '>=12'} 169 | cpu: [arm] 170 | os: [linux] 171 | requiresBuild: true 172 | dev: true 173 | optional: true 174 | 175 | /@esbuild/linux-ia32@0.18.11: 176 | resolution: {integrity: sha512-S3hkIF6KUqRh9n1Q0dSyYcWmcVa9Cg+mSoZEfFuzoYXXsk6196qndrM+ZiHNwpZKi3XOXpShZZ+9dfN5ykqjjw==} 177 | engines: {node: '>=12'} 178 | cpu: [ia32] 179 | os: [linux] 180 | requiresBuild: true 181 | dev: true 182 | optional: true 183 | 184 | /@esbuild/linux-loong64@0.18.11: 185 | resolution: {integrity: sha512-MRESANOoObQINBA+RMZW+Z0TJWpibtE7cPFnahzyQHDCA9X9LOmGh68MVimZlM9J8n5Ia8lU773te6O3ILW8kw==} 186 | engines: {node: '>=12'} 187 | cpu: [loong64] 188 | os: [linux] 189 | requiresBuild: true 190 | dev: true 191 | optional: true 192 | 193 | /@esbuild/linux-mips64el@0.18.11: 194 | resolution: {integrity: sha512-qVyPIZrXNMOLYegtD1u8EBccCrBVshxMrn5MkuFc3mEVsw7CCQHaqZ4jm9hbn4gWY95XFnb7i4SsT3eflxZsUg==} 195 | engines: {node: '>=12'} 196 | cpu: [mips64el] 197 | os: [linux] 198 | requiresBuild: true 199 | dev: true 200 | optional: true 201 | 202 | /@esbuild/linux-ppc64@0.18.11: 203 | resolution: {integrity: sha512-T3yd8vJXfPirZaUOoA9D2ZjxZX4Gr3QuC3GztBJA6PklLotc/7sXTOuuRkhE9W/5JvJP/K9b99ayPNAD+R+4qQ==} 204 | engines: {node: '>=12'} 205 | cpu: [ppc64] 206 | os: [linux] 207 | requiresBuild: true 208 | dev: true 209 | optional: true 210 | 211 | /@esbuild/linux-riscv64@0.18.11: 212 | resolution: {integrity: sha512-evUoRPWiwuFk++snjH9e2cAjF5VVSTj+Dnf+rkO/Q20tRqv+644279TZlPK8nUGunjPAtQRCj1jQkDAvL6rm2w==} 213 | engines: {node: '>=12'} 214 | cpu: [riscv64] 215 | os: [linux] 216 | requiresBuild: true 217 | dev: true 218 | optional: true 219 | 220 | /@esbuild/linux-s390x@0.18.11: 221 | resolution: {integrity: sha512-/SlRJ15XR6i93gRWquRxYCfhTeC5PdqEapKoLbX63PLCmAkXZHY2uQm2l9bN0oPHBsOw2IswRZctMYS0MijFcg==} 222 | engines: {node: '>=12'} 223 | cpu: [s390x] 224 | os: [linux] 225 | requiresBuild: true 226 | dev: true 227 | optional: true 228 | 229 | /@esbuild/linux-x64@0.18.11: 230 | resolution: {integrity: sha512-xcncej+wF16WEmIwPtCHi0qmx1FweBqgsRtEL1mSHLFR6/mb3GEZfLQnx+pUDfRDEM4DQF8dpXIW7eDOZl1IbA==} 231 | engines: {node: '>=12'} 232 | cpu: [x64] 233 | os: [linux] 234 | requiresBuild: true 235 | dev: true 236 | optional: true 237 | 238 | /@esbuild/netbsd-x64@0.18.11: 239 | resolution: {integrity: sha512-aSjMHj/F7BuS1CptSXNg6S3M4F3bLp5wfFPIJM+Km2NfIVfFKhdmfHF9frhiCLIGVzDziggqWll0B+9AUbud/Q==} 240 | engines: {node: '>=12'} 241 | cpu: [x64] 242 | os: [netbsd] 243 | requiresBuild: true 244 | dev: true 245 | optional: true 246 | 247 | /@esbuild/openbsd-x64@0.18.11: 248 | resolution: {integrity: sha512-tNBq+6XIBZtht0xJGv7IBB5XaSyvYPCm1PxJ33zLQONdZoLVM0bgGqUrXnJyiEguD9LU4AHiu+GCXy/Hm9LsdQ==} 249 | engines: {node: '>=12'} 250 | cpu: [x64] 251 | os: [openbsd] 252 | requiresBuild: true 253 | dev: true 254 | optional: true 255 | 256 | /@esbuild/sunos-x64@0.18.11: 257 | resolution: {integrity: sha512-kxfbDOrH4dHuAAOhr7D7EqaYf+W45LsAOOhAet99EyuxxQmjbk8M9N4ezHcEiCYPaiW8Dj3K26Z2V17Gt6p3ng==} 258 | engines: {node: '>=12'} 259 | cpu: [x64] 260 | os: [sunos] 261 | requiresBuild: true 262 | dev: true 263 | optional: true 264 | 265 | /@esbuild/win32-arm64@0.18.11: 266 | resolution: {integrity: sha512-Sh0dDRyk1Xi348idbal7lZyfSkjhJsdFeuC13zqdipsvMetlGiFQNdO+Yfp6f6B4FbyQm7qsk16yaZk25LChzg==} 267 | engines: {node: '>=12'} 268 | cpu: [arm64] 269 | os: [win32] 270 | requiresBuild: true 271 | dev: true 272 | optional: true 273 | 274 | /@esbuild/win32-ia32@0.18.11: 275 | resolution: {integrity: sha512-o9JUIKF1j0rqJTFbIoF4bXj6rvrTZYOrfRcGyL0Vm5uJ/j5CkBD/51tpdxe9lXEDouhRgdr/BYzUrDOvrWwJpg==} 276 | engines: {node: '>=12'} 277 | cpu: [ia32] 278 | os: [win32] 279 | requiresBuild: true 280 | dev: true 281 | optional: true 282 | 283 | /@esbuild/win32-x64@0.18.11: 284 | resolution: {integrity: sha512-rQI4cjLHd2hGsM1LqgDI7oOCYbQ6IBOVsX9ejuRMSze0GqXUG2ekwiKkiBU1pRGSeCqFFHxTrcEydB2Hyoz9CA==} 285 | engines: {node: '>=12'} 286 | cpu: [x64] 287 | os: [win32] 288 | requiresBuild: true 289 | dev: true 290 | optional: true 291 | 292 | /@eslint-community/eslint-utils@4.4.0(eslint@8.44.0): 293 | resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} 294 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 295 | peerDependencies: 296 | eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 297 | dependencies: 298 | eslint: 8.44.0 299 | eslint-visitor-keys: 3.4.1 300 | dev: true 301 | 302 | /@eslint-community/regexpp@4.5.1: 303 | resolution: {integrity: sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==} 304 | engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} 305 | dev: true 306 | 307 | /@eslint/eslintrc@2.1.0: 308 | resolution: {integrity: sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==} 309 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 310 | dependencies: 311 | ajv: 6.12.6 312 | debug: 4.3.4 313 | espree: 9.6.0 314 | globals: 13.20.0 315 | ignore: 5.2.4 316 | import-fresh: 3.3.0 317 | js-yaml: 4.1.0 318 | minimatch: 3.1.2 319 | strip-json-comments: 3.1.1 320 | transitivePeerDependencies: 321 | - supports-color 322 | dev: true 323 | 324 | /@eslint/js@8.44.0: 325 | resolution: {integrity: sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==} 326 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 327 | dev: true 328 | 329 | /@humanwhocodes/config-array@0.11.10: 330 | resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==} 331 | engines: {node: '>=10.10.0'} 332 | dependencies: 333 | '@humanwhocodes/object-schema': 1.2.1 334 | debug: 4.3.4 335 | minimatch: 3.1.2 336 | transitivePeerDependencies: 337 | - supports-color 338 | dev: true 339 | 340 | /@humanwhocodes/module-importer@1.0.1: 341 | resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} 342 | engines: {node: '>=12.22'} 343 | dev: true 344 | 345 | /@humanwhocodes/object-schema@1.2.1: 346 | resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} 347 | dev: true 348 | 349 | /@istanbuljs/schema@0.1.3: 350 | resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} 351 | engines: {node: '>=8'} 352 | dev: true 353 | 354 | /@jest/schemas@29.6.0: 355 | resolution: {integrity: sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==} 356 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 357 | dependencies: 358 | '@sinclair/typebox': 0.27.8 359 | dev: true 360 | 361 | /@jridgewell/gen-mapping@0.3.3: 362 | resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} 363 | engines: {node: '>=6.0.0'} 364 | dependencies: 365 | '@jridgewell/set-array': 1.1.2 366 | '@jridgewell/sourcemap-codec': 1.4.15 367 | '@jridgewell/trace-mapping': 0.3.18 368 | dev: true 369 | 370 | /@jridgewell/resolve-uri@3.1.0: 371 | resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} 372 | engines: {node: '>=6.0.0'} 373 | dev: true 374 | 375 | /@jridgewell/set-array@1.1.2: 376 | resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} 377 | engines: {node: '>=6.0.0'} 378 | dev: true 379 | 380 | /@jridgewell/sourcemap-codec@1.4.14: 381 | resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} 382 | dev: true 383 | 384 | /@jridgewell/sourcemap-codec@1.4.15: 385 | resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} 386 | dev: true 387 | 388 | /@jridgewell/trace-mapping@0.3.18: 389 | resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} 390 | dependencies: 391 | '@jridgewell/resolve-uri': 3.1.0 392 | '@jridgewell/sourcemap-codec': 1.4.14 393 | dev: true 394 | 395 | /@nodelib/fs.scandir@2.1.5: 396 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 397 | engines: {node: '>= 8'} 398 | dependencies: 399 | '@nodelib/fs.stat': 2.0.5 400 | run-parallel: 1.2.0 401 | dev: true 402 | 403 | /@nodelib/fs.stat@2.0.5: 404 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 405 | engines: {node: '>= 8'} 406 | dev: true 407 | 408 | /@nodelib/fs.walk@1.2.8: 409 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 410 | engines: {node: '>= 8'} 411 | dependencies: 412 | '@nodelib/fs.scandir': 2.1.5 413 | fastq: 1.15.0 414 | dev: true 415 | 416 | /@playwright/test@1.35.1: 417 | resolution: {integrity: sha512-b5YoFe6J9exsMYg0pQAobNDR85T1nLumUYgUTtKm4d21iX2L7WqKq9dW8NGJ+2vX0etZd+Y7UeuqsxDXm9+5ZA==} 418 | engines: {node: '>=16'} 419 | hasBin: true 420 | dependencies: 421 | '@types/node': 20.4.1 422 | playwright-core: 1.35.1 423 | optionalDependencies: 424 | fsevents: 2.3.2 425 | dev: true 426 | 427 | /@polka/url@1.0.0-next.21: 428 | resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==} 429 | dev: true 430 | 431 | /@sinclair/typebox@0.27.8: 432 | resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} 433 | dev: true 434 | 435 | /@sveltejs/adapter-auto@2.1.0(@sveltejs/kit@1.22.1): 436 | resolution: {integrity: sha512-o2pZCfATFtA/Gw/BB0Xm7k4EYaekXxaPGER3xGSY3FvzFJGTlJlZjBseaXwYSM94lZ0HniOjTokN3cWaLX6fow==} 437 | peerDependencies: 438 | '@sveltejs/kit': ^1.0.0 439 | dependencies: 440 | '@sveltejs/kit': 1.22.1(svelte@4.0.5)(vite@4.4.2) 441 | import-meta-resolve: 3.0.0 442 | dev: true 443 | 444 | /@sveltejs/kit@1.22.1(svelte@4.0.5)(vite@4.4.2): 445 | resolution: {integrity: sha512-idFhKVEHuCKbTETvuo3V7UShqSYX9JMKVJXP546dOTkh5ZRejo5XtKtsB5TCSwNBa0TH8hIV44/bnylaFhM1Vg==} 446 | engines: {node: ^16.14 || >=18} 447 | hasBin: true 448 | requiresBuild: true 449 | peerDependencies: 450 | svelte: ^3.54.0 || ^4.0.0-next.0 451 | vite: ^4.0.0 452 | dependencies: 453 | '@sveltejs/vite-plugin-svelte': 2.4.2(svelte@4.0.5)(vite@4.4.2) 454 | '@types/cookie': 0.5.1 455 | cookie: 0.5.0 456 | devalue: 4.3.2 457 | esm-env: 1.0.0 458 | kleur: 4.1.5 459 | magic-string: 0.30.1 460 | mime: 3.0.0 461 | sade: 1.8.1 462 | set-cookie-parser: 2.6.0 463 | sirv: 2.0.3 464 | svelte: 4.0.5 465 | undici: 5.22.1 466 | vite: 4.4.2(@types/node@20.4.1)(sass@1.63.6) 467 | transitivePeerDependencies: 468 | - supports-color 469 | dev: true 470 | 471 | /@sveltejs/package@2.1.0(svelte@4.0.5)(typescript@5.1.6): 472 | resolution: {integrity: sha512-c6PLH9G2YLQ48kqrS2XX422BrLNABBstSiapamchVJaQnOTXyJmUR8KmoCCySnzVy3PiYL6jg12UnoPmjW3SwA==} 473 | engines: {node: ^16.14 || >=18} 474 | hasBin: true 475 | peerDependencies: 476 | svelte: ^3.44.0 || ^4.0.0 477 | dependencies: 478 | chokidar: 3.5.3 479 | kleur: 4.1.5 480 | sade: 1.8.1 481 | svelte: 4.0.5 482 | svelte2tsx: 0.6.19(svelte@4.0.5)(typescript@5.1.6) 483 | transitivePeerDependencies: 484 | - typescript 485 | dev: true 486 | 487 | /@sveltejs/vite-plugin-svelte-inspector@1.0.3(@sveltejs/vite-plugin-svelte@2.4.2)(svelte@4.0.5)(vite@4.4.2): 488 | resolution: {integrity: sha512-Khdl5jmmPN6SUsVuqSXatKpQTMIifoQPDanaxC84m9JxIibWvSABJyHpyys0Z+1yYrxY5TTEQm+6elh0XCMaOA==} 489 | engines: {node: ^14.18.0 || >= 16} 490 | peerDependencies: 491 | '@sveltejs/vite-plugin-svelte': ^2.2.0 492 | svelte: ^3.54.0 || ^4.0.0 493 | vite: ^4.0.0 494 | dependencies: 495 | '@sveltejs/vite-plugin-svelte': 2.4.2(svelte@4.0.5)(vite@4.4.2) 496 | debug: 4.3.4 497 | svelte: 4.0.5 498 | vite: 4.4.2(@types/node@20.4.1)(sass@1.63.6) 499 | transitivePeerDependencies: 500 | - supports-color 501 | dev: true 502 | 503 | /@sveltejs/vite-plugin-svelte@2.4.2(svelte@4.0.5)(vite@4.4.2): 504 | resolution: {integrity: sha512-ePfcC48ftMKhkT0OFGdOyycYKnnkT6i/buzey+vHRTR/JpQvuPzzhf1PtKqCDQfJRgoPSN2vscXs6gLigx/zGw==} 505 | engines: {node: ^14.18.0 || >= 16} 506 | peerDependencies: 507 | svelte: ^3.54.0 || ^4.0.0 508 | vite: ^4.0.0 509 | dependencies: 510 | '@sveltejs/vite-plugin-svelte-inspector': 1.0.3(@sveltejs/vite-plugin-svelte@2.4.2)(svelte@4.0.5)(vite@4.4.2) 511 | debug: 4.3.4 512 | deepmerge: 4.3.1 513 | kleur: 4.1.5 514 | magic-string: 0.30.1 515 | svelte: 4.0.5 516 | svelte-hmr: 0.15.2(svelte@4.0.5) 517 | vite: 4.4.2(@types/node@20.4.1)(sass@1.63.6) 518 | vitefu: 0.2.4(vite@4.4.2) 519 | transitivePeerDependencies: 520 | - supports-color 521 | dev: true 522 | 523 | /@types/chai-subset@1.3.3: 524 | resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} 525 | dependencies: 526 | '@types/chai': 4.3.5 527 | dev: true 528 | 529 | /@types/chai@4.3.5: 530 | resolution: {integrity: sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==} 531 | dev: true 532 | 533 | /@types/cookie@0.5.1: 534 | resolution: {integrity: sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==} 535 | dev: true 536 | 537 | /@types/estree@1.0.1: 538 | resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==} 539 | dev: true 540 | 541 | /@types/istanbul-lib-coverage@2.0.4: 542 | resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} 543 | dev: true 544 | 545 | /@types/json-schema@7.0.12: 546 | resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==} 547 | dev: true 548 | 549 | /@types/node@20.4.1: 550 | resolution: {integrity: sha512-JIzsAvJeA/5iY6Y/OxZbv1lUcc8dNSE77lb2gnBH+/PJ3lFR1Ccvgwl5JWnHAkNHcRsT0TbpVOsiMKZ1F/yyJg==} 551 | dev: true 552 | 553 | /@types/pug@2.0.6: 554 | resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==} 555 | dev: true 556 | 557 | /@types/semver@7.5.0: 558 | resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==} 559 | dev: true 560 | 561 | /@typescript-eslint/eslint-plugin@5.61.0(@typescript-eslint/parser@5.61.0)(eslint@8.44.0)(typescript@5.1.6): 562 | resolution: {integrity: sha512-A5l/eUAug103qtkwccSCxn8ZRwT+7RXWkFECdA4Cvl1dOlDUgTpAOfSEElZn2uSUxhdDpnCdetrf0jvU4qrL+g==} 563 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 564 | peerDependencies: 565 | '@typescript-eslint/parser': ^5.0.0 566 | eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 567 | typescript: '*' 568 | peerDependenciesMeta: 569 | typescript: 570 | optional: true 571 | dependencies: 572 | '@eslint-community/regexpp': 4.5.1 573 | '@typescript-eslint/parser': 5.61.0(eslint@8.44.0)(typescript@5.1.6) 574 | '@typescript-eslint/scope-manager': 5.61.0 575 | '@typescript-eslint/type-utils': 5.61.0(eslint@8.44.0)(typescript@5.1.6) 576 | '@typescript-eslint/utils': 5.61.0(eslint@8.44.0)(typescript@5.1.6) 577 | debug: 4.3.4 578 | eslint: 8.44.0 579 | graphemer: 1.4.0 580 | ignore: 5.2.4 581 | natural-compare-lite: 1.4.0 582 | semver: 7.5.4 583 | tsutils: 3.21.0(typescript@5.1.6) 584 | typescript: 5.1.6 585 | transitivePeerDependencies: 586 | - supports-color 587 | dev: true 588 | 589 | /@typescript-eslint/parser@5.61.0(eslint@8.44.0)(typescript@5.1.6): 590 | resolution: {integrity: sha512-yGr4Sgyh8uO6fSi9hw3jAFXNBHbCtKKFMdX2IkT3ZqpKmtAq3lHS4ixB/COFuAIJpwl9/AqF7j72ZDWYKmIfvg==} 591 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 592 | peerDependencies: 593 | eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 594 | typescript: '*' 595 | peerDependenciesMeta: 596 | typescript: 597 | optional: true 598 | dependencies: 599 | '@typescript-eslint/scope-manager': 5.61.0 600 | '@typescript-eslint/types': 5.61.0 601 | '@typescript-eslint/typescript-estree': 5.61.0(typescript@5.1.6) 602 | debug: 4.3.4 603 | eslint: 8.44.0 604 | typescript: 5.1.6 605 | transitivePeerDependencies: 606 | - supports-color 607 | dev: true 608 | 609 | /@typescript-eslint/scope-manager@5.61.0: 610 | resolution: {integrity: sha512-W8VoMjoSg7f7nqAROEmTt6LoBpn81AegP7uKhhW5KzYlehs8VV0ZW0fIDVbcZRcaP3aPSW+JZFua+ysQN+m/Nw==} 611 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 612 | dependencies: 613 | '@typescript-eslint/types': 5.61.0 614 | '@typescript-eslint/visitor-keys': 5.61.0 615 | dev: true 616 | 617 | /@typescript-eslint/type-utils@5.61.0(eslint@8.44.0)(typescript@5.1.6): 618 | resolution: {integrity: sha512-kk8u//r+oVK2Aj3ph/26XdH0pbAkC2RiSjUYhKD+PExemG4XSjpGFeyZ/QM8lBOa7O8aGOU+/yEbMJgQv/DnCg==} 619 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 620 | peerDependencies: 621 | eslint: '*' 622 | typescript: '*' 623 | peerDependenciesMeta: 624 | typescript: 625 | optional: true 626 | dependencies: 627 | '@typescript-eslint/typescript-estree': 5.61.0(typescript@5.1.6) 628 | '@typescript-eslint/utils': 5.61.0(eslint@8.44.0)(typescript@5.1.6) 629 | debug: 4.3.4 630 | eslint: 8.44.0 631 | tsutils: 3.21.0(typescript@5.1.6) 632 | typescript: 5.1.6 633 | transitivePeerDependencies: 634 | - supports-color 635 | dev: true 636 | 637 | /@typescript-eslint/types@5.61.0: 638 | resolution: {integrity: sha512-ldyueo58KjngXpzloHUog/h9REmHl59G1b3a5Sng1GfBo14BkS3ZbMEb3693gnP1k//97lh7bKsp6/V/0v1veQ==} 639 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 640 | dev: true 641 | 642 | /@typescript-eslint/typescript-estree@5.61.0(typescript@5.1.6): 643 | resolution: {integrity: sha512-Fud90PxONnnLZ36oR5ClJBLTLfU4pIWBmnvGwTbEa2cXIqj70AEDEmOmpkFComjBZ/037ueKrOdHuYmSFVD7Rw==} 644 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 645 | peerDependencies: 646 | typescript: '*' 647 | peerDependenciesMeta: 648 | typescript: 649 | optional: true 650 | dependencies: 651 | '@typescript-eslint/types': 5.61.0 652 | '@typescript-eslint/visitor-keys': 5.61.0 653 | debug: 4.3.4 654 | globby: 11.1.0 655 | is-glob: 4.0.3 656 | semver: 7.5.4 657 | tsutils: 3.21.0(typescript@5.1.6) 658 | typescript: 5.1.6 659 | transitivePeerDependencies: 660 | - supports-color 661 | dev: true 662 | 663 | /@typescript-eslint/utils@5.61.0(eslint@8.44.0)(typescript@5.1.6): 664 | resolution: {integrity: sha512-mV6O+6VgQmVE6+xzlA91xifndPW9ElFW8vbSF0xCT/czPXVhwDewKila1jOyRwa9AE19zKnrr7Cg5S3pJVrTWQ==} 665 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 666 | peerDependencies: 667 | eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 668 | dependencies: 669 | '@eslint-community/eslint-utils': 4.4.0(eslint@8.44.0) 670 | '@types/json-schema': 7.0.12 671 | '@types/semver': 7.5.0 672 | '@typescript-eslint/scope-manager': 5.61.0 673 | '@typescript-eslint/types': 5.61.0 674 | '@typescript-eslint/typescript-estree': 5.61.0(typescript@5.1.6) 675 | eslint: 8.44.0 676 | eslint-scope: 5.1.1 677 | semver: 7.5.4 678 | transitivePeerDependencies: 679 | - supports-color 680 | - typescript 681 | dev: true 682 | 683 | /@typescript-eslint/visitor-keys@5.61.0: 684 | resolution: {integrity: sha512-50XQ5VdbWrX06mQXhy93WywSFZZGsv3EOjq+lqp6WC2t+j3mb6A9xYVdrRxafvK88vg9k9u+CT4l6D8PEatjKg==} 685 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 686 | dependencies: 687 | '@typescript-eslint/types': 5.61.0 688 | eslint-visitor-keys: 3.4.1 689 | dev: true 690 | 691 | /@vitest/coverage-v8@0.33.0(vitest@0.32.4): 692 | resolution: {integrity: sha512-Rj5IzoLF7FLj6yR7TmqsfRDSeaFki6NAJ/cQexqhbWkHEV2htlVGrmuOde3xzvFsCbLCagf4omhcIaVmfU8Okg==} 693 | peerDependencies: 694 | vitest: '>=0.32.0 <1' 695 | dependencies: 696 | '@ampproject/remapping': 2.2.1 697 | '@bcoe/v8-coverage': 0.2.3 698 | istanbul-lib-coverage: 3.2.0 699 | istanbul-lib-report: 3.0.0 700 | istanbul-lib-source-maps: 4.0.1 701 | istanbul-reports: 3.1.5 702 | magic-string: 0.30.1 703 | picocolors: 1.0.0 704 | std-env: 3.3.3 705 | test-exclude: 6.0.0 706 | v8-to-istanbul: 9.1.0 707 | vitest: 0.32.4(sass@1.63.6) 708 | transitivePeerDependencies: 709 | - supports-color 710 | dev: true 711 | 712 | /@vitest/expect@0.32.4: 713 | resolution: {integrity: sha512-m7EPUqmGIwIeoU763N+ivkFjTzbaBn0n9evsTOcde03ugy2avPs3kZbYmw3DkcH1j5mxhMhdamJkLQ6dM1bk/A==} 714 | dependencies: 715 | '@vitest/spy': 0.32.4 716 | '@vitest/utils': 0.32.4 717 | chai: 4.3.7 718 | dev: true 719 | 720 | /@vitest/runner@0.32.4: 721 | resolution: {integrity: sha512-cHOVCkiRazobgdKLnczmz2oaKK9GJOw6ZyRcaPdssO1ej+wzHVIkWiCiNacb3TTYPdzMddYkCgMjZ4r8C0JFCw==} 722 | dependencies: 723 | '@vitest/utils': 0.32.4 724 | p-limit: 4.0.0 725 | pathe: 1.1.1 726 | dev: true 727 | 728 | /@vitest/snapshot@0.32.4: 729 | resolution: {integrity: sha512-IRpyqn9t14uqsFlVI2d7DFMImGMs1Q9218of40bdQQgMePwVdmix33yMNnebXcTzDU5eiV3eUsoxxH5v0x/IQA==} 730 | dependencies: 731 | magic-string: 0.30.1 732 | pathe: 1.1.1 733 | pretty-format: 29.6.1 734 | dev: true 735 | 736 | /@vitest/spy@0.32.4: 737 | resolution: {integrity: sha512-oA7rCOqVOOpE6rEoXuCOADX7Lla1LIa4hljI2MSccbpec54q+oifhziZIJXxlE/CvI2E+ElhBHzVu0VEvJGQKQ==} 738 | dependencies: 739 | tinyspy: 2.1.1 740 | dev: true 741 | 742 | /@vitest/utils@0.32.4: 743 | resolution: {integrity: sha512-Gwnl8dhd1uJ+HXrYyV0eRqfmk9ek1ASE/LWfTCuWMw+d07ogHqp4hEAV28NiecimK6UY9DpSEPh+pXBA5gtTBg==} 744 | dependencies: 745 | diff-sequences: 29.4.3 746 | loupe: 2.3.6 747 | pretty-format: 29.6.1 748 | dev: true 749 | 750 | /acorn-jsx@5.3.2(acorn@8.10.0): 751 | resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} 752 | peerDependencies: 753 | acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 754 | dependencies: 755 | acorn: 8.10.0 756 | dev: true 757 | 758 | /acorn-walk@8.2.0: 759 | resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} 760 | engines: {node: '>=0.4.0'} 761 | dev: true 762 | 763 | /acorn@8.10.0: 764 | resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} 765 | engines: {node: '>=0.4.0'} 766 | hasBin: true 767 | dev: true 768 | 769 | /ajv@6.12.6: 770 | resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} 771 | dependencies: 772 | fast-deep-equal: 3.1.3 773 | fast-json-stable-stringify: 2.1.0 774 | json-schema-traverse: 0.4.1 775 | uri-js: 4.4.1 776 | dev: true 777 | 778 | /ansi-regex@5.0.1: 779 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 780 | engines: {node: '>=8'} 781 | dev: true 782 | 783 | /ansi-styles@3.2.1: 784 | resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} 785 | engines: {node: '>=4'} 786 | dependencies: 787 | color-convert: 1.9.3 788 | dev: true 789 | 790 | /ansi-styles@4.3.0: 791 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 792 | engines: {node: '>=8'} 793 | dependencies: 794 | color-convert: 2.0.1 795 | dev: true 796 | 797 | /ansi-styles@5.2.0: 798 | resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} 799 | engines: {node: '>=10'} 800 | dev: true 801 | 802 | /anymatch@3.1.3: 803 | resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} 804 | engines: {node: '>= 8'} 805 | dependencies: 806 | normalize-path: 3.0.0 807 | picomatch: 2.3.1 808 | dev: true 809 | 810 | /argparse@2.0.1: 811 | resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 812 | dev: true 813 | 814 | /aria-query@5.3.0: 815 | resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} 816 | dependencies: 817 | dequal: 2.0.3 818 | dev: true 819 | 820 | /array-buffer-byte-length@1.0.0: 821 | resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} 822 | dependencies: 823 | call-bind: 1.0.2 824 | is-array-buffer: 3.0.2 825 | dev: true 826 | 827 | /array-union@2.1.0: 828 | resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} 829 | engines: {node: '>=8'} 830 | dev: true 831 | 832 | /assertion-error@1.1.0: 833 | resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} 834 | dev: true 835 | 836 | /available-typed-arrays@1.0.5: 837 | resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} 838 | engines: {node: '>= 0.4'} 839 | dev: true 840 | 841 | /axobject-query@3.2.1: 842 | resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==} 843 | dependencies: 844 | dequal: 2.0.3 845 | dev: true 846 | 847 | /balanced-match@1.0.2: 848 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 849 | dev: true 850 | 851 | /binary-extensions@2.2.0: 852 | resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} 853 | engines: {node: '>=8'} 854 | dev: true 855 | 856 | /brace-expansion@1.1.11: 857 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 858 | dependencies: 859 | balanced-match: 1.0.2 860 | concat-map: 0.0.1 861 | dev: true 862 | 863 | /brace-expansion@2.0.1: 864 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 865 | dependencies: 866 | balanced-match: 1.0.2 867 | dev: true 868 | 869 | /braces@3.0.2: 870 | resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} 871 | engines: {node: '>=8'} 872 | dependencies: 873 | fill-range: 7.0.1 874 | dev: true 875 | 876 | /buffer-crc32@0.2.13: 877 | resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} 878 | dev: true 879 | 880 | /busboy@1.6.0: 881 | resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} 882 | engines: {node: '>=10.16.0'} 883 | dependencies: 884 | streamsearch: 1.1.0 885 | dev: true 886 | 887 | /cac@6.7.14: 888 | resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} 889 | engines: {node: '>=8'} 890 | dev: true 891 | 892 | /call-bind@1.0.2: 893 | resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} 894 | dependencies: 895 | function-bind: 1.1.1 896 | get-intrinsic: 1.2.1 897 | dev: true 898 | 899 | /callsites@3.1.0: 900 | resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} 901 | engines: {node: '>=6'} 902 | dev: true 903 | 904 | /chai@4.3.7: 905 | resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==} 906 | engines: {node: '>=4'} 907 | dependencies: 908 | assertion-error: 1.1.0 909 | check-error: 1.0.2 910 | deep-eql: 4.1.3 911 | get-func-name: 2.0.0 912 | loupe: 2.3.6 913 | pathval: 1.1.1 914 | type-detect: 4.0.8 915 | dev: true 916 | 917 | /chalk@2.4.2: 918 | resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} 919 | engines: {node: '>=4'} 920 | dependencies: 921 | ansi-styles: 3.2.1 922 | escape-string-regexp: 1.0.5 923 | supports-color: 5.5.0 924 | dev: true 925 | 926 | /chalk@4.1.2: 927 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 928 | engines: {node: '>=10'} 929 | dependencies: 930 | ansi-styles: 4.3.0 931 | supports-color: 7.2.0 932 | dev: true 933 | 934 | /check-error@1.0.2: 935 | resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} 936 | dev: true 937 | 938 | /chokidar@3.5.3: 939 | resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} 940 | engines: {node: '>= 8.10.0'} 941 | dependencies: 942 | anymatch: 3.1.3 943 | braces: 3.0.2 944 | glob-parent: 5.1.2 945 | is-binary-path: 2.1.0 946 | is-glob: 4.0.3 947 | normalize-path: 3.0.0 948 | readdirp: 3.6.0 949 | optionalDependencies: 950 | fsevents: 2.3.2 951 | dev: true 952 | 953 | /code-red@1.0.3: 954 | resolution: {integrity: sha512-kVwJELqiILQyG5aeuyKFbdsI1fmQy1Cmf7dQ8eGmVuJoaRVdwey7WaMknr2ZFeVSYSKT0rExsa8EGw0aoI/1QQ==} 955 | dependencies: 956 | '@jridgewell/sourcemap-codec': 1.4.15 957 | '@types/estree': 1.0.1 958 | acorn: 8.10.0 959 | estree-walker: 3.0.3 960 | periscopic: 3.1.0 961 | dev: true 962 | 963 | /color-convert@1.9.3: 964 | resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} 965 | dependencies: 966 | color-name: 1.1.3 967 | dev: true 968 | 969 | /color-convert@2.0.1: 970 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 971 | engines: {node: '>=7.0.0'} 972 | dependencies: 973 | color-name: 1.1.4 974 | dev: true 975 | 976 | /color-name@1.1.3: 977 | resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} 978 | dev: true 979 | 980 | /color-name@1.1.4: 981 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 982 | dev: true 983 | 984 | /concat-map@0.0.1: 985 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 986 | dev: true 987 | 988 | /convert-source-map@1.9.0: 989 | resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} 990 | dev: true 991 | 992 | /cookie@0.5.0: 993 | resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} 994 | engines: {node: '>= 0.6'} 995 | dev: true 996 | 997 | /cross-spawn@6.0.5: 998 | resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} 999 | engines: {node: '>=4.8'} 1000 | dependencies: 1001 | nice-try: 1.0.5 1002 | path-key: 2.0.1 1003 | semver: 5.7.1 1004 | shebang-command: 1.2.0 1005 | which: 1.3.1 1006 | dev: true 1007 | 1008 | /cross-spawn@7.0.3: 1009 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} 1010 | engines: {node: '>= 8'} 1011 | dependencies: 1012 | path-key: 3.1.1 1013 | shebang-command: 2.0.0 1014 | which: 2.0.2 1015 | dev: true 1016 | 1017 | /css-tree@2.3.1: 1018 | resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} 1019 | engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} 1020 | dependencies: 1021 | mdn-data: 2.0.30 1022 | source-map-js: 1.0.2 1023 | dev: true 1024 | 1025 | /cssesc@3.0.0: 1026 | resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} 1027 | engines: {node: '>=4'} 1028 | hasBin: true 1029 | dev: true 1030 | 1031 | /debug@4.3.4: 1032 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} 1033 | engines: {node: '>=6.0'} 1034 | peerDependencies: 1035 | supports-color: '*' 1036 | peerDependenciesMeta: 1037 | supports-color: 1038 | optional: true 1039 | dependencies: 1040 | ms: 2.1.2 1041 | dev: true 1042 | 1043 | /dedent-js@1.0.1: 1044 | resolution: {integrity: sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==} 1045 | dev: true 1046 | 1047 | /deep-eql@4.1.3: 1048 | resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} 1049 | engines: {node: '>=6'} 1050 | dependencies: 1051 | type-detect: 4.0.8 1052 | dev: true 1053 | 1054 | /deep-is@0.1.4: 1055 | resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} 1056 | dev: true 1057 | 1058 | /deepmerge@4.3.1: 1059 | resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} 1060 | engines: {node: '>=0.10.0'} 1061 | dev: true 1062 | 1063 | /define-properties@1.2.0: 1064 | resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} 1065 | engines: {node: '>= 0.4'} 1066 | dependencies: 1067 | has-property-descriptors: 1.0.0 1068 | object-keys: 1.1.1 1069 | dev: true 1070 | 1071 | /dequal@2.0.3: 1072 | resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} 1073 | engines: {node: '>=6'} 1074 | dev: true 1075 | 1076 | /detect-indent@6.1.0: 1077 | resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} 1078 | engines: {node: '>=8'} 1079 | dev: true 1080 | 1081 | /devalue@4.3.2: 1082 | resolution: {integrity: sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==} 1083 | dev: true 1084 | 1085 | /diff-sequences@29.4.3: 1086 | resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==} 1087 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 1088 | dev: true 1089 | 1090 | /dir-glob@3.0.1: 1091 | resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} 1092 | engines: {node: '>=8'} 1093 | dependencies: 1094 | path-type: 4.0.0 1095 | dev: true 1096 | 1097 | /doctrine@3.0.0: 1098 | resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} 1099 | engines: {node: '>=6.0.0'} 1100 | dependencies: 1101 | esutils: 2.0.3 1102 | dev: true 1103 | 1104 | /error-ex@1.3.2: 1105 | resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} 1106 | dependencies: 1107 | is-arrayish: 0.2.1 1108 | dev: true 1109 | 1110 | /es-abstract@1.21.2: 1111 | resolution: {integrity: sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==} 1112 | engines: {node: '>= 0.4'} 1113 | dependencies: 1114 | array-buffer-byte-length: 1.0.0 1115 | available-typed-arrays: 1.0.5 1116 | call-bind: 1.0.2 1117 | es-set-tostringtag: 2.0.1 1118 | es-to-primitive: 1.2.1 1119 | function.prototype.name: 1.1.5 1120 | get-intrinsic: 1.2.1 1121 | get-symbol-description: 1.0.0 1122 | globalthis: 1.0.3 1123 | gopd: 1.0.1 1124 | has: 1.0.3 1125 | has-property-descriptors: 1.0.0 1126 | has-proto: 1.0.1 1127 | has-symbols: 1.0.3 1128 | internal-slot: 1.0.5 1129 | is-array-buffer: 3.0.2 1130 | is-callable: 1.2.7 1131 | is-negative-zero: 2.0.2 1132 | is-regex: 1.1.4 1133 | is-shared-array-buffer: 1.0.2 1134 | is-string: 1.0.7 1135 | is-typed-array: 1.1.10 1136 | is-weakref: 1.0.2 1137 | object-inspect: 1.12.3 1138 | object-keys: 1.1.1 1139 | object.assign: 4.1.4 1140 | regexp.prototype.flags: 1.5.0 1141 | safe-regex-test: 1.0.0 1142 | string.prototype.trim: 1.2.7 1143 | string.prototype.trimend: 1.0.6 1144 | string.prototype.trimstart: 1.0.6 1145 | typed-array-length: 1.0.4 1146 | unbox-primitive: 1.0.2 1147 | which-typed-array: 1.1.9 1148 | dev: true 1149 | 1150 | /es-set-tostringtag@2.0.1: 1151 | resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} 1152 | engines: {node: '>= 0.4'} 1153 | dependencies: 1154 | get-intrinsic: 1.2.1 1155 | has: 1.0.3 1156 | has-tostringtag: 1.0.0 1157 | dev: true 1158 | 1159 | /es-to-primitive@1.2.1: 1160 | resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} 1161 | engines: {node: '>= 0.4'} 1162 | dependencies: 1163 | is-callable: 1.2.7 1164 | is-date-object: 1.0.5 1165 | is-symbol: 1.0.4 1166 | dev: true 1167 | 1168 | /es6-promise@3.3.1: 1169 | resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} 1170 | dev: true 1171 | 1172 | /esbuild@0.18.11: 1173 | resolution: {integrity: sha512-i8u6mQF0JKJUlGR3OdFLKldJQMMs8OqM9Cc3UCi9XXziJ9WERM5bfkHaEAy0YAvPRMgqSW55W7xYn84XtEFTtA==} 1174 | engines: {node: '>=12'} 1175 | hasBin: true 1176 | requiresBuild: true 1177 | optionalDependencies: 1178 | '@esbuild/android-arm': 0.18.11 1179 | '@esbuild/android-arm64': 0.18.11 1180 | '@esbuild/android-x64': 0.18.11 1181 | '@esbuild/darwin-arm64': 0.18.11 1182 | '@esbuild/darwin-x64': 0.18.11 1183 | '@esbuild/freebsd-arm64': 0.18.11 1184 | '@esbuild/freebsd-x64': 0.18.11 1185 | '@esbuild/linux-arm': 0.18.11 1186 | '@esbuild/linux-arm64': 0.18.11 1187 | '@esbuild/linux-ia32': 0.18.11 1188 | '@esbuild/linux-loong64': 0.18.11 1189 | '@esbuild/linux-mips64el': 0.18.11 1190 | '@esbuild/linux-ppc64': 0.18.11 1191 | '@esbuild/linux-riscv64': 0.18.11 1192 | '@esbuild/linux-s390x': 0.18.11 1193 | '@esbuild/linux-x64': 0.18.11 1194 | '@esbuild/netbsd-x64': 0.18.11 1195 | '@esbuild/openbsd-x64': 0.18.11 1196 | '@esbuild/sunos-x64': 0.18.11 1197 | '@esbuild/win32-arm64': 0.18.11 1198 | '@esbuild/win32-ia32': 0.18.11 1199 | '@esbuild/win32-x64': 0.18.11 1200 | dev: true 1201 | 1202 | /escape-string-regexp@1.0.5: 1203 | resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} 1204 | engines: {node: '>=0.8.0'} 1205 | dev: true 1206 | 1207 | /escape-string-regexp@4.0.0: 1208 | resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 1209 | engines: {node: '>=10'} 1210 | dev: true 1211 | 1212 | /eslint-config-prettier@8.8.0(eslint@8.44.0): 1213 | resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==} 1214 | hasBin: true 1215 | peerDependencies: 1216 | eslint: '>=7.0.0' 1217 | dependencies: 1218 | eslint: 8.44.0 1219 | dev: true 1220 | 1221 | /eslint-plugin-svelte@2.32.2(eslint@8.44.0)(svelte@4.0.5): 1222 | resolution: {integrity: sha512-Jgbop2fNZsoxxkklZAIbDNhwAPynvnCtUXLsEC6O2qax7N/pfe2cNqT0ZoBbubXKJitQQDEyVDQ1rZs4ZWcrTA==} 1223 | engines: {node: ^14.17.0 || >=16.0.0} 1224 | peerDependencies: 1225 | eslint: ^7.0.0 || ^8.0.0-0 1226 | svelte: ^3.37.0 || ^4.0.0 1227 | peerDependenciesMeta: 1228 | svelte: 1229 | optional: true 1230 | dependencies: 1231 | '@eslint-community/eslint-utils': 4.4.0(eslint@8.44.0) 1232 | '@jridgewell/sourcemap-codec': 1.4.15 1233 | debug: 4.3.4 1234 | eslint: 8.44.0 1235 | esutils: 2.0.3 1236 | known-css-properties: 0.27.0 1237 | postcss: 8.4.25 1238 | postcss-load-config: 3.1.4(postcss@8.4.25) 1239 | postcss-safe-parser: 6.0.0(postcss@8.4.25) 1240 | postcss-selector-parser: 6.0.13 1241 | semver: 7.5.4 1242 | svelte: 4.0.5 1243 | svelte-eslint-parser: 0.32.1(svelte@4.0.5) 1244 | transitivePeerDependencies: 1245 | - supports-color 1246 | - ts-node 1247 | dev: true 1248 | 1249 | /eslint-scope@5.1.1: 1250 | resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} 1251 | engines: {node: '>=8.0.0'} 1252 | dependencies: 1253 | esrecurse: 4.3.0 1254 | estraverse: 4.3.0 1255 | dev: true 1256 | 1257 | /eslint-scope@7.2.0: 1258 | resolution: {integrity: sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==} 1259 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 1260 | dependencies: 1261 | esrecurse: 4.3.0 1262 | estraverse: 5.3.0 1263 | dev: true 1264 | 1265 | /eslint-visitor-keys@3.4.1: 1266 | resolution: {integrity: sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==} 1267 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 1268 | dev: true 1269 | 1270 | /eslint@8.44.0: 1271 | resolution: {integrity: sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==} 1272 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 1273 | hasBin: true 1274 | dependencies: 1275 | '@eslint-community/eslint-utils': 4.4.0(eslint@8.44.0) 1276 | '@eslint-community/regexpp': 4.5.1 1277 | '@eslint/eslintrc': 2.1.0 1278 | '@eslint/js': 8.44.0 1279 | '@humanwhocodes/config-array': 0.11.10 1280 | '@humanwhocodes/module-importer': 1.0.1 1281 | '@nodelib/fs.walk': 1.2.8 1282 | ajv: 6.12.6 1283 | chalk: 4.1.2 1284 | cross-spawn: 7.0.3 1285 | debug: 4.3.4 1286 | doctrine: 3.0.0 1287 | escape-string-regexp: 4.0.0 1288 | eslint-scope: 7.2.0 1289 | eslint-visitor-keys: 3.4.1 1290 | espree: 9.6.0 1291 | esquery: 1.5.0 1292 | esutils: 2.0.3 1293 | fast-deep-equal: 3.1.3 1294 | file-entry-cache: 6.0.1 1295 | find-up: 5.0.0 1296 | glob-parent: 6.0.2 1297 | globals: 13.20.0 1298 | graphemer: 1.4.0 1299 | ignore: 5.2.4 1300 | import-fresh: 3.3.0 1301 | imurmurhash: 0.1.4 1302 | is-glob: 4.0.3 1303 | is-path-inside: 3.0.3 1304 | js-yaml: 4.1.0 1305 | json-stable-stringify-without-jsonify: 1.0.1 1306 | levn: 0.4.1 1307 | lodash.merge: 4.6.2 1308 | minimatch: 3.1.2 1309 | natural-compare: 1.4.0 1310 | optionator: 0.9.3 1311 | strip-ansi: 6.0.1 1312 | strip-json-comments: 3.1.1 1313 | text-table: 0.2.0 1314 | transitivePeerDependencies: 1315 | - supports-color 1316 | dev: true 1317 | 1318 | /esm-env@1.0.0: 1319 | resolution: {integrity: sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==} 1320 | dev: true 1321 | 1322 | /espree@9.6.0: 1323 | resolution: {integrity: sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==} 1324 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 1325 | dependencies: 1326 | acorn: 8.10.0 1327 | acorn-jsx: 5.3.2(acorn@8.10.0) 1328 | eslint-visitor-keys: 3.4.1 1329 | dev: true 1330 | 1331 | /esquery@1.5.0: 1332 | resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} 1333 | engines: {node: '>=0.10'} 1334 | dependencies: 1335 | estraverse: 5.3.0 1336 | dev: true 1337 | 1338 | /esrecurse@4.3.0: 1339 | resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} 1340 | engines: {node: '>=4.0'} 1341 | dependencies: 1342 | estraverse: 5.3.0 1343 | dev: true 1344 | 1345 | /estraverse@4.3.0: 1346 | resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} 1347 | engines: {node: '>=4.0'} 1348 | dev: true 1349 | 1350 | /estraverse@5.3.0: 1351 | resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} 1352 | engines: {node: '>=4.0'} 1353 | dev: true 1354 | 1355 | /estree-walker@3.0.3: 1356 | resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} 1357 | dependencies: 1358 | '@types/estree': 1.0.1 1359 | dev: true 1360 | 1361 | /esutils@2.0.3: 1362 | resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} 1363 | engines: {node: '>=0.10.0'} 1364 | dev: true 1365 | 1366 | /fast-deep-equal@3.1.3: 1367 | resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 1368 | dev: true 1369 | 1370 | /fast-glob@3.3.0: 1371 | resolution: {integrity: sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==} 1372 | engines: {node: '>=8.6.0'} 1373 | dependencies: 1374 | '@nodelib/fs.stat': 2.0.5 1375 | '@nodelib/fs.walk': 1.2.8 1376 | glob-parent: 5.1.2 1377 | merge2: 1.4.1 1378 | micromatch: 4.0.5 1379 | dev: true 1380 | 1381 | /fast-json-stable-stringify@2.1.0: 1382 | resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} 1383 | dev: true 1384 | 1385 | /fast-levenshtein@2.0.6: 1386 | resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} 1387 | dev: true 1388 | 1389 | /fastq@1.15.0: 1390 | resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} 1391 | dependencies: 1392 | reusify: 1.0.4 1393 | dev: true 1394 | 1395 | /file-entry-cache@6.0.1: 1396 | resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} 1397 | engines: {node: ^10.12.0 || >=12.0.0} 1398 | dependencies: 1399 | flat-cache: 3.0.4 1400 | dev: true 1401 | 1402 | /fill-range@7.0.1: 1403 | resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} 1404 | engines: {node: '>=8'} 1405 | dependencies: 1406 | to-regex-range: 5.0.1 1407 | dev: true 1408 | 1409 | /find-up@5.0.0: 1410 | resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 1411 | engines: {node: '>=10'} 1412 | dependencies: 1413 | locate-path: 6.0.0 1414 | path-exists: 4.0.0 1415 | dev: true 1416 | 1417 | /flat-cache@3.0.4: 1418 | resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} 1419 | engines: {node: ^10.12.0 || >=12.0.0} 1420 | dependencies: 1421 | flatted: 3.2.7 1422 | rimraf: 3.0.2 1423 | dev: true 1424 | 1425 | /flatted@3.2.7: 1426 | resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} 1427 | dev: true 1428 | 1429 | /for-each@0.3.3: 1430 | resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} 1431 | dependencies: 1432 | is-callable: 1.2.7 1433 | dev: true 1434 | 1435 | /fs.realpath@1.0.0: 1436 | resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} 1437 | dev: true 1438 | 1439 | /fsevents@2.3.2: 1440 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} 1441 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 1442 | os: [darwin] 1443 | requiresBuild: true 1444 | dev: true 1445 | optional: true 1446 | 1447 | /function-bind@1.1.1: 1448 | resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} 1449 | dev: true 1450 | 1451 | /function.prototype.name@1.1.5: 1452 | resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} 1453 | engines: {node: '>= 0.4'} 1454 | dependencies: 1455 | call-bind: 1.0.2 1456 | define-properties: 1.2.0 1457 | es-abstract: 1.21.2 1458 | functions-have-names: 1.2.3 1459 | dev: true 1460 | 1461 | /functions-have-names@1.2.3: 1462 | resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} 1463 | dev: true 1464 | 1465 | /get-func-name@2.0.0: 1466 | resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} 1467 | dev: true 1468 | 1469 | /get-intrinsic@1.2.1: 1470 | resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} 1471 | dependencies: 1472 | function-bind: 1.1.1 1473 | has: 1.0.3 1474 | has-proto: 1.0.1 1475 | has-symbols: 1.0.3 1476 | dev: true 1477 | 1478 | /get-symbol-description@1.0.0: 1479 | resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} 1480 | engines: {node: '>= 0.4'} 1481 | dependencies: 1482 | call-bind: 1.0.2 1483 | get-intrinsic: 1.2.1 1484 | dev: true 1485 | 1486 | /glob-parent@5.1.2: 1487 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 1488 | engines: {node: '>= 6'} 1489 | dependencies: 1490 | is-glob: 4.0.3 1491 | dev: true 1492 | 1493 | /glob-parent@6.0.2: 1494 | resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 1495 | engines: {node: '>=10.13.0'} 1496 | dependencies: 1497 | is-glob: 4.0.3 1498 | dev: true 1499 | 1500 | /glob@7.2.3: 1501 | resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} 1502 | dependencies: 1503 | fs.realpath: 1.0.0 1504 | inflight: 1.0.6 1505 | inherits: 2.0.4 1506 | minimatch: 3.1.2 1507 | once: 1.4.0 1508 | path-is-absolute: 1.0.1 1509 | dev: true 1510 | 1511 | /glob@8.1.0: 1512 | resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} 1513 | engines: {node: '>=12'} 1514 | dependencies: 1515 | fs.realpath: 1.0.0 1516 | inflight: 1.0.6 1517 | inherits: 2.0.4 1518 | minimatch: 5.1.6 1519 | once: 1.4.0 1520 | dev: true 1521 | 1522 | /globals@13.20.0: 1523 | resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==} 1524 | engines: {node: '>=8'} 1525 | dependencies: 1526 | type-fest: 0.20.2 1527 | dev: true 1528 | 1529 | /globalthis@1.0.3: 1530 | resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} 1531 | engines: {node: '>= 0.4'} 1532 | dependencies: 1533 | define-properties: 1.2.0 1534 | dev: true 1535 | 1536 | /globby@11.1.0: 1537 | resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} 1538 | engines: {node: '>=10'} 1539 | dependencies: 1540 | array-union: 2.1.0 1541 | dir-glob: 3.0.1 1542 | fast-glob: 3.3.0 1543 | ignore: 5.2.4 1544 | merge2: 1.4.1 1545 | slash: 3.0.0 1546 | dev: true 1547 | 1548 | /gopd@1.0.1: 1549 | resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} 1550 | dependencies: 1551 | get-intrinsic: 1.2.1 1552 | dev: true 1553 | 1554 | /graceful-fs@4.2.11: 1555 | resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} 1556 | dev: true 1557 | 1558 | /graphemer@1.4.0: 1559 | resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} 1560 | dev: true 1561 | 1562 | /has-bigints@1.0.2: 1563 | resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} 1564 | dev: true 1565 | 1566 | /has-flag@3.0.0: 1567 | resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} 1568 | engines: {node: '>=4'} 1569 | dev: true 1570 | 1571 | /has-flag@4.0.0: 1572 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 1573 | engines: {node: '>=8'} 1574 | dev: true 1575 | 1576 | /has-property-descriptors@1.0.0: 1577 | resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} 1578 | dependencies: 1579 | get-intrinsic: 1.2.1 1580 | dev: true 1581 | 1582 | /has-proto@1.0.1: 1583 | resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} 1584 | engines: {node: '>= 0.4'} 1585 | dev: true 1586 | 1587 | /has-symbols@1.0.3: 1588 | resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} 1589 | engines: {node: '>= 0.4'} 1590 | dev: true 1591 | 1592 | /has-tostringtag@1.0.0: 1593 | resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} 1594 | engines: {node: '>= 0.4'} 1595 | dependencies: 1596 | has-symbols: 1.0.3 1597 | dev: true 1598 | 1599 | /has@1.0.3: 1600 | resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} 1601 | engines: {node: '>= 0.4.0'} 1602 | dependencies: 1603 | function-bind: 1.1.1 1604 | dev: true 1605 | 1606 | /hosted-git-info@2.8.9: 1607 | resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} 1608 | dev: true 1609 | 1610 | /html-escaper@2.0.2: 1611 | resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} 1612 | dev: true 1613 | 1614 | /ignore-walk@5.0.1: 1615 | resolution: {integrity: sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==} 1616 | engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} 1617 | dependencies: 1618 | minimatch: 5.1.6 1619 | dev: true 1620 | 1621 | /ignore@5.2.4: 1622 | resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} 1623 | engines: {node: '>= 4'} 1624 | dev: true 1625 | 1626 | /immutable@4.3.0: 1627 | resolution: {integrity: sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==} 1628 | dev: true 1629 | 1630 | /import-fresh@3.3.0: 1631 | resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} 1632 | engines: {node: '>=6'} 1633 | dependencies: 1634 | parent-module: 1.0.1 1635 | resolve-from: 4.0.0 1636 | dev: true 1637 | 1638 | /import-meta-resolve@3.0.0: 1639 | resolution: {integrity: sha512-4IwhLhNNA8yy445rPjD/lWh++7hMDOml2eHtd58eG7h+qK3EryMuuRbsHGPikCoAgIkkDnckKfWSk2iDla/ejg==} 1640 | dev: true 1641 | 1642 | /imurmurhash@0.1.4: 1643 | resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} 1644 | engines: {node: '>=0.8.19'} 1645 | dev: true 1646 | 1647 | /inflight@1.0.6: 1648 | resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} 1649 | dependencies: 1650 | once: 1.4.0 1651 | wrappy: 1.0.2 1652 | dev: true 1653 | 1654 | /inherits@2.0.4: 1655 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 1656 | dev: true 1657 | 1658 | /internal-slot@1.0.5: 1659 | resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} 1660 | engines: {node: '>= 0.4'} 1661 | dependencies: 1662 | get-intrinsic: 1.2.1 1663 | has: 1.0.3 1664 | side-channel: 1.0.4 1665 | dev: true 1666 | 1667 | /is-array-buffer@3.0.2: 1668 | resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} 1669 | dependencies: 1670 | call-bind: 1.0.2 1671 | get-intrinsic: 1.2.1 1672 | is-typed-array: 1.1.10 1673 | dev: true 1674 | 1675 | /is-arrayish@0.2.1: 1676 | resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} 1677 | dev: true 1678 | 1679 | /is-bigint@1.0.4: 1680 | resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} 1681 | dependencies: 1682 | has-bigints: 1.0.2 1683 | dev: true 1684 | 1685 | /is-binary-path@2.1.0: 1686 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} 1687 | engines: {node: '>=8'} 1688 | dependencies: 1689 | binary-extensions: 2.2.0 1690 | dev: true 1691 | 1692 | /is-boolean-object@1.1.2: 1693 | resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} 1694 | engines: {node: '>= 0.4'} 1695 | dependencies: 1696 | call-bind: 1.0.2 1697 | has-tostringtag: 1.0.0 1698 | dev: true 1699 | 1700 | /is-callable@1.2.7: 1701 | resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} 1702 | engines: {node: '>= 0.4'} 1703 | dev: true 1704 | 1705 | /is-core-module@2.12.1: 1706 | resolution: {integrity: sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==} 1707 | dependencies: 1708 | has: 1.0.3 1709 | dev: true 1710 | 1711 | /is-date-object@1.0.5: 1712 | resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} 1713 | engines: {node: '>= 0.4'} 1714 | dependencies: 1715 | has-tostringtag: 1.0.0 1716 | dev: true 1717 | 1718 | /is-extglob@2.1.1: 1719 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 1720 | engines: {node: '>=0.10.0'} 1721 | dev: true 1722 | 1723 | /is-glob@4.0.3: 1724 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 1725 | engines: {node: '>=0.10.0'} 1726 | dependencies: 1727 | is-extglob: 2.1.1 1728 | dev: true 1729 | 1730 | /is-negative-zero@2.0.2: 1731 | resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} 1732 | engines: {node: '>= 0.4'} 1733 | dev: true 1734 | 1735 | /is-number-object@1.0.7: 1736 | resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} 1737 | engines: {node: '>= 0.4'} 1738 | dependencies: 1739 | has-tostringtag: 1.0.0 1740 | dev: true 1741 | 1742 | /is-number@7.0.0: 1743 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 1744 | engines: {node: '>=0.12.0'} 1745 | dev: true 1746 | 1747 | /is-path-inside@3.0.3: 1748 | resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} 1749 | engines: {node: '>=8'} 1750 | dev: true 1751 | 1752 | /is-reference@3.0.1: 1753 | resolution: {integrity: sha512-baJJdQLiYaJdvFbJqXrcGv3WU3QCzBlUcI5QhbesIm6/xPsvmO+2CDoi/GMOFBQEQm+PXkwOPrp9KK5ozZsp2w==} 1754 | dependencies: 1755 | '@types/estree': 1.0.1 1756 | dev: true 1757 | 1758 | /is-regex@1.1.4: 1759 | resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} 1760 | engines: {node: '>= 0.4'} 1761 | dependencies: 1762 | call-bind: 1.0.2 1763 | has-tostringtag: 1.0.0 1764 | dev: true 1765 | 1766 | /is-shared-array-buffer@1.0.2: 1767 | resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} 1768 | dependencies: 1769 | call-bind: 1.0.2 1770 | dev: true 1771 | 1772 | /is-string@1.0.7: 1773 | resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} 1774 | engines: {node: '>= 0.4'} 1775 | dependencies: 1776 | has-tostringtag: 1.0.0 1777 | dev: true 1778 | 1779 | /is-symbol@1.0.4: 1780 | resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} 1781 | engines: {node: '>= 0.4'} 1782 | dependencies: 1783 | has-symbols: 1.0.3 1784 | dev: true 1785 | 1786 | /is-typed-array@1.1.10: 1787 | resolution: {integrity: sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==} 1788 | engines: {node: '>= 0.4'} 1789 | dependencies: 1790 | available-typed-arrays: 1.0.5 1791 | call-bind: 1.0.2 1792 | for-each: 0.3.3 1793 | gopd: 1.0.1 1794 | has-tostringtag: 1.0.0 1795 | dev: true 1796 | 1797 | /is-weakref@1.0.2: 1798 | resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} 1799 | dependencies: 1800 | call-bind: 1.0.2 1801 | dev: true 1802 | 1803 | /isexe@2.0.0: 1804 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 1805 | dev: true 1806 | 1807 | /istanbul-lib-coverage@3.2.0: 1808 | resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} 1809 | engines: {node: '>=8'} 1810 | dev: true 1811 | 1812 | /istanbul-lib-report@3.0.0: 1813 | resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==} 1814 | engines: {node: '>=8'} 1815 | dependencies: 1816 | istanbul-lib-coverage: 3.2.0 1817 | make-dir: 3.1.0 1818 | supports-color: 7.2.0 1819 | dev: true 1820 | 1821 | /istanbul-lib-source-maps@4.0.1: 1822 | resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} 1823 | engines: {node: '>=10'} 1824 | dependencies: 1825 | debug: 4.3.4 1826 | istanbul-lib-coverage: 3.2.0 1827 | source-map: 0.6.1 1828 | transitivePeerDependencies: 1829 | - supports-color 1830 | dev: true 1831 | 1832 | /istanbul-reports@3.1.5: 1833 | resolution: {integrity: sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==} 1834 | engines: {node: '>=8'} 1835 | dependencies: 1836 | html-escaper: 2.0.2 1837 | istanbul-lib-report: 3.0.0 1838 | dev: true 1839 | 1840 | /js-yaml@4.1.0: 1841 | resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} 1842 | hasBin: true 1843 | dependencies: 1844 | argparse: 2.0.1 1845 | dev: true 1846 | 1847 | /json-parse-better-errors@1.0.2: 1848 | resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} 1849 | dev: true 1850 | 1851 | /json-schema-traverse@0.4.1: 1852 | resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} 1853 | dev: true 1854 | 1855 | /json-stable-stringify-without-jsonify@1.0.1: 1856 | resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} 1857 | dev: true 1858 | 1859 | /jsonc-parser@3.2.0: 1860 | resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} 1861 | dev: true 1862 | 1863 | /kleur@4.1.5: 1864 | resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} 1865 | engines: {node: '>=6'} 1866 | dev: true 1867 | 1868 | /known-css-properties@0.27.0: 1869 | resolution: {integrity: sha512-uMCj6+hZYDoffuvAJjFAPz56E9uoowFHmTkqRtRq5WyC5Q6Cu/fTZKNQpX/RbzChBYLLl3lo8CjFZBAZXq9qFg==} 1870 | dev: true 1871 | 1872 | /levn@0.4.1: 1873 | resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} 1874 | engines: {node: '>= 0.8.0'} 1875 | dependencies: 1876 | prelude-ls: 1.2.1 1877 | type-check: 0.4.0 1878 | dev: true 1879 | 1880 | /lilconfig@2.1.0: 1881 | resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} 1882 | engines: {node: '>=10'} 1883 | dev: true 1884 | 1885 | /load-json-file@4.0.0: 1886 | resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} 1887 | engines: {node: '>=4'} 1888 | dependencies: 1889 | graceful-fs: 4.2.11 1890 | parse-json: 4.0.0 1891 | pify: 3.0.0 1892 | strip-bom: 3.0.0 1893 | dev: true 1894 | 1895 | /local-pkg@0.4.3: 1896 | resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} 1897 | engines: {node: '>=14'} 1898 | dev: true 1899 | 1900 | /locate-character@3.0.0: 1901 | resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} 1902 | dev: true 1903 | 1904 | /locate-path@6.0.0: 1905 | resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 1906 | engines: {node: '>=10'} 1907 | dependencies: 1908 | p-locate: 5.0.0 1909 | dev: true 1910 | 1911 | /lodash.merge@4.6.2: 1912 | resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} 1913 | dev: true 1914 | 1915 | /loupe@2.3.6: 1916 | resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} 1917 | dependencies: 1918 | get-func-name: 2.0.0 1919 | dev: true 1920 | 1921 | /lower-case@2.0.2: 1922 | resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} 1923 | dependencies: 1924 | tslib: 2.6.0 1925 | dev: true 1926 | 1927 | /lru-cache@6.0.0: 1928 | resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} 1929 | engines: {node: '>=10'} 1930 | dependencies: 1931 | yallist: 4.0.0 1932 | dev: true 1933 | 1934 | /magic-string@0.27.0: 1935 | resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} 1936 | engines: {node: '>=12'} 1937 | dependencies: 1938 | '@jridgewell/sourcemap-codec': 1.4.15 1939 | dev: true 1940 | 1941 | /magic-string@0.30.1: 1942 | resolution: {integrity: sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==} 1943 | engines: {node: '>=12'} 1944 | dependencies: 1945 | '@jridgewell/sourcemap-codec': 1.4.15 1946 | dev: true 1947 | 1948 | /make-dir@3.1.0: 1949 | resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} 1950 | engines: {node: '>=8'} 1951 | dependencies: 1952 | semver: 6.3.0 1953 | dev: true 1954 | 1955 | /mdn-data@2.0.30: 1956 | resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} 1957 | dev: true 1958 | 1959 | /memorystream@0.3.1: 1960 | resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} 1961 | engines: {node: '>= 0.10.0'} 1962 | dev: true 1963 | 1964 | /merge2@1.4.1: 1965 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 1966 | engines: {node: '>= 8'} 1967 | dev: true 1968 | 1969 | /micromatch@4.0.5: 1970 | resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} 1971 | engines: {node: '>=8.6'} 1972 | dependencies: 1973 | braces: 3.0.2 1974 | picomatch: 2.3.1 1975 | dev: true 1976 | 1977 | /mime@3.0.0: 1978 | resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} 1979 | engines: {node: '>=10.0.0'} 1980 | hasBin: true 1981 | dev: true 1982 | 1983 | /min-indent@1.0.1: 1984 | resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} 1985 | engines: {node: '>=4'} 1986 | dev: true 1987 | 1988 | /minimatch@3.1.2: 1989 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 1990 | dependencies: 1991 | brace-expansion: 1.1.11 1992 | dev: true 1993 | 1994 | /minimatch@5.1.6: 1995 | resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} 1996 | engines: {node: '>=10'} 1997 | dependencies: 1998 | brace-expansion: 2.0.1 1999 | dev: true 2000 | 2001 | /minimist@1.2.8: 2002 | resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} 2003 | dev: true 2004 | 2005 | /mkdirp@0.5.6: 2006 | resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} 2007 | hasBin: true 2008 | dependencies: 2009 | minimist: 1.2.8 2010 | dev: true 2011 | 2012 | /mlly@1.4.0: 2013 | resolution: {integrity: sha512-ua8PAThnTwpprIaU47EPeZ/bPUVp2QYBbWMphUQpVdBI3Lgqzm5KZQ45Agm3YJedHXaIHl6pBGabaLSUPPSptg==} 2014 | dependencies: 2015 | acorn: 8.10.0 2016 | pathe: 1.1.1 2017 | pkg-types: 1.0.3 2018 | ufo: 1.1.2 2019 | dev: true 2020 | 2021 | /mri@1.2.0: 2022 | resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} 2023 | engines: {node: '>=4'} 2024 | dev: true 2025 | 2026 | /mrmime@1.0.1: 2027 | resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} 2028 | engines: {node: '>=10'} 2029 | dev: true 2030 | 2031 | /ms@2.1.2: 2032 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 2033 | dev: true 2034 | 2035 | /nanoid@3.3.6: 2036 | resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} 2037 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 2038 | hasBin: true 2039 | dev: true 2040 | 2041 | /natural-compare-lite@1.4.0: 2042 | resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} 2043 | dev: true 2044 | 2045 | /natural-compare@1.4.0: 2046 | resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} 2047 | dev: true 2048 | 2049 | /nice-try@1.0.5: 2050 | resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} 2051 | dev: true 2052 | 2053 | /no-case@3.0.4: 2054 | resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} 2055 | dependencies: 2056 | lower-case: 2.0.2 2057 | tslib: 2.6.0 2058 | dev: true 2059 | 2060 | /normalize-package-data@2.5.0: 2061 | resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} 2062 | dependencies: 2063 | hosted-git-info: 2.8.9 2064 | resolve: 1.22.2 2065 | semver: 5.7.1 2066 | validate-npm-package-license: 3.0.4 2067 | dev: true 2068 | 2069 | /normalize-path@3.0.0: 2070 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} 2071 | engines: {node: '>=0.10.0'} 2072 | dev: true 2073 | 2074 | /npm-bundled@2.0.1: 2075 | resolution: {integrity: sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==} 2076 | engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} 2077 | dependencies: 2078 | npm-normalize-package-bin: 2.0.0 2079 | dev: true 2080 | 2081 | /npm-normalize-package-bin@2.0.0: 2082 | resolution: {integrity: sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==} 2083 | engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} 2084 | dev: true 2085 | 2086 | /npm-packlist@5.1.3: 2087 | resolution: {integrity: sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==} 2088 | engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} 2089 | hasBin: true 2090 | dependencies: 2091 | glob: 8.1.0 2092 | ignore-walk: 5.0.1 2093 | npm-bundled: 2.0.1 2094 | npm-normalize-package-bin: 2.0.0 2095 | dev: true 2096 | 2097 | /npm-run-all@4.1.5: 2098 | resolution: {integrity: sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==} 2099 | engines: {node: '>= 4'} 2100 | hasBin: true 2101 | dependencies: 2102 | ansi-styles: 3.2.1 2103 | chalk: 2.4.2 2104 | cross-spawn: 6.0.5 2105 | memorystream: 0.3.1 2106 | minimatch: 3.1.2 2107 | pidtree: 0.3.1 2108 | read-pkg: 3.0.0 2109 | shell-quote: 1.8.1 2110 | string.prototype.padend: 3.1.4 2111 | dev: true 2112 | 2113 | /object-inspect@1.12.3: 2114 | resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} 2115 | dev: true 2116 | 2117 | /object-keys@1.1.1: 2118 | resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} 2119 | engines: {node: '>= 0.4'} 2120 | dev: true 2121 | 2122 | /object.assign@4.1.4: 2123 | resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} 2124 | engines: {node: '>= 0.4'} 2125 | dependencies: 2126 | call-bind: 1.0.2 2127 | define-properties: 1.2.0 2128 | has-symbols: 1.0.3 2129 | object-keys: 1.1.1 2130 | dev: true 2131 | 2132 | /once@1.4.0: 2133 | resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} 2134 | dependencies: 2135 | wrappy: 1.0.2 2136 | dev: true 2137 | 2138 | /optionator@0.9.3: 2139 | resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} 2140 | engines: {node: '>= 0.8.0'} 2141 | dependencies: 2142 | '@aashutoshrathi/word-wrap': 1.2.6 2143 | deep-is: 0.1.4 2144 | fast-levenshtein: 2.0.6 2145 | levn: 0.4.1 2146 | prelude-ls: 1.2.1 2147 | type-check: 0.4.0 2148 | dev: true 2149 | 2150 | /p-limit@3.1.0: 2151 | resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 2152 | engines: {node: '>=10'} 2153 | dependencies: 2154 | yocto-queue: 0.1.0 2155 | dev: true 2156 | 2157 | /p-limit@4.0.0: 2158 | resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} 2159 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 2160 | dependencies: 2161 | yocto-queue: 1.0.0 2162 | dev: true 2163 | 2164 | /p-locate@5.0.0: 2165 | resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} 2166 | engines: {node: '>=10'} 2167 | dependencies: 2168 | p-limit: 3.1.0 2169 | dev: true 2170 | 2171 | /parent-module@1.0.1: 2172 | resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} 2173 | engines: {node: '>=6'} 2174 | dependencies: 2175 | callsites: 3.1.0 2176 | dev: true 2177 | 2178 | /parse-json@4.0.0: 2179 | resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} 2180 | engines: {node: '>=4'} 2181 | dependencies: 2182 | error-ex: 1.3.2 2183 | json-parse-better-errors: 1.0.2 2184 | dev: true 2185 | 2186 | /pascal-case@3.1.2: 2187 | resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} 2188 | dependencies: 2189 | no-case: 3.0.4 2190 | tslib: 2.6.0 2191 | dev: true 2192 | 2193 | /path-exists@4.0.0: 2194 | resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 2195 | engines: {node: '>=8'} 2196 | dev: true 2197 | 2198 | /path-is-absolute@1.0.1: 2199 | resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} 2200 | engines: {node: '>=0.10.0'} 2201 | dev: true 2202 | 2203 | /path-key@2.0.1: 2204 | resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} 2205 | engines: {node: '>=4'} 2206 | dev: true 2207 | 2208 | /path-key@3.1.1: 2209 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 2210 | engines: {node: '>=8'} 2211 | dev: true 2212 | 2213 | /path-parse@1.0.7: 2214 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 2215 | dev: true 2216 | 2217 | /path-type@3.0.0: 2218 | resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} 2219 | engines: {node: '>=4'} 2220 | dependencies: 2221 | pify: 3.0.0 2222 | dev: true 2223 | 2224 | /path-type@4.0.0: 2225 | resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} 2226 | engines: {node: '>=8'} 2227 | dev: true 2228 | 2229 | /pathe@1.1.1: 2230 | resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==} 2231 | dev: true 2232 | 2233 | /pathval@1.1.1: 2234 | resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} 2235 | dev: true 2236 | 2237 | /periscopic@3.1.0: 2238 | resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} 2239 | dependencies: 2240 | '@types/estree': 1.0.1 2241 | estree-walker: 3.0.3 2242 | is-reference: 3.0.1 2243 | dev: true 2244 | 2245 | /picocolors@1.0.0: 2246 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 2247 | dev: true 2248 | 2249 | /picomatch@2.3.1: 2250 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 2251 | engines: {node: '>=8.6'} 2252 | dev: true 2253 | 2254 | /pidtree@0.3.1: 2255 | resolution: {integrity: sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==} 2256 | engines: {node: '>=0.10'} 2257 | hasBin: true 2258 | dev: true 2259 | 2260 | /pify@3.0.0: 2261 | resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} 2262 | engines: {node: '>=4'} 2263 | dev: true 2264 | 2265 | /pkg-types@1.0.3: 2266 | resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} 2267 | dependencies: 2268 | jsonc-parser: 3.2.0 2269 | mlly: 1.4.0 2270 | pathe: 1.1.1 2271 | dev: true 2272 | 2273 | /playwright-core@1.35.1: 2274 | resolution: {integrity: sha512-pNXb6CQ7OqmGDRspEjlxE49w+4YtR6a3X6mT1hZXeJHWmsEz7SunmvZeiG/+y1yyMZdHnnn73WKYdtV1er0Xyg==} 2275 | engines: {node: '>=16'} 2276 | hasBin: true 2277 | dev: true 2278 | 2279 | /postcss-load-config@3.1.4(postcss@8.4.25): 2280 | resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} 2281 | engines: {node: '>= 10'} 2282 | peerDependencies: 2283 | postcss: '>=8.0.9' 2284 | ts-node: '>=9.0.0' 2285 | peerDependenciesMeta: 2286 | postcss: 2287 | optional: true 2288 | ts-node: 2289 | optional: true 2290 | dependencies: 2291 | lilconfig: 2.1.0 2292 | postcss: 8.4.25 2293 | yaml: 1.10.2 2294 | dev: true 2295 | 2296 | /postcss-safe-parser@6.0.0(postcss@8.4.25): 2297 | resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} 2298 | engines: {node: '>=12.0'} 2299 | peerDependencies: 2300 | postcss: ^8.3.3 2301 | dependencies: 2302 | postcss: 8.4.25 2303 | dev: true 2304 | 2305 | /postcss-scss@4.0.6(postcss@8.4.25): 2306 | resolution: {integrity: sha512-rLDPhJY4z/i4nVFZ27j9GqLxj1pwxE80eAzUNRMXtcpipFYIeowerzBgG3yJhMtObGEXidtIgbUpQ3eLDsf5OQ==} 2307 | engines: {node: '>=12.0'} 2308 | peerDependencies: 2309 | postcss: ^8.4.19 2310 | dependencies: 2311 | postcss: 8.4.25 2312 | dev: true 2313 | 2314 | /postcss-selector-parser@6.0.13: 2315 | resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==} 2316 | engines: {node: '>=4'} 2317 | dependencies: 2318 | cssesc: 3.0.0 2319 | util-deprecate: 1.0.2 2320 | dev: true 2321 | 2322 | /postcss@8.4.25: 2323 | resolution: {integrity: sha512-7taJ/8t2av0Z+sQEvNzCkpDynl0tX3uJMCODi6nT3PfASC7dYCWV9aQ+uiCf+KBD4SEFcu+GvJdGdwzQ6OSjCw==} 2324 | engines: {node: ^10 || ^12 || >=14} 2325 | dependencies: 2326 | nanoid: 3.3.6 2327 | picocolors: 1.0.0 2328 | source-map-js: 1.0.2 2329 | dev: true 2330 | 2331 | /prelude-ls@1.2.1: 2332 | resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} 2333 | engines: {node: '>= 0.8.0'} 2334 | dev: true 2335 | 2336 | /prettier-plugin-svelte@2.10.1(prettier@2.8.8)(svelte@4.0.5): 2337 | resolution: {integrity: sha512-Wlq7Z5v2ueCubWo0TZzKc9XHcm7TDxqcuzRuGd0gcENfzfT4JZ9yDlCbEgxWgiPmLHkBjfOtpAWkcT28MCDpUQ==} 2338 | peerDependencies: 2339 | prettier: ^1.16.4 || ^2.0.0 2340 | svelte: ^3.2.0 || ^4.0.0-next.0 2341 | dependencies: 2342 | prettier: 2.8.8 2343 | svelte: 4.0.5 2344 | dev: true 2345 | 2346 | /prettier@2.8.8: 2347 | resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} 2348 | engines: {node: '>=10.13.0'} 2349 | hasBin: true 2350 | dev: true 2351 | 2352 | /pretty-format@29.6.1: 2353 | resolution: {integrity: sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog==} 2354 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 2355 | dependencies: 2356 | '@jest/schemas': 29.6.0 2357 | ansi-styles: 5.2.0 2358 | react-is: 18.2.0 2359 | dev: true 2360 | 2361 | /publint@0.1.16: 2362 | resolution: {integrity: sha512-wJgk7HnXDT5Ap0DjFYbGz78kPkN44iQvDiaq8P63IEEyNU9mYXvaMd2cAyIM6OgqXM/IA3CK6XWIsRq+wjNpgw==} 2363 | engines: {node: '>=16'} 2364 | hasBin: true 2365 | dependencies: 2366 | npm-packlist: 5.1.3 2367 | picocolors: 1.0.0 2368 | sade: 1.8.1 2369 | dev: true 2370 | 2371 | /punycode@2.3.0: 2372 | resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} 2373 | engines: {node: '>=6'} 2374 | dev: true 2375 | 2376 | /queue-microtask@1.2.3: 2377 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 2378 | dev: true 2379 | 2380 | /react-is@18.2.0: 2381 | resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} 2382 | dev: true 2383 | 2384 | /read-pkg@3.0.0: 2385 | resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} 2386 | engines: {node: '>=4'} 2387 | dependencies: 2388 | load-json-file: 4.0.0 2389 | normalize-package-data: 2.5.0 2390 | path-type: 3.0.0 2391 | dev: true 2392 | 2393 | /readdirp@3.6.0: 2394 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 2395 | engines: {node: '>=8.10.0'} 2396 | dependencies: 2397 | picomatch: 2.3.1 2398 | dev: true 2399 | 2400 | /regexp.prototype.flags@1.5.0: 2401 | resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} 2402 | engines: {node: '>= 0.4'} 2403 | dependencies: 2404 | call-bind: 1.0.2 2405 | define-properties: 1.2.0 2406 | functions-have-names: 1.2.3 2407 | dev: true 2408 | 2409 | /resolve-from@4.0.0: 2410 | resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} 2411 | engines: {node: '>=4'} 2412 | dev: true 2413 | 2414 | /resolve@1.22.2: 2415 | resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==} 2416 | hasBin: true 2417 | dependencies: 2418 | is-core-module: 2.12.1 2419 | path-parse: 1.0.7 2420 | supports-preserve-symlinks-flag: 1.0.0 2421 | dev: true 2422 | 2423 | /reusify@1.0.4: 2424 | resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} 2425 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 2426 | dev: true 2427 | 2428 | /rimraf@2.7.1: 2429 | resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} 2430 | hasBin: true 2431 | dependencies: 2432 | glob: 7.2.3 2433 | dev: true 2434 | 2435 | /rimraf@3.0.2: 2436 | resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} 2437 | hasBin: true 2438 | dependencies: 2439 | glob: 7.2.3 2440 | dev: true 2441 | 2442 | /rollup@3.26.2: 2443 | resolution: {integrity: sha512-6umBIGVz93er97pMgQO08LuH3m6PUb3jlDUUGFsNJB6VgTCUaDFpupf5JfU30529m/UKOgmiX+uY6Sx8cOYpLA==} 2444 | engines: {node: '>=14.18.0', npm: '>=8.0.0'} 2445 | hasBin: true 2446 | optionalDependencies: 2447 | fsevents: 2.3.2 2448 | dev: true 2449 | 2450 | /run-parallel@1.2.0: 2451 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 2452 | dependencies: 2453 | queue-microtask: 1.2.3 2454 | dev: true 2455 | 2456 | /sade@1.8.1: 2457 | resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} 2458 | engines: {node: '>=6'} 2459 | dependencies: 2460 | mri: 1.2.0 2461 | dev: true 2462 | 2463 | /safe-regex-test@1.0.0: 2464 | resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} 2465 | dependencies: 2466 | call-bind: 1.0.2 2467 | get-intrinsic: 1.2.1 2468 | is-regex: 1.1.4 2469 | dev: true 2470 | 2471 | /sander@0.5.1: 2472 | resolution: {integrity: sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==} 2473 | dependencies: 2474 | es6-promise: 3.3.1 2475 | graceful-fs: 4.2.11 2476 | mkdirp: 0.5.6 2477 | rimraf: 2.7.1 2478 | dev: true 2479 | 2480 | /sass@1.63.6: 2481 | resolution: {integrity: sha512-MJuxGMHzaOW7ipp+1KdELtqKbfAWbH7OLIdoSMnVe3EXPMTmxTmlaZDCTsgIpPCs3w99lLo9/zDKkOrJuT5byw==} 2482 | engines: {node: '>=14.0.0'} 2483 | hasBin: true 2484 | dependencies: 2485 | chokidar: 3.5.3 2486 | immutable: 4.3.0 2487 | source-map-js: 1.0.2 2488 | dev: true 2489 | 2490 | /semver@5.7.1: 2491 | resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} 2492 | hasBin: true 2493 | dev: true 2494 | 2495 | /semver@6.3.0: 2496 | resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} 2497 | hasBin: true 2498 | dev: true 2499 | 2500 | /semver@7.5.4: 2501 | resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} 2502 | engines: {node: '>=10'} 2503 | hasBin: true 2504 | dependencies: 2505 | lru-cache: 6.0.0 2506 | dev: true 2507 | 2508 | /set-cookie-parser@2.6.0: 2509 | resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} 2510 | dev: true 2511 | 2512 | /shebang-command@1.2.0: 2513 | resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} 2514 | engines: {node: '>=0.10.0'} 2515 | dependencies: 2516 | shebang-regex: 1.0.0 2517 | dev: true 2518 | 2519 | /shebang-command@2.0.0: 2520 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 2521 | engines: {node: '>=8'} 2522 | dependencies: 2523 | shebang-regex: 3.0.0 2524 | dev: true 2525 | 2526 | /shebang-regex@1.0.0: 2527 | resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} 2528 | engines: {node: '>=0.10.0'} 2529 | dev: true 2530 | 2531 | /shebang-regex@3.0.0: 2532 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 2533 | engines: {node: '>=8'} 2534 | dev: true 2535 | 2536 | /shell-quote@1.8.1: 2537 | resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} 2538 | dev: true 2539 | 2540 | /side-channel@1.0.4: 2541 | resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} 2542 | dependencies: 2543 | call-bind: 1.0.2 2544 | get-intrinsic: 1.2.1 2545 | object-inspect: 1.12.3 2546 | dev: true 2547 | 2548 | /siginfo@2.0.0: 2549 | resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} 2550 | dev: true 2551 | 2552 | /sirv@2.0.3: 2553 | resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==} 2554 | engines: {node: '>= 10'} 2555 | dependencies: 2556 | '@polka/url': 1.0.0-next.21 2557 | mrmime: 1.0.1 2558 | totalist: 3.0.1 2559 | dev: true 2560 | 2561 | /slash@3.0.0: 2562 | resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} 2563 | engines: {node: '>=8'} 2564 | dev: true 2565 | 2566 | /sorcery@0.11.0: 2567 | resolution: {integrity: sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==} 2568 | hasBin: true 2569 | dependencies: 2570 | '@jridgewell/sourcemap-codec': 1.4.15 2571 | buffer-crc32: 0.2.13 2572 | minimist: 1.2.8 2573 | sander: 0.5.1 2574 | dev: true 2575 | 2576 | /source-map-js@1.0.2: 2577 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} 2578 | engines: {node: '>=0.10.0'} 2579 | dev: true 2580 | 2581 | /source-map@0.6.1: 2582 | resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} 2583 | engines: {node: '>=0.10.0'} 2584 | dev: true 2585 | 2586 | /spdx-correct@3.2.0: 2587 | resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} 2588 | dependencies: 2589 | spdx-expression-parse: 3.0.1 2590 | spdx-license-ids: 3.0.13 2591 | dev: true 2592 | 2593 | /spdx-exceptions@2.3.0: 2594 | resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} 2595 | dev: true 2596 | 2597 | /spdx-expression-parse@3.0.1: 2598 | resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} 2599 | dependencies: 2600 | spdx-exceptions: 2.3.0 2601 | spdx-license-ids: 3.0.13 2602 | dev: true 2603 | 2604 | /spdx-license-ids@3.0.13: 2605 | resolution: {integrity: sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==} 2606 | dev: true 2607 | 2608 | /stackback@0.0.2: 2609 | resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} 2610 | dev: true 2611 | 2612 | /std-env@3.3.3: 2613 | resolution: {integrity: sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==} 2614 | dev: true 2615 | 2616 | /streamsearch@1.1.0: 2617 | resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} 2618 | engines: {node: '>=10.0.0'} 2619 | dev: true 2620 | 2621 | /string.prototype.padend@3.1.4: 2622 | resolution: {integrity: sha512-67otBXoksdjsnXXRUq+KMVTdlVRZ2af422Y0aTyTjVaoQkGr3mxl2Bc5emi7dOQ3OGVVQQskmLEWwFXwommpNw==} 2623 | engines: {node: '>= 0.4'} 2624 | dependencies: 2625 | call-bind: 1.0.2 2626 | define-properties: 1.2.0 2627 | es-abstract: 1.21.2 2628 | dev: true 2629 | 2630 | /string.prototype.trim@1.2.7: 2631 | resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} 2632 | engines: {node: '>= 0.4'} 2633 | dependencies: 2634 | call-bind: 1.0.2 2635 | define-properties: 1.2.0 2636 | es-abstract: 1.21.2 2637 | dev: true 2638 | 2639 | /string.prototype.trimend@1.0.6: 2640 | resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} 2641 | dependencies: 2642 | call-bind: 1.0.2 2643 | define-properties: 1.2.0 2644 | es-abstract: 1.21.2 2645 | dev: true 2646 | 2647 | /string.prototype.trimstart@1.0.6: 2648 | resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} 2649 | dependencies: 2650 | call-bind: 1.0.2 2651 | define-properties: 1.2.0 2652 | es-abstract: 1.21.2 2653 | dev: true 2654 | 2655 | /strip-ansi@6.0.1: 2656 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 2657 | engines: {node: '>=8'} 2658 | dependencies: 2659 | ansi-regex: 5.0.1 2660 | dev: true 2661 | 2662 | /strip-bom@3.0.0: 2663 | resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} 2664 | engines: {node: '>=4'} 2665 | dev: true 2666 | 2667 | /strip-indent@3.0.0: 2668 | resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} 2669 | engines: {node: '>=8'} 2670 | dependencies: 2671 | min-indent: 1.0.1 2672 | dev: true 2673 | 2674 | /strip-json-comments@3.1.1: 2675 | resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 2676 | engines: {node: '>=8'} 2677 | dev: true 2678 | 2679 | /strip-literal@1.0.1: 2680 | resolution: {integrity: sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q==} 2681 | dependencies: 2682 | acorn: 8.10.0 2683 | dev: true 2684 | 2685 | /supports-color@5.5.0: 2686 | resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} 2687 | engines: {node: '>=4'} 2688 | dependencies: 2689 | has-flag: 3.0.0 2690 | dev: true 2691 | 2692 | /supports-color@7.2.0: 2693 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 2694 | engines: {node: '>=8'} 2695 | dependencies: 2696 | has-flag: 4.0.0 2697 | dev: true 2698 | 2699 | /supports-preserve-symlinks-flag@1.0.0: 2700 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 2701 | engines: {node: '>= 0.4'} 2702 | dev: true 2703 | 2704 | /svelte-check@3.4.5(postcss@8.4.25)(sass@1.63.6)(svelte@4.0.5): 2705 | resolution: {integrity: sha512-FsD/CUVdEI0F9sfylh1Fe15kDjvvbyBxzDpACPsdq0EASgaZukBXaMXofpxlgmWsgVET3OynMQlbtUQoWCz9Rw==} 2706 | hasBin: true 2707 | peerDependencies: 2708 | svelte: ^3.55.0 || ^4.0.0-next.0 || ^4.0.0 2709 | dependencies: 2710 | '@jridgewell/trace-mapping': 0.3.18 2711 | chokidar: 3.5.3 2712 | fast-glob: 3.3.0 2713 | import-fresh: 3.3.0 2714 | picocolors: 1.0.0 2715 | sade: 1.8.1 2716 | svelte: 4.0.5 2717 | svelte-preprocess: 5.0.4(postcss@8.4.25)(sass@1.63.6)(svelte@4.0.5)(typescript@5.1.6) 2718 | typescript: 5.1.6 2719 | transitivePeerDependencies: 2720 | - '@babel/core' 2721 | - coffeescript 2722 | - less 2723 | - postcss 2724 | - postcss-load-config 2725 | - pug 2726 | - sass 2727 | - stylus 2728 | - sugarss 2729 | dev: true 2730 | 2731 | /svelte-eslint-parser@0.32.1(svelte@4.0.5): 2732 | resolution: {integrity: sha512-GCSfeIzdgk53CaOzK+s/+l2igfTno3mWGkwoDYAwPes/rD9Al2fc7ksfopjx5UL87S7dw1eL73F6wNYiiuhzIA==} 2733 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 2734 | peerDependencies: 2735 | svelte: ^3.37.0 || ^4.0.0 2736 | peerDependenciesMeta: 2737 | svelte: 2738 | optional: true 2739 | dependencies: 2740 | eslint-scope: 7.2.0 2741 | eslint-visitor-keys: 3.4.1 2742 | espree: 9.6.0 2743 | postcss: 8.4.25 2744 | postcss-scss: 4.0.6(postcss@8.4.25) 2745 | svelte: 4.0.5 2746 | dev: true 2747 | 2748 | /svelte-hmr@0.15.2(svelte@4.0.5): 2749 | resolution: {integrity: sha512-q/bAruCvFLwvNbeE1x3n37TYFb3mTBJ6TrCq6p2CoFbSTNhDE9oAtEfpy+wmc9So8AG0Tja+X0/mJzX9tSfvIg==} 2750 | engines: {node: ^12.20 || ^14.13.1 || >= 16} 2751 | peerDependencies: 2752 | svelte: ^3.19.0 || ^4.0.0-next.0 2753 | dependencies: 2754 | svelte: 4.0.5 2755 | dev: true 2756 | 2757 | /svelte-preprocess@5.0.4(postcss@8.4.25)(sass@1.63.6)(svelte@4.0.5)(typescript@5.1.6): 2758 | resolution: {integrity: sha512-ABia2QegosxOGsVlsSBJvoWeXy1wUKSfF7SWJdTjLAbx/Y3SrVevvvbFNQqrSJw89+lNSsM58SipmZJ5SRi5iw==} 2759 | engines: {node: '>= 14.10.0'} 2760 | requiresBuild: true 2761 | peerDependencies: 2762 | '@babel/core': ^7.10.2 2763 | coffeescript: ^2.5.1 2764 | less: ^3.11.3 || ^4.0.0 2765 | postcss: ^7 || ^8 2766 | postcss-load-config: ^2.1.0 || ^3.0.0 || ^4.0.0 2767 | pug: ^3.0.0 2768 | sass: ^1.26.8 2769 | stylus: ^0.55.0 2770 | sugarss: ^2.0.0 || ^3.0.0 || ^4.0.0 2771 | svelte: ^3.23.0 || ^4.0.0-next.0 || ^4.0.0 2772 | typescript: '>=3.9.5 || ^4.0.0 || ^5.0.0' 2773 | peerDependenciesMeta: 2774 | '@babel/core': 2775 | optional: true 2776 | coffeescript: 2777 | optional: true 2778 | less: 2779 | optional: true 2780 | postcss: 2781 | optional: true 2782 | postcss-load-config: 2783 | optional: true 2784 | pug: 2785 | optional: true 2786 | sass: 2787 | optional: true 2788 | stylus: 2789 | optional: true 2790 | sugarss: 2791 | optional: true 2792 | typescript: 2793 | optional: true 2794 | dependencies: 2795 | '@types/pug': 2.0.6 2796 | detect-indent: 6.1.0 2797 | magic-string: 0.27.0 2798 | postcss: 8.4.25 2799 | sass: 1.63.6 2800 | sorcery: 0.11.0 2801 | strip-indent: 3.0.0 2802 | svelte: 4.0.5 2803 | typescript: 5.1.6 2804 | dev: true 2805 | 2806 | /svelte2tsx@0.6.19(svelte@4.0.5)(typescript@5.1.6): 2807 | resolution: {integrity: sha512-h3b5OtcO8zyVL/RiB2zsDwCopeo/UH+887uyhgb2mjnewOFwiTxu+4IGuVwrrlyuh2onM2ktfUemNrNmQwXONQ==} 2808 | peerDependencies: 2809 | svelte: ^3.55 || ^4.0.0-next.0 || ^4.0 2810 | typescript: ^4.9.4 || ^5.0.0 2811 | dependencies: 2812 | dedent-js: 1.0.1 2813 | pascal-case: 3.1.2 2814 | svelte: 4.0.5 2815 | typescript: 5.1.6 2816 | dev: true 2817 | 2818 | /svelte@4.0.5: 2819 | resolution: {integrity: sha512-PHKPWP1wiWHBtsE57nCb8xiWB3Ht7/3Kvi3jac0XIxUM2rep8alO7YoAtgWeGD7++tFy46krilOrPW0mG3Dx+A==} 2820 | engines: {node: '>=16'} 2821 | dependencies: 2822 | '@ampproject/remapping': 2.2.1 2823 | '@jridgewell/sourcemap-codec': 1.4.15 2824 | '@jridgewell/trace-mapping': 0.3.18 2825 | acorn: 8.10.0 2826 | aria-query: 5.3.0 2827 | axobject-query: 3.2.1 2828 | code-red: 1.0.3 2829 | css-tree: 2.3.1 2830 | estree-walker: 3.0.3 2831 | is-reference: 3.0.1 2832 | locate-character: 3.0.0 2833 | magic-string: 0.30.1 2834 | periscopic: 3.1.0 2835 | dev: true 2836 | 2837 | /test-exclude@6.0.0: 2838 | resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} 2839 | engines: {node: '>=8'} 2840 | dependencies: 2841 | '@istanbuljs/schema': 0.1.3 2842 | glob: 7.2.3 2843 | minimatch: 3.1.2 2844 | dev: true 2845 | 2846 | /text-table@0.2.0: 2847 | resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} 2848 | dev: true 2849 | 2850 | /tinybench@2.5.0: 2851 | resolution: {integrity: sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==} 2852 | dev: true 2853 | 2854 | /tinypool@0.5.0: 2855 | resolution: {integrity: sha512-paHQtnrlS1QZYKF/GnLoOM/DN9fqaGOFbCbxzAhwniySnzl9Ebk8w73/dd34DAhe/obUbPAOldTyYXQZxnPBPQ==} 2856 | engines: {node: '>=14.0.0'} 2857 | dev: true 2858 | 2859 | /tinyspy@2.1.1: 2860 | resolution: {integrity: sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w==} 2861 | engines: {node: '>=14.0.0'} 2862 | dev: true 2863 | 2864 | /to-regex-range@5.0.1: 2865 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 2866 | engines: {node: '>=8.0'} 2867 | dependencies: 2868 | is-number: 7.0.0 2869 | dev: true 2870 | 2871 | /totalist@3.0.1: 2872 | resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} 2873 | engines: {node: '>=6'} 2874 | dev: true 2875 | 2876 | /tslib@1.14.1: 2877 | resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} 2878 | dev: true 2879 | 2880 | /tslib@2.6.0: 2881 | resolution: {integrity: sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==} 2882 | dev: true 2883 | 2884 | /tsutils@3.21.0(typescript@5.1.6): 2885 | resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} 2886 | engines: {node: '>= 6'} 2887 | peerDependencies: 2888 | typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' 2889 | dependencies: 2890 | tslib: 1.14.1 2891 | typescript: 5.1.6 2892 | dev: true 2893 | 2894 | /type-check@0.4.0: 2895 | resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} 2896 | engines: {node: '>= 0.8.0'} 2897 | dependencies: 2898 | prelude-ls: 1.2.1 2899 | dev: true 2900 | 2901 | /type-detect@4.0.8: 2902 | resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} 2903 | engines: {node: '>=4'} 2904 | dev: true 2905 | 2906 | /type-fest@0.20.2: 2907 | resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} 2908 | engines: {node: '>=10'} 2909 | dev: true 2910 | 2911 | /typed-array-length@1.0.4: 2912 | resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} 2913 | dependencies: 2914 | call-bind: 1.0.2 2915 | for-each: 0.3.3 2916 | is-typed-array: 1.1.10 2917 | dev: true 2918 | 2919 | /typesafe-i18n@5.24.3(typescript@5.1.6): 2920 | resolution: {integrity: sha512-VqjGbIydOqtQjO81mGo++TjuqeC8YoxxgYrO1/ikXIS/l/Q6mAcz4yrt5AvqjdFHMt3gRm7oGbVI/yLyV+VTOg==} 2921 | hasBin: true 2922 | peerDependencies: 2923 | typescript: '>=3.5.1' 2924 | dependencies: 2925 | typescript: 5.1.6 2926 | dev: true 2927 | 2928 | /typescript@5.1.6: 2929 | resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} 2930 | engines: {node: '>=14.17'} 2931 | hasBin: true 2932 | dev: true 2933 | 2934 | /ufo@1.1.2: 2935 | resolution: {integrity: sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==} 2936 | dev: true 2937 | 2938 | /unbox-primitive@1.0.2: 2939 | resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} 2940 | dependencies: 2941 | call-bind: 1.0.2 2942 | has-bigints: 1.0.2 2943 | has-symbols: 1.0.3 2944 | which-boxed-primitive: 1.0.2 2945 | dev: true 2946 | 2947 | /undici@5.22.1: 2948 | resolution: {integrity: sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==} 2949 | engines: {node: '>=14.0'} 2950 | dependencies: 2951 | busboy: 1.6.0 2952 | dev: true 2953 | 2954 | /uri-js@4.4.1: 2955 | resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 2956 | dependencies: 2957 | punycode: 2.3.0 2958 | dev: true 2959 | 2960 | /util-deprecate@1.0.2: 2961 | resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} 2962 | dev: true 2963 | 2964 | /v8-to-istanbul@9.1.0: 2965 | resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} 2966 | engines: {node: '>=10.12.0'} 2967 | dependencies: 2968 | '@jridgewell/trace-mapping': 0.3.18 2969 | '@types/istanbul-lib-coverage': 2.0.4 2970 | convert-source-map: 1.9.0 2971 | dev: true 2972 | 2973 | /validate-npm-package-license@3.0.4: 2974 | resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} 2975 | dependencies: 2976 | spdx-correct: 3.2.0 2977 | spdx-expression-parse: 3.0.1 2978 | dev: true 2979 | 2980 | /vite-node@0.32.4(@types/node@20.4.1)(sass@1.63.6): 2981 | resolution: {integrity: sha512-L2gIw+dCxO0LK14QnUMoqSYpa9XRGnTTTDjW2h19Mr+GR0EFj4vx52W41gFXfMLqpA00eK4ZjOVYo1Xk//LFEw==} 2982 | engines: {node: '>=v14.18.0'} 2983 | hasBin: true 2984 | dependencies: 2985 | cac: 6.7.14 2986 | debug: 4.3.4 2987 | mlly: 1.4.0 2988 | pathe: 1.1.1 2989 | picocolors: 1.0.0 2990 | vite: 4.4.2(@types/node@20.4.1)(sass@1.63.6) 2991 | transitivePeerDependencies: 2992 | - '@types/node' 2993 | - less 2994 | - lightningcss 2995 | - sass 2996 | - stylus 2997 | - sugarss 2998 | - supports-color 2999 | - terser 3000 | dev: true 3001 | 3002 | /vite@4.4.2(@types/node@20.4.1)(sass@1.63.6): 3003 | resolution: {integrity: sha512-zUcsJN+UvdSyHhYa277UHhiJ3iq4hUBwHavOpsNUGsTgjBeoBlK8eDt+iT09pBq0h9/knhG/SPrZiM7cGmg7NA==} 3004 | engines: {node: ^14.18.0 || >=16.0.0} 3005 | hasBin: true 3006 | peerDependencies: 3007 | '@types/node': '>= 14' 3008 | less: '*' 3009 | lightningcss: ^1.21.0 3010 | sass: '*' 3011 | stylus: '*' 3012 | sugarss: '*' 3013 | terser: ^5.4.0 3014 | peerDependenciesMeta: 3015 | '@types/node': 3016 | optional: true 3017 | less: 3018 | optional: true 3019 | lightningcss: 3020 | optional: true 3021 | sass: 3022 | optional: true 3023 | stylus: 3024 | optional: true 3025 | sugarss: 3026 | optional: true 3027 | terser: 3028 | optional: true 3029 | dependencies: 3030 | '@types/node': 20.4.1 3031 | esbuild: 0.18.11 3032 | postcss: 8.4.25 3033 | rollup: 3.26.2 3034 | sass: 1.63.6 3035 | optionalDependencies: 3036 | fsevents: 2.3.2 3037 | dev: true 3038 | 3039 | /vitefu@0.2.4(vite@4.4.2): 3040 | resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==} 3041 | peerDependencies: 3042 | vite: ^3.0.0 || ^4.0.0 3043 | peerDependenciesMeta: 3044 | vite: 3045 | optional: true 3046 | dependencies: 3047 | vite: 4.4.2(@types/node@20.4.1)(sass@1.63.6) 3048 | dev: true 3049 | 3050 | /vitest@0.32.4(sass@1.63.6): 3051 | resolution: {integrity: sha512-3czFm8RnrsWwIzVDu/Ca48Y/M+qh3vOnF16czJm98Q/AN1y3B6PBsyV8Re91Ty5s7txKNjEhpgtGPcfdbh2MZg==} 3052 | engines: {node: '>=v14.18.0'} 3053 | hasBin: true 3054 | peerDependencies: 3055 | '@edge-runtime/vm': '*' 3056 | '@vitest/browser': '*' 3057 | '@vitest/ui': '*' 3058 | happy-dom: '*' 3059 | jsdom: '*' 3060 | playwright: '*' 3061 | safaridriver: '*' 3062 | webdriverio: '*' 3063 | peerDependenciesMeta: 3064 | '@edge-runtime/vm': 3065 | optional: true 3066 | '@vitest/browser': 3067 | optional: true 3068 | '@vitest/ui': 3069 | optional: true 3070 | happy-dom: 3071 | optional: true 3072 | jsdom: 3073 | optional: true 3074 | playwright: 3075 | optional: true 3076 | safaridriver: 3077 | optional: true 3078 | webdriverio: 3079 | optional: true 3080 | dependencies: 3081 | '@types/chai': 4.3.5 3082 | '@types/chai-subset': 1.3.3 3083 | '@types/node': 20.4.1 3084 | '@vitest/expect': 0.32.4 3085 | '@vitest/runner': 0.32.4 3086 | '@vitest/snapshot': 0.32.4 3087 | '@vitest/spy': 0.32.4 3088 | '@vitest/utils': 0.32.4 3089 | acorn: 8.10.0 3090 | acorn-walk: 8.2.0 3091 | cac: 6.7.14 3092 | chai: 4.3.7 3093 | debug: 4.3.4 3094 | local-pkg: 0.4.3 3095 | magic-string: 0.30.1 3096 | pathe: 1.1.1 3097 | picocolors: 1.0.0 3098 | std-env: 3.3.3 3099 | strip-literal: 1.0.1 3100 | tinybench: 2.5.0 3101 | tinypool: 0.5.0 3102 | vite: 4.4.2(@types/node@20.4.1)(sass@1.63.6) 3103 | vite-node: 0.32.4(@types/node@20.4.1)(sass@1.63.6) 3104 | why-is-node-running: 2.2.2 3105 | transitivePeerDependencies: 3106 | - less 3107 | - lightningcss 3108 | - sass 3109 | - stylus 3110 | - sugarss 3111 | - supports-color 3112 | - terser 3113 | dev: true 3114 | 3115 | /which-boxed-primitive@1.0.2: 3116 | resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} 3117 | dependencies: 3118 | is-bigint: 1.0.4 3119 | is-boolean-object: 1.1.2 3120 | is-number-object: 1.0.7 3121 | is-string: 1.0.7 3122 | is-symbol: 1.0.4 3123 | dev: true 3124 | 3125 | /which-typed-array@1.1.9: 3126 | resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==} 3127 | engines: {node: '>= 0.4'} 3128 | dependencies: 3129 | available-typed-arrays: 1.0.5 3130 | call-bind: 1.0.2 3131 | for-each: 0.3.3 3132 | gopd: 1.0.1 3133 | has-tostringtag: 1.0.0 3134 | is-typed-array: 1.1.10 3135 | dev: true 3136 | 3137 | /which@1.3.1: 3138 | resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} 3139 | hasBin: true 3140 | dependencies: 3141 | isexe: 2.0.0 3142 | dev: true 3143 | 3144 | /which@2.0.2: 3145 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 3146 | engines: {node: '>= 8'} 3147 | hasBin: true 3148 | dependencies: 3149 | isexe: 2.0.0 3150 | dev: true 3151 | 3152 | /why-is-node-running@2.2.2: 3153 | resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} 3154 | engines: {node: '>=8'} 3155 | hasBin: true 3156 | dependencies: 3157 | siginfo: 2.0.0 3158 | stackback: 0.0.2 3159 | dev: true 3160 | 3161 | /wrappy@1.0.2: 3162 | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} 3163 | dev: true 3164 | 3165 | /yallist@4.0.0: 3166 | resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} 3167 | dev: true 3168 | 3169 | /yaml@1.10.2: 3170 | resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} 3171 | engines: {node: '>= 6'} 3172 | dev: true 3173 | 3174 | /yocto-queue@0.1.0: 3175 | resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 3176 | engines: {node: '>=10'} 3177 | dev: true 3178 | 3179 | /yocto-queue@1.0.0: 3180 | resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} 3181 | engines: {node: '>=12.20'} 3182 | dev: true 3183 | -------------------------------------------------------------------------------- /src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://kit.svelte.dev/docs/types#app 2 | // for information about these interfaces 3 | 4 | import type { Locales, TranslationFunctions } from '$i18n/i18n-types.ts'; 5 | 6 | declare global { 7 | namespace App { 8 | interface Error { 9 | message: string; 10 | link?: string; 11 | linkText?: string; 12 | } 13 | 14 | interface Locals { 15 | locale: Locales; 16 | LL: TranslationFunctions; 17 | } 18 | 19 | // interface Platform {} 20 | } 21 | } 22 | 23 | export {}; 24 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %sveltekit.head% 8 | 9 | 10 |
%sveltekit.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /src/components/HeadHrefLangs.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | {#if !$page.error} 9 | 10 | {#each buildAlternateLinks($page.data.LP).hreflang as [hreflang, href]} 11 | 12 | {/each} 13 | 14 | 15 | 16 | {/if} 17 | -------------------------------------------------------------------------------- /src/components/Header.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 |
9 | 10 |

{$LL.site.name()} (demo)

11 |
12 | 13 | 63 | 64 | 65 |
66 | -------------------------------------------------------------------------------- /src/components/LocaleSwitcher.svelte: -------------------------------------------------------------------------------- 1 | 32 | 33 | 46 | -------------------------------------------------------------------------------- /src/hooks.server.ts: -------------------------------------------------------------------------------- 1 | import type { Locales } from '$i18n/i18n-types.js'; 2 | import { baseLocale, detectLocale, i18n, locales } from '$i18n/i18n-util.js'; 3 | import { loadAllLocales } from '$i18n/i18n-util.sync.js'; 4 | import { getLocaleFromUrl, getLocaleHomeHref, initLocalizeUrl } from '$lib/index.js'; 5 | import { redirect, type Handle, type RequestEvent } from '@sveltejs/kit'; 6 | import { initAcceptLanguageHeaderDetector } from 'typesafe-i18n/detectors'; 7 | 8 | loadAllLocales(); 9 | const L = i18n(); 10 | 11 | export const handle: Handle = async ({ event, resolve }) => { 12 | // initialize Localize URL library on server 13 | initLocalizeUrl({ 14 | baseLocale, 15 | locales 16 | }); 17 | const locale = getLocaleFromUrl(event.url) as Locales; 18 | 19 | // track the first visit for language detection 20 | let hasVisited = true; 21 | // only count translateable part of the website (exclude API) 22 | if ((event.route.id ?? '').startsWith('/[[lang=lang]]')) { 23 | hasVisited = event.cookies.get('visited') === '1'; 24 | } 25 | if (!hasVisited) { 26 | event.cookies.set('visited', '1', { path: '/' }); 27 | } 28 | 29 | /** 30 | * Redirect first time home page visitor based on their detected locale. 31 | * ! WARNING: Google advises against this: 32 | * ! https://developers.google.com/search/docs/specialty/international/managing-multi-regional-sites#let-the-user-switch-the-page-language 33 | */ 34 | if (!hasVisited && event.url.pathname === '/') { 35 | const detectedLocale = getPreferredLocale(event); 36 | if (detectedLocale !== locale) { 37 | const localeHomeUrl = getLocaleHomeHref(detectedLocale, event.url, true); 38 | throw redirect(302, localeHomeUrl); 39 | } 40 | } 41 | 42 | const LL = L[locale]; 43 | 44 | // bind locale and translation functions to current request 45 | event.locals.locale = locale; 46 | event.locals.LL = LL; 47 | 48 | console.info(LL.log({ fileName: 'hooks.server.ts' })); 49 | 50 | // replace html lang attribute with correct language 51 | return resolve(event, { transformPageChunk: ({ html }) => html.replace('%lang%', locale) }); 52 | }; 53 | 54 | const getPreferredLocale = ({ request }: RequestEvent) => { 55 | // Detect the preferred language the user has configured in his browser 56 | // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language 57 | const acceptLanguageDetector = initAcceptLanguageHeaderDetector(request); 58 | 59 | return detectLocale(acceptLanguageDetector); 60 | }; 61 | -------------------------------------------------------------------------------- /src/i18n/de/index.ts: -------------------------------------------------------------------------------- 1 | import en from '$i18n/en/index.js'; 2 | import type { Translation } from '../i18n-types.js'; 3 | 4 | const de = { 5 | // Fallback 6 | ...(en as Translation), 7 | 8 | log: "Dieses Logging wurde von '{fileName}' aufgerufen", 9 | site: { 10 | name: 'Localize URL' 11 | }, 12 | pageHome: { 13 | title: 'Startseite', 14 | welcome: 'Willkommen auf der Homepage', 15 | apiDescription: 'API-Aufrufe, die nicht von der Bibliothek betroffen sind', 16 | spectators: '{0} Zuschauer live', 17 | legend: { 18 | title: 'Legende', 19 | correct: 'Richtiger Link', 20 | fallback: 'Fallback-Link (führt zu einem anderen Gebietsschema)', 21 | vary: 'Richtiger Link (nur für unterstützte Gebietsschemas sichtbar)', 22 | redirect: 'Nicht übereinstimmender Link, Weiterleitung', 23 | error: 'Für dieses Gebietsschema nicht verfügbar' 24 | } 25 | }, 26 | pageAbout: { 27 | title: 'Über uns', 28 | text: 'Über uns Seite sagt Hallo.' 29 | }, 30 | pageEnglish: { 31 | title: 'Seite nur auf Englisch', 32 | text: '' 33 | }, 34 | pageNews: { 35 | title: 'Nachricht' 36 | }, 37 | pageError: { 38 | title404: 'Seite nicht gefunden', 39 | titleOffline: 'Es sieht so aus, als wären Sie offline', 40 | titleOther: 'Huch!', 41 | message404: 'Leider konnten wir die gewünschte Seite nicht finden.', 42 | messageNotTranslated: 'Leider ist diese Seite noch nicht in Ihrer Sprache verfügbar.', 43 | messageOffline: 'Laden Sie die Seite neu, sobald Sie das Internet gefunden haben.', 44 | messageOther: 'Bitte versuchen Sie, die Seite neu zu laden.', 45 | actionHome: 'Zur Startseite zurückkehren', 46 | actionBaseLocale: 'Diese Seite auf Englisch anzeigen' 47 | } 48 | } satisfies Translation; 49 | 50 | export default de; 51 | -------------------------------------------------------------------------------- /src/i18n/en/index.ts: -------------------------------------------------------------------------------- 1 | import type { BaseTranslation } from '../i18n-types.js'; 2 | 3 | const en = { 4 | log: "This log was called from '{fileName:string}'", 5 | site: { 6 | name: 'Localize URL' 7 | }, 8 | pageHome: { 9 | title: 'Home page', 10 | welcome: 'Welcome to the home page', 11 | apiDescription: 'API calls that are not affected by the library', 12 | spectators: '{0} live spectator{{s}}', 13 | legend: { 14 | title: 'Legend', 15 | correct: 'Correct link', 16 | fallback: 'Fallback link (leads to another locale)', 17 | vary: 'Correct link (only visible for supported locales)', 18 | redirect: 'Mismatched link, redirect', 19 | error: 'Not available for this locale' 20 | } 21 | }, 22 | pageAbout: { 23 | title: 'About', 24 | text: 'About us page says hello.' 25 | }, 26 | pageEnglish: { 27 | title: 'English-only page', 28 | text: 'This page is only available in English.' 29 | }, 30 | pageNews: { 31 | title: 'News' 32 | }, 33 | pageError: { 34 | title404: 'Page Not Found', 35 | titleOffline: "It looks like you're offline", 36 | titleOther: 'Yikes!', 37 | message404: "Sorry, we weren't able to find the requested page.", 38 | messageNotTranslated: "Sorry, this page isn't available in your language.", 39 | messageOffline: "Reload the page once you've found the internet.", 40 | messageOther: 'Please try reloading the page.', 41 | actionHome: 'Return to home page', 42 | actionBaseLocale: 'Show this page in English' 43 | } 44 | } satisfies BaseTranslation; 45 | 46 | export default en; 47 | -------------------------------------------------------------------------------- /src/i18n/formatters.ts: -------------------------------------------------------------------------------- 1 | import type { FormattersInitializer } from 'typesafe-i18n'; 2 | import type { Locales, Formatters } from './i18n-types.js'; 3 | 4 | export const initFormatters: FormattersInitializer = (locale: Locales) => { 5 | const formatters: Formatters = { 6 | // add your formatter functions here 7 | }; 8 | 9 | return formatters; 10 | }; 11 | -------------------------------------------------------------------------------- /src/i18n/i18n-svelte.ts: -------------------------------------------------------------------------------- 1 | // This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. 2 | /* eslint-disable */ 3 | 4 | import { initI18nSvelte } from 'typesafe-i18n/svelte'; 5 | import type { Formatters, Locales, TranslationFunctions, Translations } from './i18n-types.js'; 6 | import { loadedFormatters, loadedLocales } from './i18n-util.js'; 7 | 8 | const { locale, LL, setLocale } = initI18nSvelte< 9 | Locales, 10 | Translations, 11 | TranslationFunctions, 12 | Formatters 13 | >(loadedLocales, loadedFormatters); 14 | 15 | export { locale, LL, setLocale }; 16 | 17 | export default LL; 18 | -------------------------------------------------------------------------------- /src/i18n/i18n-types.ts: -------------------------------------------------------------------------------- 1 | // This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. 2 | /* eslint-disable */ 3 | import type { 4 | BaseTranslation as BaseTranslationType, 5 | LocalizedString, 6 | RequiredParams 7 | } from 'typesafe-i18n'; 8 | 9 | export type BaseTranslation = BaseTranslationType; 10 | export type BaseLocale = 'en'; 11 | 12 | export type Locales = 'de' | 'en' | 'it' | 'ru'; 13 | 14 | export type Translation = RootTranslation; 15 | 16 | export type Translations = RootTranslation; 17 | 18 | type RootTranslation = { 19 | /** 20 | * T​h​i​s​ ​l​o​g​ ​w​a​s​ ​c​a​l​l​e​d​ ​f​r​o​m​ ​'​{​f​i​l​e​N​a​m​e​}​' 21 | * @param {string} fileName 22 | */ 23 | log: RequiredParams<'fileName'>; 24 | site: { 25 | /** 26 | * L​o​c​a​l​i​z​e​ ​U​R​L 27 | */ 28 | name: string; 29 | }; 30 | pageHome: { 31 | /** 32 | * H​o​m​e​ ​p​a​g​e 33 | */ 34 | title: string; 35 | /** 36 | * W​e​l​c​o​m​e​ ​t​o​ ​t​h​e​ ​h​o​m​e​ ​p​a​g​e 37 | */ 38 | welcome: string; 39 | /** 40 | * A​P​I​ ​c​a​l​l​s​ ​t​h​a​t​ ​a​r​e​ ​n​o​t​ ​a​f​f​e​c​t​e​d​ ​b​y​ ​t​h​e​ ​l​i​b​r​a​r​y 41 | */ 42 | apiDescription: string; 43 | /** 44 | * {​0​}​ ​l​i​v​e​ ​s​p​e​c​t​a​t​o​r​{​{​s​}​} 45 | * @param {string | number | boolean} 0 46 | */ 47 | spectators: RequiredParams<'0'>; 48 | legend: { 49 | /** 50 | * L​e​g​e​n​d 51 | */ 52 | title: string; 53 | /** 54 | * C​o​r​r​e​c​t​ ​l​i​n​k 55 | */ 56 | correct: string; 57 | /** 58 | * F​a​l​l​b​a​c​k​ ​l​i​n​k​ ​(​l​e​a​d​s​ ​t​o​ ​a​n​o​t​h​e​r​ ​l​o​c​a​l​e​) 59 | */ 60 | fallback: string; 61 | /** 62 | * C​o​r​r​e​c​t​ ​l​i​n​k​ ​(​o​n​l​y​ ​v​i​s​i​b​l​e​ ​f​o​r​ ​s​u​p​p​o​r​t​e​d​ ​l​o​c​a​l​e​s​) 63 | */ 64 | vary: string; 65 | /** 66 | * M​i​s​m​a​t​c​h​e​d​ ​l​i​n​k​,​ ​r​e​d​i​r​e​c​t 67 | */ 68 | redirect: string; 69 | /** 70 | * N​o​t​ ​a​v​a​i​l​a​b​l​e​ ​f​o​r​ ​t​h​i​s​ ​l​o​c​a​l​e 71 | */ 72 | error: string; 73 | }; 74 | }; 75 | pageAbout: { 76 | /** 77 | * A​b​o​u​t 78 | */ 79 | title: string; 80 | /** 81 | * A​b​o​u​t​ ​u​s​ ​p​a​g​e​ ​s​a​y​s​ ​h​e​l​l​o​. 82 | */ 83 | text: string; 84 | }; 85 | pageEnglish: { 86 | /** 87 | * E​n​g​l​i​s​h​-​o​n​l​y​ ​p​a​g​e 88 | */ 89 | title: string; 90 | /** 91 | * T​h​i​s​ ​p​a​g​e​ ​i​s​ ​o​n​l​y​ ​a​v​a​i​l​a​b​l​e​ ​i​n​ ​E​n​g​l​i​s​h​. 92 | */ 93 | text: string; 94 | }; 95 | pageNews: { 96 | /** 97 | * N​e​w​s 98 | */ 99 | title: string; 100 | }; 101 | pageError: { 102 | /** 103 | * P​a​g​e​ ​N​o​t​ ​F​o​u​n​d 104 | */ 105 | title404: string; 106 | /** 107 | * I​t​ ​l​o​o​k​s​ ​l​i​k​e​ ​y​o​u​'​r​e​ ​o​f​f​l​i​n​e 108 | */ 109 | titleOffline: string; 110 | /** 111 | * Y​i​k​e​s​! 112 | */ 113 | titleOther: string; 114 | /** 115 | * S​o​r​r​y​,​ ​w​e​ ​w​e​r​e​n​'​t​ ​a​b​l​e​ ​t​o​ ​f​i​n​d​ ​t​h​e​ ​r​e​q​u​e​s​t​e​d​ ​p​a​g​e​. 116 | */ 117 | message404: string; 118 | /** 119 | * S​o​r​r​y​,​ ​t​h​i​s​ ​p​a​g​e​ ​i​s​n​'​t​ ​a​v​a​i​l​a​b​l​e​ ​i​n​ ​y​o​u​r​ ​l​a​n​g​u​a​g​e​. 120 | */ 121 | messageNotTranslated: string; 122 | /** 123 | * R​e​l​o​a​d​ ​t​h​e​ ​p​a​g​e​ ​o​n​c​e​ ​y​o​u​'​v​e​ ​f​o​u​n​d​ ​t​h​e​ ​i​n​t​e​r​n​e​t​. 124 | */ 125 | messageOffline: string; 126 | /** 127 | * P​l​e​a​s​e​ ​t​r​y​ ​r​e​l​o​a​d​i​n​g​ ​t​h​e​ ​p​a​g​e​. 128 | */ 129 | messageOther: string; 130 | /** 131 | * R​e​t​u​r​n​ ​t​o​ ​h​o​m​e​ ​p​a​g​e 132 | */ 133 | actionHome: string; 134 | /** 135 | * S​h​o​w​ ​t​h​i​s​ ​p​a​g​e​ ​i​n​ ​E​n​g​l​i​s​h 136 | */ 137 | actionBaseLocale: string; 138 | }; 139 | }; 140 | 141 | export type TranslationFunctions = { 142 | /** 143 | * This log was called from '{fileName}' 144 | */ 145 | log: (arg: { fileName: string }) => LocalizedString; 146 | site: { 147 | /** 148 | * Localize URL 149 | */ 150 | name: () => LocalizedString; 151 | }; 152 | pageHome: { 153 | /** 154 | * Home page 155 | */ 156 | title: () => LocalizedString; 157 | /** 158 | * Welcome to the home page 159 | */ 160 | welcome: () => LocalizedString; 161 | /** 162 | * API calls that are not affected by the library 163 | */ 164 | apiDescription: () => LocalizedString; 165 | /** 166 | * {0} live spectator{{s}} 167 | */ 168 | spectators: (arg0: string | number | boolean) => LocalizedString; 169 | legend: { 170 | /** 171 | * Legend 172 | */ 173 | title: () => LocalizedString; 174 | /** 175 | * Correct link 176 | */ 177 | correct: () => LocalizedString; 178 | /** 179 | * Fallback link (leads to another locale) 180 | */ 181 | fallback: () => LocalizedString; 182 | /** 183 | * Correct link (only visible for supported locales) 184 | */ 185 | vary: () => LocalizedString; 186 | /** 187 | * Mismatched link, redirect 188 | */ 189 | redirect: () => LocalizedString; 190 | /** 191 | * Not available for this locale 192 | */ 193 | error: () => LocalizedString; 194 | }; 195 | }; 196 | pageAbout: { 197 | /** 198 | * About 199 | */ 200 | title: () => LocalizedString; 201 | /** 202 | * About us page says hello. 203 | */ 204 | text: () => LocalizedString; 205 | }; 206 | pageEnglish: { 207 | /** 208 | * English-only page 209 | */ 210 | title: () => LocalizedString; 211 | /** 212 | * This page is only available in English. 213 | */ 214 | text: () => LocalizedString; 215 | }; 216 | pageNews: { 217 | /** 218 | * News 219 | */ 220 | title: () => LocalizedString; 221 | }; 222 | pageError: { 223 | /** 224 | * Page Not Found 225 | */ 226 | title404: () => LocalizedString; 227 | /** 228 | * It looks like you're offline 229 | */ 230 | titleOffline: () => LocalizedString; 231 | /** 232 | * Yikes! 233 | */ 234 | titleOther: () => LocalizedString; 235 | /** 236 | * Sorry, we weren't able to find the requested page. 237 | */ 238 | message404: () => LocalizedString; 239 | /** 240 | * Sorry, this page isn't available in your language. 241 | */ 242 | messageNotTranslated: () => LocalizedString; 243 | /** 244 | * Reload the page once you've found the internet. 245 | */ 246 | messageOffline: () => LocalizedString; 247 | /** 248 | * Please try reloading the page. 249 | */ 250 | messageOther: () => LocalizedString; 251 | /** 252 | * Return to home page 253 | */ 254 | actionHome: () => LocalizedString; 255 | /** 256 | * Show this page in English 257 | */ 258 | actionBaseLocale: () => LocalizedString; 259 | }; 260 | }; 261 | 262 | export type Formatters = {}; 263 | -------------------------------------------------------------------------------- /src/i18n/i18n-util.async.ts: -------------------------------------------------------------------------------- 1 | // This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. 2 | /* eslint-disable */ 3 | 4 | import { initFormatters } from './formatters.js'; 5 | import type { Locales, Translations } from './i18n-types.js'; 6 | import { loadedFormatters, loadedLocales, locales } from './i18n-util.js'; 7 | 8 | const localeTranslationLoaders = { 9 | de: () => import('./de/index.js'), 10 | en: () => import('./en/index.js'), 11 | it: () => import('./it/index.js'), 12 | ru: () => import('./ru/index.js') 13 | }; 14 | 15 | const updateDictionary = (locale: Locales, dictionary: Partial): Translations => 16 | (loadedLocales[locale] = { ...loadedLocales[locale], ...dictionary }); 17 | 18 | export const importLocaleAsync = async (locale: Locales): Promise => 19 | (await localeTranslationLoaders[locale]()).default as unknown as Translations; 20 | 21 | export const loadLocaleAsync = async (locale: Locales): Promise => { 22 | updateDictionary(locale, await importLocaleAsync(locale)); 23 | loadFormatters(locale); 24 | }; 25 | 26 | export const loadAllLocalesAsync = (): Promise => Promise.all(locales.map(loadLocaleAsync)); 27 | 28 | export const loadFormatters = (locale: Locales): void => 29 | void (loadedFormatters[locale] = initFormatters(locale)); 30 | -------------------------------------------------------------------------------- /src/i18n/i18n-util.sync.ts: -------------------------------------------------------------------------------- 1 | // This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. 2 | /* eslint-disable */ 3 | 4 | import { initFormatters } from './formatters.js'; 5 | import type { Locales, Translations } from './i18n-types.js'; 6 | import { loadedFormatters, loadedLocales, locales } from './i18n-util.js'; 7 | 8 | import de from './de/index.js'; 9 | import en from './en/index.js'; 10 | import it from './it/index.js'; 11 | import ru from './ru/index.js'; 12 | 13 | const localeTranslations = { 14 | de, 15 | en, 16 | it, 17 | ru 18 | }; 19 | 20 | export const loadLocale = (locale: Locales): void => { 21 | if (loadedLocales[locale]) return; 22 | 23 | loadedLocales[locale] = localeTranslations[locale] as unknown as Translations; 24 | loadFormatters(locale); 25 | }; 26 | 27 | export const loadAllLocales = (): void => locales.forEach(loadLocale); 28 | 29 | export const loadFormatters = (locale: Locales): void => 30 | void (loadedFormatters[locale] = initFormatters(locale)); 31 | -------------------------------------------------------------------------------- /src/i18n/i18n-util.ts: -------------------------------------------------------------------------------- 1 | // This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. 2 | /* eslint-disable */ 3 | 4 | import { 5 | i18n as initI18n, 6 | i18nObject as initI18nObject, 7 | i18nString as initI18nString 8 | } from 'typesafe-i18n'; 9 | import type { LocaleDetector } from 'typesafe-i18n/detectors'; 10 | import type { LocaleTranslationFunctions, TranslateByString } from 'typesafe-i18n'; 11 | import { detectLocale as detectLocaleFn } from 'typesafe-i18n/detectors'; 12 | import { initExtendDictionary } from 'typesafe-i18n/utils'; 13 | import type { Formatters, Locales, Translations, TranslationFunctions } from './i18n-types.js'; 14 | 15 | export const baseLocale: Locales = 'en'; 16 | 17 | export const locales: Locales[] = ['de', 'en', 'it', 'ru']; 18 | 19 | export const isLocale = (locale: string): locale is Locales => locales.includes(locale as Locales); 20 | 21 | export const loadedLocales: Record = {} as Record; 22 | 23 | export const loadedFormatters: Record = {} as Record; 24 | 25 | export const extendDictionary = initExtendDictionary(); 26 | 27 | export const i18nString = (locale: Locales): TranslateByString => 28 | initI18nString(locale, loadedFormatters[locale]); 29 | 30 | export const i18nObject = (locale: Locales): TranslationFunctions => 31 | initI18nObject( 32 | locale, 33 | loadedLocales[locale], 34 | loadedFormatters[locale] 35 | ); 36 | 37 | export const i18n = (): LocaleTranslationFunctions => 38 | initI18n( 39 | loadedLocales, 40 | loadedFormatters 41 | ); 42 | 43 | export const detectLocale = (...detectors: LocaleDetector[]): Locales => 44 | detectLocaleFn(baseLocale, locales, ...detectors); 45 | -------------------------------------------------------------------------------- /src/i18n/it/index.ts: -------------------------------------------------------------------------------- 1 | import en from '$i18n/en/index.js'; 2 | import type { Translation } from '../i18n-types.js'; 3 | 4 | const it = { 5 | // Fallback 6 | ...(en as Translation), 7 | 8 | log: "Questa protocollazione è stata chiamata da '{fileName}'", 9 | site: { 10 | name: 'Localize URL' 11 | }, 12 | pageHome: { 13 | title: 'Pagina iniziale', 14 | welcome: 'Benvenuti nella home page', 15 | apiDescription: 'Chiamate API che non sono interessate dalla libreria', 16 | spectators: '{0} {{spettatore|spettatori}} in diretta', 17 | legend: { 18 | title: 'Leggenda', 19 | correct: 'Collegamento corretto', 20 | fallback: "Collegamento di fallback (conduce a un'altra località)", 21 | vary: 'Collegamento corretto (visibile solo per le impostazioni locali supportate)', 22 | redirect: 'Collegamento non corrispondente, reindirizzamento', 23 | error: 'Non disponibile per questa lingua' 24 | } 25 | }, 26 | pageAbout: { 27 | title: 'Chi siamo', 28 | text: 'La pagina Chi siamo saluta.' 29 | }, 30 | pageEnglish: { 31 | title: 'Pagina solo in inglese', 32 | text: '' 33 | }, 34 | pageNews: { 35 | title: 'Notizia' 36 | }, 37 | pageError: { 38 | title404: 'Pagina non trovata', 39 | titleOffline: 'Sembra che tu sia offline', 40 | titleOther: 'Accidenti!', 41 | message404: 'Spiacenti, non siamo riusciti a trovare la pagina richiesta.', 42 | messageNotTranslated: 'Siamo spiacenti, questa pagina non è disponibile nella tua lingua.', 43 | messageOffline: 'Ricarica la pagina dopo aver trovato Internet.', 44 | messageOther: 'Prova a ricaricare la pagina.', 45 | actionHome: 'Ritorna alla pagina iniziale', 46 | actionBaseLocale: 'Mostra questa pagina in inglese' 47 | } 48 | } satisfies Translation; 49 | 50 | export default it; 51 | -------------------------------------------------------------------------------- /src/i18n/ru/index.ts: -------------------------------------------------------------------------------- 1 | import en from '$i18n/en/index.js'; 2 | import type { Translation } from '../i18n-types.js'; 3 | 4 | const ru = { 5 | // Fallback 6 | ...(en as Translation), 7 | 8 | log: "Этот лог был вызван из '{fileName}'", 9 | site: { 10 | name: 'Localize URL' 11 | }, 12 | pageHome: { 13 | title: 'Главная страница', 14 | welcome: 'Добро пожаловать на главную страницу', 15 | apiDescription: 'Вызовы API, на которые не влияет библиотека', 16 | spectators: '{0} зрител{{ей|ь|я|я|ей}} онлайн', 17 | legend: { 18 | title: 'Легенда', 19 | correct: 'Правильная ссылка', 20 | fallback: 'Запасная ссылка (ведет на другой язык)', 21 | vary: 'Правильная ссылка (видна только для поддерживаемых языков)', 22 | redirect: 'Неправильно составленная ссылка, перенаправление', 23 | error: 'Недоступно для этого языка' 24 | } 25 | }, 26 | pageAbout: { 27 | title: 'О нас', 28 | text: 'Страница о нас говорит привет.' 29 | }, 30 | pageEnglish: { 31 | title: 'Только английская версия', 32 | text: '' 33 | }, 34 | pageNews: { 35 | title: 'Новости' 36 | }, 37 | pageError: { 38 | title404: 'Страница не найдена', 39 | titleOffline: 'Похоже, вы не в сети', 40 | titleOther: 'Ой!', 41 | message404: 'К сожалению, мы не смогли найти запрошенную страницу.', 42 | messageNotTranslated: 'К сожалению, эта страница недоступна на вашем языке.', 43 | messageOffline: 'Перезагрузите страницу, как только ваше соединение восстановится.', 44 | messageOther: 'Пожалуйста, попробуйте перезагрузить страницу.', 45 | actionHome: 'Вернуться на главную страницу', 46 | actionBaseLocale: 'Показать эту страницу на английском языке' 47 | } 48 | } satisfies Translation; 49 | 50 | export default ru; 51 | -------------------------------------------------------------------------------- /src/index.test.ts: -------------------------------------------------------------------------------- 1 | import { afterEach, describe, expect, it, vi } from 'vitest'; 2 | import { 3 | PageCheckResult, 4 | buildAlternateLinks, 5 | checkPage, 6 | getLocaleFromUrl, 7 | getLocaleHomeHref, 8 | getPageParams, 9 | href, 10 | initLocalizeUrl, 11 | localizePage, 12 | localizeParam, 13 | matchParam, 14 | resetSettings, 15 | setPageParam, 16 | validatePage, 17 | wrapHref 18 | } from '$lib/index.js'; 19 | import { HttpError, Redirect } from '@sveltejs/kit'; 20 | 21 | const initDefault = () => { 22 | initLocalizeUrl({ 23 | baseLocale: 'en', 24 | locales: ['en', 'ru'] 25 | }); 26 | }; 27 | 28 | const initBasePrefix = () => { 29 | initLocalizeUrl({ 30 | baseLocale: 'en', 31 | locales: ['en', 'ru'], 32 | addBaseLocaleToPath: true 33 | }); 34 | }; 35 | 36 | const initHosts = () => { 37 | initLocalizeUrl({ 38 | baseLocale: 'en', 39 | locales: ['en', 'ru', 'it'], 40 | localeHosts: { 41 | en: 'foo.com', 42 | ru: 'foo.ru' 43 | } 44 | }); 45 | }; 46 | 47 | console.error = () => { 48 | // Suppress console.error messages 49 | }; 50 | // But track when it's called 51 | const consoleError = vi.spyOn(console, 'error'); 52 | 53 | describe('initLocalizeUrl', () => { 54 | afterEach(() => { 55 | vi.clearAllMocks(); 56 | resetSettings(); 57 | }); 58 | it('with prefix except for the base locale', () => { 59 | initDefault(); 60 | expect(getLocaleHomeHref('en', new URL('http://foo.com'))).toBe('http://foo.com/'); 61 | expect(getLocaleHomeHref('ru', new URL('http://foo.com'))).toBe('http://foo.com/ru'); 62 | }); 63 | it('with prefix for every locale', () => { 64 | initBasePrefix(); 65 | expect(getLocaleHomeHref('en', new URL('http://foo.com'))).toBe('http://foo.com/en'); 66 | expect(getLocaleHomeHref('ru', new URL('http://foo.com'))).toBe('http://foo.com/ru'); 67 | }); 68 | it('with separate hosts per locale', () => { 69 | initHosts(); 70 | expect(getLocaleHomeHref('en', new URL('http://foo.com'))).toBe('http://foo.com/'); 71 | expect(getLocaleHomeHref('ru', new URL('http://foo.com'))).toBe('http://foo.ru/'); 72 | expect(getLocaleHomeHref('it', new URL('http://foo.com'))).toBe('http://foo.com/it'); 73 | }); 74 | it('with separate hosts per locale, but without baseLocale host throws', () => { 75 | expect(() => { 76 | initLocalizeUrl({ 77 | baseLocale: 'en', 78 | locales: ['en', 'ru'], 79 | localeHosts: { 80 | ru: 'foo.ru' 81 | } 82 | }); 83 | }).toThrowError(); 84 | }); 85 | it('is processed only once', () => { 86 | initLocalizeUrl({ 87 | baseLocale: 'en', 88 | locales: ['en', 'ru'], 89 | addBaseLocaleToPath: true 90 | }); 91 | initLocalizeUrl({ 92 | baseLocale: 'en', 93 | locales: ['en', 'ru'], 94 | addBaseLocaleToPath: false 95 | }); 96 | expect(getLocaleHomeHref('en', new URL('http://foo.com/'))).toBe('http://foo.com/en'); 97 | }); 98 | }); 99 | 100 | describe('getLocaleFromUrl', () => { 101 | afterEach(() => { 102 | vi.clearAllMocks(); 103 | resetSettings(); 104 | }); 105 | it('from pathname prefix', () => { 106 | initDefault(); 107 | expect(getLocaleFromUrl(new URL('http://foo.com/'))).toBe('en'); 108 | expect(getLocaleFromUrl(new URL('http://foo.com/en'))).toBe('en'); 109 | expect(getLocaleFromUrl(new URL('http://foo.com/ru'))).toBe('ru'); 110 | expect(getLocaleFromUrl(new URL('http://foo.com/zz'))).toBe('en'); 111 | }); 112 | it('from pathname prefix or host', () => { 113 | initHosts(); 114 | expect(getLocaleFromUrl(new URL('http://foo.com/'))).toBe('en'); 115 | expect(getLocaleFromUrl(new URL('http://foo.com/en'))).toBe('en'); 116 | expect(getLocaleFromUrl(new URL('http://foo.com/ru'))).toBe('ru'); 117 | expect(getLocaleFromUrl(new URL('http://foo.ru/'))).toBe('ru'); 118 | expect(getLocaleFromUrl(new URL('http://foo.ru/en'))).toBe('en'); 119 | expect(getLocaleFromUrl(new URL('http://foo.com/it'))).toBe('it'); 120 | }); 121 | }); 122 | 123 | describe('localizeParam', () => { 124 | afterEach(() => { 125 | vi.clearAllMocks(); 126 | resetSettings(); 127 | initDefault(); 128 | }); 129 | it('works', () => { 130 | const localizedParam = localizeParam(1, 'l_about', { 131 | en: 'about-us', 132 | ru: 'o-nas' 133 | }); 134 | expect(matchParam('about-us', localizedParam)).toBeTruthy(); 135 | expect(matchParam('o-nas', localizedParam)).toBeTruthy(); 136 | expect(matchParam('about', localizedParam)).toBeFalsy(); 137 | }); 138 | }); 139 | 140 | describe('localizePage', () => { 141 | afterEach(() => { 142 | vi.clearAllMocks(); 143 | resetSettings(); 144 | initDefault(); 145 | }); 146 | it('catches route params', () => { 147 | const LP = localizePage( 148 | new URL('http://foo.com/about/fixed/bar'), 149 | '/[[lang=lang]]/[l_about=l_about]/fixed/[[optional]]' 150 | ); 151 | expect(LP._paramPositions).toMatchObject( 152 | new Map([ 153 | ['l_about', 1], 154 | ['optional', 3] 155 | ]) 156 | ); 157 | }); 158 | it('accepts custom params', () => { 159 | const LP = localizePage( 160 | new URL('http://foo.com/news/first-post'), 161 | '/[[lang=lang]]/[l_news=l_news]/[post]' 162 | ); 163 | setPageParam(LP, 'post', { 164 | en: 'first-post', 165 | ru: 'pervyi-post' 166 | }); 167 | const param = { 168 | name: 'post', 169 | position: 2, 170 | values: { 171 | en: 'first-post', 172 | ru: 'pervyi-post' 173 | } 174 | }; 175 | expect(LP._customParams).toMatchObject(new Map([['post', param]])); 176 | expect(getPageParams(LP, true)).toMatchObject([param]); 177 | expect(consoleError).toHaveBeenCalledTimes(0); 178 | }); 179 | it('prints an error when given unused custom params', () => { 180 | const LP = localizePage(new URL('http://foo.com/about'), '/[[lang=lang]]'); 181 | setPageParam(LP, 'post', { 182 | en: 'first-post', 183 | ru: 'pervyi-post' 184 | }); 185 | expect(consoleError).toHaveBeenCalledOnce(); 186 | }); 187 | }); 188 | 189 | describe('href', () => { 190 | afterEach(() => { 191 | vi.clearAllMocks(); 192 | resetSettings(); 193 | }); 194 | it('supports basic pathnames', () => { 195 | initDefault(); 196 | const LP = localizePage(new URL('http://foo.com/'), '/[[lang=lang]]'); 197 | expect(href(LP, 'ru', '/')).toBe('http://foo.com/ru'); 198 | expect(consoleError).toHaveBeenCalledTimes(0); 199 | }); 200 | it('supports localized params', () => { 201 | initDefault(); 202 | localizeParam(1, 'l_about', { 203 | en: 'about-us', 204 | ru: 'o-nas' 205 | }); 206 | const LP = localizePage(new URL('http://foo.com/'), '/[[lang=lang]]'); 207 | expect(href(LP, 'ru', '/[l_about]')).toBe('http://foo.com/ru/o-nas'); 208 | expect(consoleError).toHaveBeenCalledTimes(0); 209 | }); 210 | it('supports custom params', () => { 211 | initDefault(); 212 | localizeParam(1, 'l_news', { 213 | en: 'news', 214 | ru: 'novosti' 215 | }); 216 | const LP = localizePage( 217 | new URL('http://foo.com/ru/novosti/pervyi-post'), 218 | '/[[lang=lang]]/[l_news=l_news]/[post]' 219 | ); 220 | setPageParam(LP, 'post', { 221 | en: 'first-post', 222 | ru: 'pervyi-post' 223 | }); 224 | expect(href(LP, 'en', '/[l_news]/[post]/edit')).toBe('http://foo.com/news/first-post/edit'); 225 | expect( 226 | href(LP, 'en', { 227 | base: '/replaced/replaced/edit', 228 | params: ['l_news', 'post'] 229 | }) 230 | ).toBe('http://foo.com/news/first-post/edit'); 231 | expect(consoleError).toHaveBeenCalledTimes(0); 232 | }); 233 | it("prints an error when pathname param isn't registered", () => { 234 | initDefault(); 235 | const LP = localizePage(new URL('http://foo.com/'), '/[[lang=lang]]'); 236 | expect(href(LP, 'ru', '/[post]')).toBe(''); 237 | expect( 238 | href(LP, 'ru', { 239 | base: '/ru/anything', 240 | params: ['l_none'] 241 | }) 242 | ).toBe(''); 243 | expect(consoleError).toHaveBeenCalledTimes(2); 244 | }); 245 | it('prints an error when pathname param is in the wrong position', () => { 246 | initDefault(); 247 | localizeParam(1, 'l_about', { 248 | en: 'about-us', 249 | ru: 'o-nas' 250 | }); 251 | const LP = localizePage(new URL('http://foo.com/'), '/[[lang=lang]]'); 252 | expect(href(LP, 'ru', '/bar/[l_about]')).toBe(''); 253 | expect(consoleError).toHaveBeenCalledOnce(); 254 | }); 255 | it('supports fallback', () => { 256 | initDefault(); 257 | localizeParam(1, 'l_partial', { 258 | en: 'partial', 259 | de: 'teilweise' 260 | }); 261 | const LP = localizePage(new URL('http://foo.com/'), '/[[lang=lang]]'); 262 | expect(href(LP, 'ru', '/[l_partial]')).toBe(''); 263 | expect(href(LP, 'ru', '/[l_partial]', true)).toBe('http://foo.com/partial'); 264 | expect(href(LP, 'ru', '/[l_partial]', 'de')).toBe('http://foo.com/de/teilweise'); 265 | expect(href(LP, 'ru', '/[l_partial]', ['it', 'de'])).toBe('http://foo.com/de/teilweise'); 266 | expect(consoleError).toHaveBeenCalledTimes(0); 267 | }); 268 | it('supports locale hosts', () => { 269 | initHosts(); 270 | localizeParam(1, 'l_about', { 271 | en: 'about-us', 272 | ru: 'o-nas', 273 | it: 'chi-siamo' 274 | }); 275 | const LP = localizePage(new URL('http://foo.com/'), '/[[lang=lang]]'); 276 | expect(href(LP, 'en', '/[l_about]')).toBe('http://foo.com/about-us'); 277 | expect(href(LP, 'ru', '/[l_about]')).toBe('http://foo.ru/o-nas'); 278 | expect(href(LP, 'it', '/[l_about]')).toBe('http://foo.com/it/chi-siamo'); 279 | expect(consoleError).toHaveBeenCalledTimes(0); 280 | }); 281 | }); 282 | 283 | describe('wrapHref', () => { 284 | afterEach(() => { 285 | vi.clearAllMocks(); 286 | resetSettings(); 287 | }); 288 | it('works', () => { 289 | initDefault(); 290 | localizeParam(1, 'l_about', { 291 | en: 'about-us' 292 | }); 293 | const LP = localizePage(new URL('http://foo.com/'), '/[[lang=lang]]'); 294 | expect(wrapHref(LP, 'en', '/[l_about]', 'test')).toBe( 295 | 'test' 296 | ); 297 | expect(wrapHref(LP, 'ru', '/[l_about]', 'test')).toBe(''); 298 | expect(consoleError).toHaveBeenCalledTimes(0); 299 | }); 300 | }); 301 | 302 | describe('buildAlternateLinks', () => { 303 | afterEach(() => { 304 | vi.clearAllMocks(); 305 | resetSettings(); 306 | }); 307 | it('builds hreflang and switcher when page has no errors', () => { 308 | initDefault(); 309 | localizeParam(1, 'l_about', { 310 | en: 'about-us', 311 | ru: 'o-nas' 312 | }); 313 | const LP = localizePage(new URL('http://foo.com/ru/o-nas'), '/[[lang=lang]]/[l_about]'); 314 | buildAlternateLinks(LP); 315 | expect(consoleError).toHaveBeenCalledTimes(0); 316 | const expected = new Map([ 317 | ['en', 'http://foo.com/about-us'], 318 | ['ru', 'http://foo.com/ru/o-nas'] 319 | ]); 320 | expect(LP.hreflang).toMatchObject(expected); 321 | expect(LP.switcher).toMatchObject(expected); 322 | }); 323 | it('builds only switcher for pages that have an error', () => { 324 | initDefault(); 325 | localizeParam(1, 'l_about', { 326 | en: 'about-us', 327 | ru: 'o-nas' 328 | }); 329 | const LP = localizePage(new URL('http://foo.com/ru/o-nas'), '/[[lang=lang]]/[l_about]'); 330 | buildAlternateLinks(LP, true); 331 | expect(LP.hreflang).toMatchObject(new Map()); 332 | expect(LP.switcher).toMatchObject( 333 | new Map([ 334 | ['en', 'http://foo.com/'], 335 | ['ru', 'http://foo.com/ru'] 336 | ]) 337 | ); 338 | expect(consoleError).toHaveBeenCalledTimes(0); 339 | }); 340 | it('builds partial hreflang and switcher with homepage fallback for partially localized params', () => { 341 | initDefault(); 342 | localizeParam(1, 'l_about', { 343 | en: 'about-us' 344 | }); 345 | const LP = localizePage(new URL('http://foo.com/ru/o-nas'), '/[[lang=lang]]/[l_about]'); 346 | buildAlternateLinks(LP); 347 | expect(LP.hreflang).toMatchObject(new Map([['en', 'http://foo.com/about-us']])); 348 | expect(LP.switcher).toMatchObject( 349 | new Map([ 350 | ['en', 'http://foo.com/about-us'], 351 | ['ru', 'http://foo.com/ru'] 352 | ]) 353 | ); 354 | expect(consoleError).toHaveBeenCalledTimes(0); 355 | }); 356 | }); 357 | 358 | describe('checkPage', () => { 359 | afterEach(() => { 360 | vi.clearAllMocks(); 361 | resetSettings(); 362 | }); 363 | it('handles correct URLs', () => { 364 | initDefault(); 365 | localizeParam(1, 'l_about', { 366 | en: 'about-us', 367 | ru: 'o-nas' 368 | }); 369 | const LP = localizePage(new URL('http://foo.com/ru/o-nas'), '/[[lang=lang]]/[l_about]'); 370 | expect(checkPage(LP, 'ru')).toMatchObject([PageCheckResult.Success]); 371 | expect(consoleError).toHaveBeenCalledTimes(0); 372 | }); 373 | it('handles mismatched URLs', () => { 374 | initDefault(); 375 | localizeParam(1, 'l_about', { 376 | en: 'about-us', 377 | ru: 'o-nas' 378 | }); 379 | const LP = localizePage(new URL('http://foo.com/ru/about-us'), '/[[lang=lang]]/[l_about]'); 380 | expect(checkPage(LP, 'ru')).toMatchObject([ 381 | PageCheckResult.Corrected, 382 | 'http://foo.com/ru/o-nas' 383 | ]); 384 | expect(consoleError).toHaveBeenCalledTimes(0); 385 | }); 386 | it('handles not localizeable URLs', () => { 387 | initDefault(); 388 | localizeParam(1, 'l_about', { 389 | en: 'about-us' 390 | }); 391 | const LP = localizePage(new URL('http://foo.com/ru/about-us'), '/[[lang=lang]]/[l_about]'); 392 | expect(checkPage(LP, 'ru')).toMatchObject([PageCheckResult.NotLocalized]); 393 | expect(consoleError).toHaveBeenCalledTimes(0); 394 | }); 395 | it('handles URLs with search', () => { 396 | initDefault(); 397 | localizeParam(1, 'l_about', { 398 | en: 'about-us', 399 | ru: 'o-nas' 400 | }); 401 | let LP = localizePage( 402 | new URL('http://foo.com/ru/o-nas?foo=bar'), 403 | '/[[lang=lang]]/[l_about]' 404 | ); 405 | expect(checkPage(LP, 'ru')).toMatchObject([PageCheckResult.Success]); 406 | LP = localizePage(new URL('http://foo.com/ru/about?foo=bar'), '/[[lang=lang]]/[l_about]'); 407 | expect(checkPage(LP, 'ru')).toMatchObject([ 408 | PageCheckResult.Corrected, 409 | 'http://foo.com/ru/o-nas?foo=bar' 410 | ]); 411 | expect(consoleError).toHaveBeenCalledTimes(0); 412 | }); 413 | }); 414 | 415 | describe('validatePage', () => { 416 | afterEach(() => { 417 | vi.clearAllMocks(); 418 | resetSettings(); 419 | }); 420 | it('handles correct URLs', () => { 421 | initDefault(); 422 | localizeParam(1, 'l_about', { 423 | en: 'about-us', 424 | ru: 'o-nas' 425 | }); 426 | const LP = localizePage(new URL('http://foo.com/ru/o-nas'), '/[[lang=lang]]/[l_about]'); 427 | validatePage(LP); 428 | expect(consoleError).toHaveBeenCalledTimes(0); 429 | }); 430 | // it('handles mismatched URLs', () => { 431 | // initDefault(); 432 | // localizeParam(1, 'l_about', { 433 | // en: 'about-us', 434 | // ru: 'o-nas' 435 | // }); 436 | // const LP = localizePage(new URL('http://foo.com/ru/about-us'), '/[[lang=lang]]/[l_about]'); 437 | // expect(consoleError).toHaveBeenCalledTimes(0); 438 | // expect(validatePage(LP)).toThrow(Redirect); 439 | // }); 440 | // it('handles not localizeable URLs', () => { 441 | // initDefault(); 442 | // localizeParam(1, 'l_about', { 443 | // en: 'about-us' 444 | // }); 445 | // const LP = localizePage(new URL('http://foo.com/ru/about-us'), '/[[lang=lang]]/[l_about]'); 446 | // expect(consoleError).toHaveBeenCalledTimes(0); 447 | // expect(validatePage(LP)).toThrow(HttpError); 448 | // }); 449 | }); 450 | -------------------------------------------------------------------------------- /src/lib/index.ts: -------------------------------------------------------------------------------- 1 | import { error, redirect } from '@sveltejs/kit'; 2 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 3 | import type { HttpError, Redirect } from '@sveltejs/kit'; 4 | 5 | export type LocalizeUrlSettings = { 6 | /** 7 | * Default locale. 8 | */ 9 | baseLocale: string; 10 | /** 11 | * Enabled locales. 12 | */ 13 | locales: string[]; 14 | /** 15 | * If true, the correct pathname for `/page` is `/baseLocale/page`. 16 | */ 17 | addBaseLocaleToPath?: boolean; 18 | /** 19 | * Locales matched by hosts. If specified, the correct path for `example.com/de/page` is `example.de/page`. 20 | */ 21 | localeHosts?: Partial>; 22 | }; 23 | 24 | const settings: LocalizeUrlSettings & { 25 | /** 26 | * Registered localized params. 27 | */ 28 | paramRegistry: Map; 29 | isInitialized: boolean; 30 | } = { 31 | baseLocale: '', 32 | locales: [], 33 | paramRegistry: new Map(), 34 | isInitialized: false 35 | }; 36 | 37 | /** 38 | * Initializes the library. 39 | * @param baseLocale Default locale. 40 | * @param locales Enabled locales. 41 | * @param addBaseLocaleToPath If true, the correct pathname for `/page` is `/baseLocale/page`. 42 | * @param localeHosts Locales matched by hosts. If specified, the correct path for 43 | * `example.com/de/page` is `example.de/page`. 44 | */ 45 | export function initLocalizeUrl(newSettings: LocalizeUrlSettings) { 46 | if (settings.isInitialized) return; 47 | 48 | if (newSettings.localeHosts) { 49 | if (!(newSettings.baseLocale in newSettings.localeHosts)) { 50 | throw new Error( 51 | `Host for the base locale "${newSettings.baseLocale}" is not specified` 52 | ); 53 | } 54 | newSettings.addBaseLocaleToPath = false; 55 | } 56 | Object.assign(settings, newSettings); 57 | settings.isInitialized = true; 58 | } 59 | 60 | /** 61 | * For testing only 62 | */ 63 | export function resetSettings() { 64 | settings.baseLocale = ''; 65 | settings.locales = []; 66 | settings.paramRegistry.clear(); 67 | settings.isInitialized = false; 68 | delete settings.addBaseLocaleToPath; 69 | delete settings.localeHosts; 70 | } 71 | 72 | /** ==== Helpers ==== */ 73 | 74 | const isLocale = (locale: string) => settings.locales.includes(locale); 75 | 76 | /** 77 | * Detects URL locale based on the pathname or the host (if `localeHosts` was specified during init). 78 | */ 79 | export function getLocaleFromUrl(url: URL): string { 80 | const [, lang] = url.pathname.split('/'); 81 | if (isLocale(lang)) return lang; 82 | 83 | if (settings.localeHosts) { 84 | for (const [locale, host] of Object.entries(settings.localeHosts)) { 85 | if (host === url.host) { 86 | return locale; 87 | } 88 | } 89 | } 90 | return settings.baseLocale; 91 | } 92 | 93 | /** 94 | * Builds a URL href for a home page in a specified locale. 95 | * @param currentUrl Current URL to use as a base. 96 | * @param locale Desired locale. 97 | * @param leaveSearch If true, search and hash won't be removed. 98 | */ 99 | export function getLocaleHomeHref(locale: string, currentUrl: URL, leaveSearch = false): string { 100 | const url = new URL(currentUrl); 101 | url.pathname = '/'; 102 | if (!leaveSearch) { 103 | url.search = ''; 104 | url.hash = ''; 105 | } 106 | if (settings.localeHosts) { 107 | const host = settings.localeHosts[locale]; 108 | if (host) { 109 | // url leads to another domain 110 | url.host = host; 111 | return url.href; 112 | } 113 | } 114 | // add locale prefix 115 | if (settings.addBaseLocaleToPath || settings.baseLocale !== locale) { 116 | url.pathname = `/${locale}`; 117 | } 118 | return url.href; 119 | } 120 | 121 | /** ==== Param ==== */ 122 | 123 | export type LocalizedParam = { 124 | /** 125 | * Position in the pathname. 126 | */ 127 | position: number; 128 | /** 129 | * Param name. 130 | */ 131 | name: string; 132 | /** 133 | * Locales matched by paths. 134 | */ 135 | values: Partial>; 136 | }; 137 | 138 | /** 139 | * Registers localized param. 140 | * @param position Param segment position in the URL starting with 1. 141 | * @param name Unique param name that matches router param name. 142 | * @param values Supported locales and their corresponding values. 143 | * @returns Localized param. 144 | * @example 145 | * ```typescript 146 | * // src/params/l_faq.ts - for URL: /[l_about]/[l_faq] 147 | * const localizedParam = localizeParam(2, 'l_faq', { 148 | * en: 'faq', 149 | * ru: 'voprosi-i-otveti' 150 | * }) 151 | * ``` 152 | */ 153 | export function localizeParam( 154 | position: number, 155 | name: string, 156 | values: Partial> 157 | ): LocalizedParam { 158 | const result: LocalizedParam = { 159 | name, 160 | position, 161 | values 162 | }; 163 | settings.paramRegistry.set(name, result); 164 | return result; 165 | } 166 | 167 | /** 168 | * Checks if the route param value matches any of the localized param values. 169 | * @example 170 | * ```typescript 171 | * export const match: ParamMatcher = (param) => matchParam(param, localizedParam) 172 | * ``` 173 | */ 174 | export function matchParam(routeParamValue: string, param: LocalizedParam): boolean { 175 | return Object.values(param.values).includes(routeParamValue); 176 | } 177 | 178 | /** ==== Page ==== */ 179 | 180 | export type LocalizedPage = { 181 | /** 182 | * Localized URL hrefs by locale for `; 186 | /** 187 | * Localized URL hrefs by locale for a language switcher. 188 | * If a href doesn't exist for the locale, it will default to a home page href (`/de`). 189 | */ 190 | switcher: Map; 191 | /** 192 | * Current page URL. 193 | */ 194 | _url: URL; 195 | /** 196 | * Matched route id. 197 | */ 198 | _routeId: string | null; 199 | /** 200 | * Route params and their positions in the pathname. 201 | */ 202 | _paramPositions: Map; 203 | /** 204 | * Custom page-specific params like post slugs. 205 | */ 206 | _customParams: Map; 207 | /** 208 | * Alternate hrefs need to be rebuilt. 209 | */ 210 | _isDirty: boolean; 211 | /** 212 | * If the {@link checkPage} function marked that this page is not available for a requested locale. 213 | */ 214 | _isNotAvailableForLocale: boolean; 215 | }; 216 | 217 | /** 218 | * Builds new localized page object. 219 | * @param url Current URL. 220 | * @param routeId Matched route id. 221 | */ 222 | export function localizePage(url: URL, routeId: string | null): LocalizedPage { 223 | // find matched route params and their positions 224 | const paramPositions = new Map(); 225 | if (routeId) { 226 | const parts = routeId.split('/'); 227 | for (let pos = 2; pos < parts.length; pos++) { 228 | const part = parts[pos]; 229 | if (part[0] !== '[') continue; 230 | 231 | let name = part[1] === '[' ? part.slice(2, -2) : part.slice(1, -1); 232 | name = name.split('=')[0]; 233 | paramPositions.set(name, pos - 1); 234 | } 235 | } 236 | return { 237 | hreflang: new Map(), 238 | switcher: new Map(), 239 | _url: new URL(url), 240 | _routeId: routeId, 241 | _paramPositions: paramPositions, 242 | _customParams: new Map(), 243 | _isDirty: true, 244 | _isNotAvailableForLocale: false 245 | }; 246 | } 247 | 248 | /** 249 | * Set locales and matching values for dynamic params that depend on the server (like post slugs). 250 | * @param values Locales matched by values. Doesn't have to include all locales. 251 | * @example 252 | * ```typescript 253 | * // Route: /[l_news]/[category]/[post]. [category] and [post] are used to fetch an article from DB 254 | * setPageParam(localizedPage, 'category', post.category.slugs); 255 | * setPageParam(localizedPage, 'post', post.slugs); 256 | * ``` 257 | */ 258 | export function setPageParam( 259 | page: LocalizedPage, 260 | name: string, 261 | values: Partial> 262 | ) { 263 | const position = page._paramPositions.get(name); 264 | if (!position) { 265 | console.error( 266 | `svelte-localize-url: Param "${name}" isn't used at the route "${page._routeId}"` 267 | ); 268 | return; 269 | } 270 | 271 | page._customParams.set(name, { 272 | position, 273 | name, 274 | values 275 | }); 276 | page._isDirty = true; 277 | } 278 | 279 | /** 280 | * Retrieves page params (localized and optionally custom). 281 | * @param page Localized page. 282 | * @param includeCustom If true, includes custom params such as slugs. 283 | */ 284 | export function getPageParams(page: LocalizedPage, includeCustom = false): LocalizedParam[] { 285 | const params: LocalizedParam[] = []; 286 | for (const [name] of page._paramPositions) { 287 | let param = settings.paramRegistry.get(name); 288 | if (!param && includeCustom) { 289 | param = page._customParams.get(name); 290 | } 291 | if (param) { 292 | params.push(param); 293 | } 294 | } 295 | return params; 296 | } 297 | 298 | /** ==== Paths ==== */ 299 | 300 | /** 301 | * Attempts to create a new URL href for the desired locale. 302 | * @param page Localized page. 303 | * @param locale Desired locale. 304 | * @param pathname A new pathname to be localized. Can include localized parameters `[l_param]`. 305 | * @param fallbackLocales If specified, when one of the parameters doesn't support requested locale, 306 | * the function will try to generate a href for the fallback locale instead. 307 | * - `true` - use `baseLocale` as a fallback; 308 | * - `'locale'` - use one locale as a fallback; 309 | * - `['locale1', 'locale2', ]` - use multiple fallback locales. 310 | * @returns Localized href or an empty string if that is not possible. 311 | * @example 312 | * ```typescript 313 | * const pathFAQ = href(localizedPage, 'ru', '/[l_about]/[l_faq]') 314 | * const pathNewsPost = href(localizedPage, 'ru', '/[l_news]/[category]/[post]', ['en', 'de']) 315 | * ``` 316 | */ 317 | export function href( 318 | page: LocalizedPage, 319 | locale: string, 320 | pathname: string, 321 | fallbackLocales?: boolean | string | string[] 322 | ): string; 323 | 324 | /** 325 | * Attempts to transform a base pathname, replacing its parts according to the params. 326 | * @param page Localized page. 327 | * @param locale Desired locale. 328 | * @param transform Contents: 329 | * - `base` - An old pathname (shouldn't include any excplicitly stated `[l_param]` params). 330 | * - `params` - A list of localized params or their names that should be used. 331 | * @param fallbackLocales If specified, when one of the parameters doesn't support requested locale, 332 | * the function will try to generate a href for the fallback locale instead. 333 | * - `true` - use `baseLocale` as a fallback; 334 | * - `'locale'` - use one locale as a fallback; 335 | * - `['locale1', 'locale2', ]` - use multiple fallback locales. 336 | * @returns Transformed localized href or an empty string if that is not possible. 337 | * @example 338 | * ```typescript 339 | * const pathFAQ = href(localizedPage, 'ru', { 340 | * base: url.pathname, 341 | * params: ['l_about', 'l_faq'] 342 | * }) 343 | * ``` 344 | */ 345 | export function href( 346 | page: LocalizedPage, 347 | locale: string, 348 | transform: { 349 | base: string; 350 | params: (LocalizedParam | string)[]; 351 | }, 352 | fallbackLocales?: boolean | string | string[] 353 | ): string; 354 | 355 | export function href( 356 | page: LocalizedPage, 357 | locale: string, 358 | pathData: 359 | | string 360 | | { 361 | base: string; 362 | params: (LocalizedParam | string)[]; 363 | }, 364 | fallbackLocales?: boolean | string | string[] 365 | ): string; 366 | 367 | export function href( 368 | page: LocalizedPage, 369 | locale: string, 370 | pathData: 371 | | string 372 | | { 373 | base: string; 374 | params: (LocalizedParam | string)[]; 375 | }, 376 | fallbackLocale: boolean | string | string[] = false 377 | ): string { 378 | let parts: string[]; 379 | let usedParams: (LocalizedParam | string)[]; 380 | if (typeof pathData === 'string') { 381 | // new pathname 382 | usedParams = []; 383 | parts = pathData.split('/'); 384 | 385 | // find params that are used in the pathname 386 | for (let i = 1; i < parts.length; i++) { 387 | const part = parts[i]; 388 | if (part[0] !== '[') continue; 389 | 390 | const name = part.slice(1, -1); 391 | let param = settings.paramRegistry.get(name); 392 | if (!param) { 393 | param = page._customParams.get(name); 394 | } 395 | if (!param) { 396 | console.error( 397 | `svelte-localize-url: Param "${name}" that is inferred from the path "${pathData}"` + 398 | ` doesn't exist at the route "${page._routeId}"` 399 | ); 400 | return ''; 401 | } 402 | if (param.position !== i) { 403 | console.error( 404 | `svelte-localize-url: Param "${name}" that is inferred from the path "${pathData}"` + 405 | ` is in the wrong position ${i} (${param.position} expected) at the route "${page._routeId}"` 406 | ); 407 | return ''; 408 | } 409 | 410 | usedParams.push(param); 411 | } 412 | } else { 413 | // build on top of a base pathname 414 | parts = pathData.base.split('/'); 415 | usedParams = pathData.params; 416 | } 417 | 418 | if (parts[1] === '' || isLocale(parts[1])) { 419 | // remove locale for now 420 | parts.splice(1, 1); 421 | } 422 | 423 | // replace parts of the original pathname 424 | for (const name of usedParams) { 425 | let param = typeof name === 'string' ? settings.paramRegistry.get(name) : name; 426 | if (!param) { 427 | param = page._customParams.get(name as string); 428 | } 429 | if (!param) { 430 | console.error( 431 | `svelte-localize-url: Param "${name}" doesn't exist at the route "${page._routeId}"` 432 | ); 433 | return ''; 434 | } 435 | 436 | const value = param.values[locale]; 437 | if (value) { 438 | parts[param.position] = value; 439 | continue; 440 | } 441 | 442 | // no path for this locale 443 | if (fallbackLocale) { 444 | if (fallbackLocale === true) { 445 | return href(page, settings.baseLocale, pathData); 446 | } 447 | 448 | if (typeof fallbackLocale === 'string') { 449 | return href(page, fallbackLocale, pathData); 450 | } 451 | 452 | for (const fallback of fallbackLocale) { 453 | if (fallback === locale) continue; 454 | 455 | const fallbackHref = href(page, fallback, pathData); 456 | if (fallbackHref) { 457 | return fallbackHref; 458 | } 459 | } 460 | } 461 | 462 | return ''; 463 | } 464 | 465 | // assemble final href 466 | let pathname = parts.join('/'); 467 | // copy url without search and hash 468 | const url = new URL('/', page._url); 469 | if (settings.localeHosts) { 470 | let host = settings.localeHosts[locale]; 471 | if (!host) { 472 | // host is not specified for a locale - use the base locale host but add the locale in the pathname 473 | host = settings.localeHosts[settings.baseLocale] as string; 474 | pathname = `/${locale}${pathname}`; 475 | } 476 | url.host = host; 477 | url.pathname = pathname; 478 | return url.href; 479 | } 480 | 481 | // all locales that are on the same host to the pathname 482 | if (settings.addBaseLocaleToPath || settings.baseLocale !== locale) { 483 | pathname = `/${locale}${pathname}`; 484 | } 485 | url.pathname = pathname; 486 | return url.href; 487 | } 488 | 489 | /** 490 | * A helper function that acts similarly to the {@link href} but instead returns a string that contains 491 | * the resulting href. 492 | * @param page Localized page. 493 | * @param locale Desired locale. 494 | * @param pathname A new pathname to be localized. Can include localized parameters `[l_param]`. 495 | * @param text A text that will be returned if the path is localized successfully. 496 | * Must contain a `%href%` piece that will be replaced with the localized href. 497 | * @param fallbackLocales If specified, when one of the parameters doesn't support requested locale, 498 | * the function will try to generate a href for the fallback locale instead. 499 | * - `true` - use `baseLocale` as a fallback; 500 | * - `'locale'` - use one locale as a fallback; 501 | * - `['locale1', 'locale2', ]` - use multiple fallback locales. 502 | * @example 503 | * ```svelte 504 | * {@html wrapHref(localizedPage, 'ru', '/[l_about]/[l_faq]', `${$LL.pageFAQ()}`)} 505 | * ``` 506 | */ 507 | export function wrapHref( 508 | page: LocalizedPage, 509 | locale: string, 510 | pathname: string, 511 | text: string, 512 | fallbackLocales: boolean | string | string[] = false 513 | ): string { 514 | const localizedPath = href(page, locale, pathname, fallbackLocales); 515 | if (!localizedPath) return ''; 516 | 517 | return text.replace('%href%', localizedPath); 518 | } 519 | 520 | /** 521 | * Regenerates alternate links and returns localized page. 522 | * @param page Localized page. 523 | * @param pageError If there is a page error 524 | */ 525 | export function buildAlternateLinks( 526 | page: LocalizedPage, 527 | pageError: App.Error | boolean | null = false 528 | ): LocalizedPage { 529 | if (!page._isDirty) return page; 530 | page._isDirty = false; 531 | 532 | // get localized params and custom slugs 533 | const params = getPageParams(page, true); 534 | 535 | // regenerate alternate links 536 | page.switcher.clear(); 537 | page.hreflang.clear(); 538 | 539 | if (page._isNotAvailableForLocale || !!pageError) { 540 | // only generate language switcher links 541 | for (const locale of settings.locales) { 542 | page.switcher.set(locale, getLocaleHomeHref(locale, page._url)); 543 | } 544 | return page; 545 | } 546 | 547 | for (const locale of settings.locales) { 548 | const localizedPath = href(page, locale, { 549 | base: page._url.pathname, 550 | params 551 | }); 552 | if (localizedPath) { 553 | page.hreflang.set(locale, localizedPath); 554 | // leave search and hash in switcher for better usability 555 | const switcherUrl = new URL(localizedPath); 556 | switcherUrl.search = page._url.search; 557 | // it's better to update hash dynamically in the language switcher component 558 | // switcherUrl.hash = page._url.hash; 559 | page.switcher.set(locale, switcherUrl.href); 560 | continue; 561 | } 562 | // no path for locale, point to the home page 563 | page.switcher.set(locale, getLocaleHomeHref(locale, page._url)); 564 | } 565 | 566 | return page; 567 | } 568 | 569 | /** ==== Validation ==== */ 570 | 571 | export enum PageCheckResult { 572 | Success = 0, 573 | Corrected = 1, 574 | NotLocalized = 2 575 | } 576 | 577 | /** 578 | * Checks if the page URL is correct for a given locale. 579 | * @param page Localized page. 580 | * @param locale Current locale. 581 | * @returns Check result, optionally followed by a correct path (if redirect is needed). 582 | */ 583 | export function checkPage( 584 | page: LocalizedPage, 585 | locale: string 586 | ): [PageCheckResult.Corrected, string] | [PageCheckResult] { 587 | const correctHref = href(page, locale, { 588 | base: page._url.pathname, 589 | params: getPageParams(page, true) 590 | }); 591 | 592 | if (!correctHref) { 593 | // not available for this locale 594 | page._isNotAvailableForLocale = true; 595 | return [PageCheckResult.NotLocalized]; 596 | } 597 | 598 | const correctUrl = new URL(correctHref); 599 | if (correctUrl.host !== page._url.host || correctUrl.pathname !== page._url.pathname) { 600 | correctUrl.search = page._url.search; 601 | return [PageCheckResult.Corrected, correctUrl.href]; 602 | } 603 | 604 | return [PageCheckResult.Success]; 605 | } 606 | 607 | export type ValidatePageOptions = { 608 | /** 609 | * Default: 301. Which HTTP code to use when redirecting to a corrected localized URL. 610 | */ 611 | redirectCode?: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308; 612 | /** 613 | * Allows to specify data that will be passed with the 404 error, like a 614 | * "This page isn't available in your language" message. 615 | */ 616 | notFound?: string | App.Error; 617 | }; 618 | 619 | /** 620 | * Validates the current page URL. 621 | * Throws a 404 error if there is no version of the page for the locale. 622 | * Redirects to a corrected URL if necessary. 623 | * @param currentUrl - Current URL from the `load()` function. 624 | * @param options.redirectCode Default: 301. Which HTTP code to use when redirecting to a corrected localized URL. 625 | * @param options.notFound Allows to specify data that will be passed with the 404 error, like a 626 | * "This page isn't available in your language" message. 627 | * @see {@link HttpError}, {@link Redirect} 628 | */ 629 | export function validatePage(page: LocalizedPage, options?: ValidatePageOptions) { 630 | const locale = getLocaleFromUrl(page._url); 631 | const [status, correctPath] = checkPage(page, locale); 632 | if (status === PageCheckResult.Success) { 633 | return; 634 | } 635 | 636 | if (status === PageCheckResult.Corrected) { 637 | throw redirect(options?.redirectCode ?? 301, correctPath as string); 638 | } 639 | 640 | // not available for this locale 641 | throw error(404, options?.notFound); 642 | } 643 | -------------------------------------------------------------------------------- /src/params/l_about.ts: -------------------------------------------------------------------------------- 1 | import { localizeParam, matchParam } from '$lib/index.js'; 2 | import type { ParamMatcher } from '@sveltejs/kit'; 3 | 4 | const localizedParam = localizeParam(1, 'l_about', { 5 | en: 'about', 6 | de: 'uber-uns', 7 | it: 'chi-samo', 8 | ru: 'o-nas' 9 | }); 10 | 11 | export const match: ParamMatcher = (param) => matchParam(param, localizedParam); 12 | -------------------------------------------------------------------------------- /src/params/l_english.ts: -------------------------------------------------------------------------------- 1 | import { localizeParam, matchParam } from '$lib/index.js'; 2 | import type { ParamMatcher } from '@sveltejs/kit'; 3 | 4 | const localizedParam = localizeParam(2, 'l_english', { 5 | en: 'english-only' 6 | }); 7 | 8 | export const match: ParamMatcher = (param) => matchParam(param, localizedParam); 9 | -------------------------------------------------------------------------------- /src/params/l_news.ts: -------------------------------------------------------------------------------- 1 | import { localizeParam, matchParam } from '$lib/index.js'; 2 | import type { ParamMatcher } from '@sveltejs/kit'; 3 | 4 | const localizedParam = localizeParam(1, 'l_news', { 5 | en: 'news', 6 | de: 'nachricht', 7 | it: 'notizia', 8 | ru: 'novosti' 9 | }); 10 | 11 | export const match: ParamMatcher = (param) => matchParam(param, localizedParam); 12 | -------------------------------------------------------------------------------- /src/params/lang.ts: -------------------------------------------------------------------------------- 1 | import { isLocale } from '$i18n/i18n-util.js'; 2 | import type { ParamMatcher } from '@sveltejs/kit'; 3 | 4 | // only accept valid languages as a segment in the URL 5 | export const match: ParamMatcher = (param) => isLocale(param); 6 | -------------------------------------------------------------------------------- /src/routes/+error.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | {#if $page.status === 404} 12 |

{$LL.pageError.title404()}

13 |

14 | {#if !$page.error?.message || $page.error?.message === 'Not Found'} 15 | 16 | {$LL.pageError.message404()} 17 | {:else} 18 | {$page.error.message} 19 | {/if} 20 |

21 | {:else if !online} 22 |

{$LL.pageError.titleOffline()}

23 |

{$LL.pageError.messageOffline()}

24 | {:else} 25 |

{$LL.pageError.titleOther()}

26 |

27 | {#if !$page.error?.message || $page.error?.message === 'Internal Error'} 28 | 29 | {$LL.pageError.messageOther()} 30 | {:else} 31 | {$page.error.message} 32 | {/if} 33 |

34 | {/if} 35 | 36 | {#if $page.status === 404 || online} 37 | 38 | {#if $page.error?.link} 39 |

40 | {$page.error.linkText} 41 |

42 | {/if} 43 |

44 | {$LL.pageError.actionHome()} 45 |

46 | {/if} 47 | -------------------------------------------------------------------------------- /src/routes/+layout.server.ts: -------------------------------------------------------------------------------- 1 | import type { LayoutServerLoad } from './$types.js'; 2 | 3 | export const load: LayoutServerLoad = ({ locals: { locale, LL } }) => { 4 | console.info(LL.log({ fileName: 'routes/+layout.server.ts' })); 5 | 6 | // pass locale information from "server-context" to "shared server + client context" 7 | return { locale }; 8 | }; 9 | -------------------------------------------------------------------------------- /src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 | 19 |
20 | 21 |
22 | 23 | 28 | 29 | 30 | {$page.data.title || $LL.site.name()} 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/routes/+layout.ts: -------------------------------------------------------------------------------- 1 | import LL, { setLocale, locale as storeLocale } from '$i18n/i18n-svelte.js'; 2 | import { loadLocaleAsync } from '$i18n/i18n-util.async.js'; 3 | import { get } from 'svelte/store'; 4 | import { initLocalizeUrl, localizePage } from '$lib/index.js'; 5 | import { baseLocale, locales } from '$i18n/i18n-util.js'; 6 | import { browser } from '$app/environment'; 7 | import type { LayoutLoad } from './$types.js'; 8 | 9 | export const load: LayoutLoad = async ({ url, route, data: { locale } }) => { 10 | // initialize Localize URL library for client (already initialized on server) 11 | initLocalizeUrl({ 12 | baseLocale, 13 | locales 14 | }); 15 | 16 | /** 17 | * Make sure that we're not overriding clientside typesafe-i18n locale when preloading pages on link hover. 18 | * An alternative solution is setting `data-sveltekit-preload-data` on the `body` tag to `tap` of `off`. 19 | */ 20 | let loadLocale = true; 21 | if (browser) { 22 | const $storeLocale = get(storeLocale); 23 | if ($storeLocale !== undefined) { 24 | locale = $storeLocale; 25 | loadLocale = false; 26 | } 27 | } 28 | 29 | if (loadLocale) { 30 | // load dictionary into memory 31 | await loadLocaleAsync(locale); 32 | } 33 | 34 | // if you need to output a localized string in a `load` function, 35 | // you always need to call `setLocale` right before you access the `LL` store 36 | setLocale(locale); 37 | // get the translation functions value from the store 38 | const $LL = get(LL); 39 | console.info($LL.log({ fileName: 'routes/+layout.ts' })); 40 | 41 | /** 42 | * We need to create localized page exactly here at the top layer ('routes/+layout.ts'), 43 | * so even if during page validation a 404 will be thrown, layout and error page could still access it. 44 | */ 45 | const LP = localizePage(url, route.id); 46 | 47 | // pass locale and localized page to the "rendering context" 48 | return { locale, LP }; 49 | }; 50 | -------------------------------------------------------------------------------- /src/routes/[[lang=lang]]/+layout.ts: -------------------------------------------------------------------------------- 1 | import { href, validatePage, getPageParams } from '$lib/index.js'; 2 | import { get } from 'svelte/store'; 3 | import LL, { setLocale } from '$i18n/i18n-svelte.js'; 4 | import { baseLocale } from '$i18n/i18n-util.js'; 5 | import type { LayoutLoad } from './$types.js'; 6 | 7 | export const load: LayoutLoad = async ({ parent, url }) => { 8 | // wait for parent `+layout.ts` to load dictionary and pass locale information 9 | const { locale, LP } = await parent(); 10 | 11 | // if you need to output a localized string in a `load` function, 12 | // you always need to call `setLocale` right before you access the `LL` store 13 | setLocale(locale); 14 | // get the translation functions value from the store 15 | const $LL = get(LL); 16 | console.info($LL.log({ fileName: 'routes/[[lang=lang]]/+layout.ts' })); 17 | 18 | /** 19 | * Create fallback path that will be suggested to a visitor if the requested page isn't available 20 | * for their locale (404). 21 | */ 22 | const fallbackPath = href(LP, baseLocale, { 23 | base: url.pathname, 24 | params: getPageParams(LP) 25 | }); 26 | 27 | /** 28 | * Localized page validation should happen exactly here in the `routes/[[lang=lang]]/+layout.ts` and NOT in the 29 | * `routes/+layout.ts`. There are 2 reasons for this: 30 | * 1. We shouldn't validate paths like `/api`. 31 | * 2. "If the error occurs inside a load function in `+layout(.server).js`, the closest error boundary in the tree 32 | * is an `+error.svelte` file above that layout (not next to it)." - https://kit.svelte.dev/docs/errors#responses 33 | * In other words, when a 404 is thrown here, the `routes/+error.svelte` layout will be used. 34 | * If we place validation in the `routes/+layout.ts`, there won't be an error layout to use, so SvelteKit will 35 | * use a default empty white page with a 404 code. 36 | */ 37 | validatePage(LP, { 38 | notFound: { 39 | message: $LL.pageError.messageNotTranslated(), 40 | link: fallbackPath, 41 | linkText: $LL.pageError.actionBaseLocale() 42 | } 43 | }); 44 | 45 | // pass locale and localized page to the "rendering context" 46 | return { locale, LP }; 47 | }; 48 | -------------------------------------------------------------------------------- /src/routes/[[lang=lang]]/+page.svelte: -------------------------------------------------------------------------------- 1 | 24 | 25 |

26 | {$LL.pageHome.welcome()} 27 |

28 | 29 |
30 |

{$LL.pageHome.legend.title()}

31 |
    32 | 33 | 34 | 35 | 36 | 37 |
38 |

{$LL.pageHome.apiDescription()}

39 |

{$LL.pageHome.spectators(spectators)}

40 |
41 | -------------------------------------------------------------------------------- /src/routes/[[lang=lang]]/+page.ts: -------------------------------------------------------------------------------- 1 | import type { PageLoad } from './$types.js'; 2 | import LL, { setLocale } from '$i18n/i18n-svelte.js'; 3 | import { get } from 'svelte/store'; 4 | 5 | export const load: PageLoad = async ({ parent }) => { 6 | // wait for `+layout.ts` to load dictionary and pass locale information 7 | const { locale } = await parent(); 8 | 9 | // if you need to output a localized string in a `load` function, 10 | // you always need to call `setLocale` right before you access the `LL` store 11 | setLocale(locale); 12 | // get the translation functions value from the store 13 | const $LL = get(LL); 14 | console.info($LL.log({ fileName: 'routes/[[lang=lang]]/+page.ts' })); 15 | 16 | return { 17 | title: $LL.pageHome.title() 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /src/routes/[[lang=lang]]/[l_about=l_about]/+page.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |

12 | {$LL.pageAbout.title()} 13 |

14 |
15 | {$LL.pageAbout.text()} 16 |
17 | -------------------------------------------------------------------------------- /src/routes/[[lang=lang]]/[l_about=l_about]/+page.ts: -------------------------------------------------------------------------------- 1 | import type { PageLoad } from './$types.js'; 2 | import LL, { setLocale } from '$i18n/i18n-svelte.js'; 3 | import { get } from 'svelte/store'; 4 | 5 | export const load: PageLoad = async ({ parent }) => { 6 | // wait for `+layout.ts` to load dictionary and pass locale information 7 | const { locale } = await parent(); 8 | 9 | // if you need to output a localized string in a `load` function, 10 | // you always need to call `setLocale` right before you access the `LL` store 11 | setLocale(locale); 12 | // get the translation functions value from the store 13 | const $LL = get(LL); 14 | console.info($LL.log({ fileName: 'routes/[[lang=lang]]/[l_about=l_about]/+page.ts' })); 15 | 16 | return { 17 | title: $LL.pageAbout.title() 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /src/routes/[[lang=lang]]/[l_about=l_about]/[l_english=l_english]/+page.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |

12 | {$LL.pageEnglish.title()} 13 |

14 |
15 | {$LL.pageEnglish.text()} 16 |
17 | -------------------------------------------------------------------------------- /src/routes/[[lang=lang]]/[l_about=l_about]/[l_english=l_english]/+page.ts: -------------------------------------------------------------------------------- 1 | import type { PageLoad } from './$types.js'; 2 | import LL, { setLocale } from '$i18n/i18n-svelte.js'; 3 | import { get } from 'svelte/store'; 4 | 5 | export const load: PageLoad = async ({ parent }) => { 6 | // wait for `+layout.ts` to load dictionary and pass locale information 7 | const { locale } = await parent(); 8 | 9 | // if you need to output a localized string in a `load` function, 10 | // you always need to call `setLocale` right before you access the `LL` store 11 | setLocale(locale); 12 | // get the translation functions value from the store 13 | const $LL = get(LL); 14 | console.info( 15 | $LL.log({ 16 | fileName: 'routes/[[lang=lang]]/[l_about=l_about]/[l_english=l_english]/+page.ts' 17 | }) 18 | ); 19 | 20 | return { 21 | title: $LL.pageEnglish.title() 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /src/routes/[[lang=lang]]/[l_news=l_news]/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { getPostsByLocale } from '$utils/db.js'; 2 | import type { PageServerLoad } from './$types.js'; 3 | 4 | export const load: PageServerLoad = async ({ locals: { locale, LL } }) => { 5 | console.info(LL.log({ fileName: 'routes/[[lang=lang]]/[l_news=l_news]/+page.server.ts' })); 6 | 7 | return { 8 | posts: await getPostsByLocale(locale) 9 | }; 10 | }; 11 | -------------------------------------------------------------------------------- /src/routes/[[lang=lang]]/[l_news=l_news]/+page.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |

15 | {$LL.pageNews.title()} 16 |

17 |
18 | {#if data.posts} 19 |
    20 | {#each data.posts as post} 21 | 30 | {/each} 31 |
32 | {/if} 33 |
34 | 44 |
45 | -------------------------------------------------------------------------------- /src/routes/[[lang=lang]]/[l_news=l_news]/+page.ts: -------------------------------------------------------------------------------- 1 | import type { PageLoad } from './$types.js'; 2 | import LL, { setLocale } from '$i18n/i18n-svelte.js'; 3 | import { get } from 'svelte/store'; 4 | 5 | export const load: PageLoad = async ({ parent, data: { posts } }) => { 6 | // wait for `+layout.ts` to load dictionary and pass locale information 7 | const { locale } = await parent(); 8 | 9 | // if you need to output a localized string in a `load` function, 10 | // you always need to call `setLocale` right before you access the `LL` store 11 | setLocale(locale); 12 | // get the translation functions value from the store 13 | const $LL = get(LL); 14 | console.info($LL.log({ fileName: 'routes/[[lang=lang]]/[l_about=l_about]/+page.ts' })); 15 | 16 | return { 17 | title: $LL.pageNews.title(), 18 | posts 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /src/routes/[[lang=lang]]/[l_news=l_news]/[post]/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { getPostBySlug } from '$utils/db.js'; 2 | import type { PageServerLoad } from './$types.js'; 3 | 4 | export const load: PageServerLoad = async ({ params, locals: { LL } }) => { 5 | console.info( 6 | LL.log({ fileName: 'routes/[[lang=lang]]/[l_news=l_news]/[post]/+page.server.ts' }) 7 | ); 8 | 9 | return { 10 | post: await getPostBySlug(params.post) 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /src/routes/[[lang=lang]]/[l_news=l_news]/[post]/+page.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 |

16 | {data.post.title} 17 |

18 |
19 | {data.post.content} 20 |
21 | -------------------------------------------------------------------------------- /src/routes/[[lang=lang]]/[l_news=l_news]/[post]/+page.ts: -------------------------------------------------------------------------------- 1 | import LL, { setLocale } from '$i18n/i18n-svelte.js'; 2 | import { get } from 'svelte/store'; 3 | import type { PageLoad } from './$types.js'; 4 | import { error } from '@sveltejs/kit'; 5 | import { getPageParams, href, setPageParam, validatePage } from '$lib/index.js'; 6 | import { baseLocale } from '$i18n/i18n-util.js'; 7 | 8 | export const load: PageLoad = async ({ url, parent, data: { post } }) => { 9 | // wait for `+layout.ts` to load dictionary and pass locale information 10 | const { locale, LP } = await parent(); 11 | 12 | // if you need to output a localized string in a `load` function, 13 | // you always need to call `setLocale` right before you access the `LL` store 14 | setLocale(locale); 15 | // get the translation functions value from the store 16 | const $LL = get(LL); 17 | console.info($LL.log({ fileName: 'routes/[[lang=lang]]/[l_about=l_about]/[post]/+page.ts' })); 18 | 19 | if (!post) { 20 | throw error(404); 21 | } 22 | 23 | /** 24 | * This page depends on the slug param `post`. And in our case this slug is localized too. 25 | * Which is why we need to set a page param with the allowed values. 26 | */ 27 | setPageParam(LP, 'post', post.slugs); 28 | 29 | /** 30 | * Create fallback path that will be suggested to a visitor if the requested post isn't available 31 | * for their locale (404). 32 | */ 33 | const fallbackPath = href(LP, baseLocale, { 34 | base: url.pathname, 35 | params: getPageParams(LP, true) 36 | }); 37 | 38 | /** 39 | * Since we added a new param we need to revalidate the page. 40 | * Just like before this may result in a redirect or a 404 page. 41 | */ 42 | validatePage(LP, { 43 | notFound: { 44 | message: $LL.pageError.messageNotTranslated(), 45 | link: fallbackPath, 46 | linkText: $LL.pageError.actionBaseLocale() 47 | } 48 | }); 49 | 50 | return { 51 | title: post.title, 52 | post 53 | }; 54 | }; 55 | -------------------------------------------------------------------------------- /src/routes/api/spectators/+server.ts: -------------------------------------------------------------------------------- 1 | import { json } from '@sveltejs/kit'; 2 | import type { RequestHandler } from './$types.js'; 3 | 4 | export const GET: RequestHandler = async ({ url, locals: { LL } }) => { 5 | // no real data, just a simulation ;) 6 | const oldSpectators = Number(url.searchParams.get('oldSpectators') ?? '0'); 7 | let spectators = oldSpectators * 2 + 1; 8 | if (spectators > 100_000) { 9 | spectators = 0; 10 | } 11 | 12 | console.info(LL.log({ fileName: 'routes/api/+server.ts' })); 13 | 14 | return json({ spectators }); 15 | }; 16 | -------------------------------------------------------------------------------- /src/styles/global.scss: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | html { 6 | height: 100%; 7 | } 8 | 9 | body { 10 | margin: 0; 11 | padding: 0; 12 | font-family: Arial, Helvetica, sans-serif; 13 | background-color: #ff3e001a; 14 | overscroll-behavior: none; 15 | height: 100%; 16 | display: flex; 17 | flex-direction: column; 18 | } 19 | 20 | header { 21 | background-color: white; 22 | min-height: 100px; 23 | display: flex; 24 | justify-content: space-between; 25 | align-items: center; 26 | padding: 1rem 1.5rem; 27 | font-family: monospace; 28 | gap: 0.5rem 1.5rem; 29 | flex-shrink: 0; 30 | 31 | @media (max-width: 1024px) { 32 | & { 33 | flex-wrap: wrap; 34 | } 35 | } 36 | 37 | > a { 38 | flex-shrink: 0; 39 | text-decoration: none; 40 | 41 | h1 { 42 | font-size: 1.25rem; 43 | } 44 | } 45 | 46 | ul { 47 | list-style-type: none; 48 | display: flex; 49 | gap: 0.5rem; 50 | padding: 0; 51 | 52 | li > a { 53 | all: initial; 54 | cursor: pointer; 55 | padding: 0.5rem; 56 | color: #444; 57 | 58 | &.active { 59 | color: #ff3e00; 60 | border-bottom: 1px solid; 61 | } 62 | } 63 | 64 | &.menu { 65 | flex-wrap: wrap; 66 | 67 | li > a { 68 | background: #fdfdfd; 69 | display: block; 70 | border: 1px solid #ddd; 71 | } 72 | 73 | @media (max-width: 1024px) { 74 | & { 75 | order: 1; 76 | } 77 | } 78 | 79 | @media (max-width: 576px) { 80 | li { 81 | flex-basis: 100%; 82 | } 83 | } 84 | } 85 | 86 | &.switcher { 87 | flex-shrink: 0; 88 | } 89 | } 90 | } 91 | 92 | .center-list { 93 | margin: 0 auto 16px; 94 | display: table; 95 | text-align: left; 96 | line-height: 1.5; 97 | padding-left: 2rem; 98 | 99 | > li { 100 | margin-bottom: 0.5em; 101 | } 102 | } 103 | 104 | .link-hint:before { 105 | font-size: 14px; 106 | padding: 2px 5px; 107 | margin-right: 5px; 108 | border: 1px solid; 109 | } 110 | .link-hint--success:before { 111 | content: '200'; 112 | background: #bbffb1; 113 | border-color: #3da027; 114 | } 115 | .link-hint--fallback:before { 116 | content: '200F'; 117 | background: #b1f6ff; 118 | border-color: #2798a0; 119 | } 120 | .link-hint--vary:before { 121 | content: '200V'; 122 | background: #e1b1ff; 123 | border-color: #7a27a0; 124 | } 125 | .link-hint--redirect:before { 126 | content: '301'; 127 | background: #fdffb1; 128 | border-color: #a0a027; 129 | } 130 | .link-hint--error:before { 131 | content: '404'; 132 | background: #ffbbb1; 133 | border-color: #a03d27; 134 | } 135 | 136 | h1 { 137 | color: #ff3e00; 138 | } 139 | 140 | h2 { 141 | margin: 2rem 0; 142 | text-align: center; 143 | } 144 | 145 | h3 { 146 | margin: 1.5rem 0; 147 | text-align: center; 148 | } 149 | 150 | main { 151 | padding: 1rem 1.5rem; 152 | overflow: hidden; 153 | color: #444; 154 | flex-shrink: 0; 155 | flex-grow: 1; 156 | } 157 | 158 | .content { 159 | text-align: center; 160 | } 161 | 162 | footer { 163 | background: #ffffff; 164 | padding: 1.5rem; 165 | margin-top: 1.5rem; 166 | flex-shrink: 0; 167 | text-align: center; 168 | 169 | a { 170 | text-decoration: none; 171 | color: #ff3e00; 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/utils/db.ts: -------------------------------------------------------------------------------- 1 | import type { Locales } from '$i18n/i18n-types.js'; 2 | 3 | // Simulates DB 4 | const posts = [ 5 | { 6 | id: 1, 7 | titles: { 8 | en: 'First post', 9 | de: 'Erster Beitrag', 10 | it: 'Primo post', 11 | ru: 'Первый пост' 12 | }, 13 | slugs: { 14 | en: 'first-post', 15 | de: 'erster-beitrag', 16 | it: 'primo-post', 17 | ru: 'perviy-post' 18 | }, 19 | contents: { 20 | en: 'First post says hello!', 21 | de: 'Erster Beitrag sagt Hallo!', 22 | it: 'Il primo post ti saluta!', 23 | ru: 'Первый пост говорит привет!' 24 | } 25 | }, 26 | { 27 | id: 2, 28 | titles: { 29 | en: 'Second post', 30 | de: 'Zweiter Beitrag', 31 | it: 'Secondo post', 32 | ru: 'Второй пост' 33 | }, 34 | slugs: { 35 | en: 'second-post', 36 | de: 'zweiter-beitrag', 37 | it: 'secondo-post', 38 | ru: 'vtoroy-post' 39 | }, 40 | contents: { 41 | en: 'Second post says hello!', 42 | de: 'Zweiter Beitrag sagt Hallo!', 43 | it: 'Il secondo post ti saluta!', 44 | ru: 'Второй пост говорит привет!' 45 | } 46 | }, 47 | { 48 | id: 3, 49 | titles: { 50 | en: 'English-only post' 51 | }, 52 | slugs: { 53 | en: 'english-only-post' 54 | }, 55 | contents: { 56 | en: 'English-only post says hello!' 57 | } 58 | } 59 | ]; 60 | 61 | export type Post = { 62 | id: number; 63 | title: string; 64 | content: string; 65 | slugs: Partial>; 66 | locale: Locales; 67 | }; 68 | 69 | export async function getPostsByLocale(locale: Locales): Promise { 70 | const results: Post[] = []; 71 | for (const post of posts) { 72 | if (!(locale in post.slugs)) continue; 73 | 74 | results.push({ 75 | id: post.id, 76 | title: post.titles[locale] as string, 77 | content: post.contents[locale] as string, 78 | slugs: post.slugs, 79 | locale 80 | }); 81 | } 82 | 83 | return results; 84 | } 85 | 86 | export async function getPostBySlug(slug: string): Promise { 87 | for (const post of posts) { 88 | for (const [locale, postSlug] of Object.entries(post.slugs)) { 89 | if (postSlug !== slug) continue; 90 | 91 | return { 92 | id: post.id, 93 | title: post.titles[locale as Locales] as string, 94 | content: post.contents[locale as Locales] as string, 95 | slugs: post.slugs, 96 | locale: locale as Locales 97 | }; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rinart73/sveltekit-localize-url/4007f0715f8e1234c1ce52e0f8f88a227b916a90/static/favicon.png -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-auto'; 2 | import { vitePreprocess } from '@sveltejs/kit/vite'; 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | // Consult https://kit.svelte.dev/docs/integrations#preprocessors 7 | // for more information about preprocessors 8 | preprocess: vitePreprocess(), 9 | 10 | kit: { 11 | // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. 12 | // If your environment is not supported or you settled on a specific environment, switch out the adapter. 13 | // See https://kit.svelte.dev/docs/adapters for more information about adapters. 14 | adapter: adapter(), 15 | alias: { 16 | $i18n: 'src/i18n', 17 | $components: 'src/components', 18 | $utils: 'src/utils' 19 | } 20 | } 21 | }; 22 | 23 | export default config; 24 | -------------------------------------------------------------------------------- /tests/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@playwright/test'; 2 | 3 | test('placeholder', async ({ page }) => { 4 | // await page.goto('/'); 5 | // await expect(page.getByRole('heading', { name: 'Welcome to SvelteKit' })).toBeVisible(); 6 | }); 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true, 12 | "moduleResolution": "NodeNext" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import { defineConfig } from 'vitest/config'; 3 | 4 | export default defineConfig({ 5 | plugins: [sveltekit()], 6 | test: { 7 | include: ['src/**/*.{test,spec}.{js,ts}'] 8 | } 9 | }); 10 | --------------------------------------------------------------------------------