diff --git a/package-lock.json b/package-lock.json index 171233be..6c9b38ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -546,7 +546,7 @@ "version": "2.9.0", "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.9.0.tgz", "integrity": "sha512-rnJenoStJ8nvmt9Gzye8nkYd6V22xUAnu4086ER7h1zJ508vStko4pMvDeQ446ilDTFpV5wnoc5YS7XvMwwMqA==", - "dev": true, + "devOptional": true, "license": "(Apache-2.0 AND BSD-3-Clause)" }, "node_modules/@colors/colors": { @@ -600,7 +600,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -617,7 +616,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -634,7 +632,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -651,7 +648,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -668,7 +664,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -685,7 +680,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -702,7 +696,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -719,7 +712,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -736,7 +728,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -753,7 +744,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -770,7 +760,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -787,7 +776,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -804,7 +792,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -821,7 +808,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -838,7 +824,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -855,7 +840,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -872,7 +856,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -889,7 +872,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -906,7 +888,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -923,7 +904,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -940,7 +920,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -957,7 +936,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -974,7 +952,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -991,7 +968,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1008,7 +984,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1025,7 +1000,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1881,7 +1855,6 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -1921,7 +1894,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1942,7 +1914,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1963,7 +1934,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1984,7 +1954,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2005,7 +1974,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2026,7 +1994,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2047,7 +2014,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2068,7 +2034,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2089,7 +2054,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2110,7 +2074,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2131,7 +2094,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2152,7 +2114,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2173,7 +2134,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2225,7 +2185,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2239,7 +2198,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2253,7 +2211,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2267,7 +2224,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2281,7 +2237,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2295,7 +2250,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2309,7 +2263,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2323,7 +2276,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2337,7 +2289,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2351,7 +2302,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2365,7 +2315,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2379,7 +2328,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2393,7 +2341,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2407,7 +2354,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2421,7 +2367,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2435,7 +2380,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2449,7 +2393,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2463,7 +2406,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2477,7 +2419,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2491,7 +2432,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2505,7 +2445,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2519,7 +2458,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2664,7 +2602,7 @@ "version": "24.6.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.2.tgz", "integrity": "sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~7.13.0" @@ -3702,7 +3640,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -3756,7 +3694,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/buffer-builder/-/buffer-builder-0.2.0.tgz", "integrity": "sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==", - "dev": true, + "devOptional": true, "license": "MIT/X11" }, "node_modules/bundle-name": { @@ -4036,7 +3974,7 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz", "integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/colors": { @@ -4398,7 +4336,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "dev": true, "license": "Apache-2.0", "optional": true, "bin": { @@ -5390,7 +5327,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -5593,7 +5530,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -5881,7 +5817,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -6029,7 +5965,7 @@ "version": "5.1.3", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/import-fresh": { @@ -6158,7 +6094,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -6178,7 +6114,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -6210,7 +6146,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -6989,7 +6925,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -7003,7 +6939,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -7312,7 +7248,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "dev": true, "license": "MIT", "optional": true }, @@ -8276,7 +8211,7 @@ "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" @@ -8332,7 +8267,6 @@ "version": "1.93.2", "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.2.tgz", "integrity": "sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -8354,7 +8288,7 @@ "version": "1.93.2", "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.93.2.tgz", "integrity": "sha512-FvQdkn2dZ8DGiLgi0Uf4zsj7r/BsiLImNa5QJ10eZalY6NfZyjrmWGFcuCN5jNwlDlXFJnftauv+UtvBKLvepQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@bufbuild/protobuf": "^2.5.0", @@ -8403,7 +8337,6 @@ "!riscv64", "!x64" ], - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -8417,7 +8350,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8434,7 +8366,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8451,7 +8382,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8468,7 +8398,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8485,7 +8414,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8502,7 +8430,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8519,7 +8446,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8536,7 +8462,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8553,7 +8478,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8570,7 +8494,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8587,7 +8510,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8604,7 +8526,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8621,7 +8542,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8638,7 +8558,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8652,7 +8571,6 @@ "version": "1.93.2", "resolved": "https://registry.npmjs.org/sass-embedded-unknown-all/-/sass-embedded-unknown-all-1.93.2.tgz", "integrity": "sha512-7VnaOmyewcXohiuoFagJ3SK5ddP9yXpU0rzz+pZQmS1/+5O6vzyFCUoEt3HDRaLctH4GT3nUGoK1jg0ae62IfQ==", - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8672,7 +8590,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8689,7 +8606,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8703,7 +8619,7 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -8719,7 +8635,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -8736,7 +8651,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, "license": "MIT", "optional": true, "engines": { @@ -9259,7 +9173,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/sync-child-process/-/sync-child-process-1.0.2.tgz", "integrity": "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "sync-message-port": "^1.0.0" @@ -9272,7 +9186,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/sync-message-port/-/sync-message-port-1.1.3.tgz", "integrity": "sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=16.0.0" @@ -9368,7 +9282,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -9460,7 +9374,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, + "devOptional": true, "license": "0BSD" }, "node_modules/type-check": { @@ -9546,7 +9460,7 @@ "version": "7.13.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.13.0.tgz", "integrity": "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/unicorn-magic": { @@ -9717,7 +9631,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/vary": { diff --git a/src/components/attributeTable/AttributeTableWin.vue b/src/components/attributeTable/AttributeTableWin.vue index 057a52dc..07c8422b 100644 --- a/src/components/attributeTable/AttributeTableWin.vue +++ b/src/components/attributeTable/AttributeTableWin.vue @@ -69,9 +69,12 @@ export default { * Reactive property to return the OpenLayers vector layers to be shown in the selection menu. */ displayedLayers () { - return this.layers + if (!this.layers) { + return []; + } + return this.layers.getArray() .filter(layer => - layer instanceof VectorLayer && + layer.toRaw() instanceof VectorLayer && layer.get('lid') !== 'wgu-measure-layer' && layer.get('lid') !== 'wgu-geolocator-layer' ) diff --git a/src/components/bglayerswitcher/BgLayerList.vue b/src/components/bglayerswitcher/BgLayerList.vue index eaebf529..b39a7747 100644 --- a/src/components/bglayerswitcher/BgLayerList.vue +++ b/src/components/bglayerswitcher/BgLayerList.vue @@ -78,7 +78,10 @@ export default { * Reactive property to return the OpenLayers layers marked as 'isBaseLayer'. */ displayedLayers () { - return this.layers + if (!this.layers) { + return []; + } + return this.layers.getArray() .filter(layer => layer.get('isBaseLayer')) .reverse(); }, diff --git a/src/components/bglayerswitcher/BgLayerSwitcher.vue b/src/components/bglayerswitcher/BgLayerSwitcher.vue index 3ef8a369..0e7fe7e5 100644 --- a/src/components/bglayerswitcher/BgLayerSwitcher.vue +++ b/src/components/bglayerswitcher/BgLayerSwitcher.vue @@ -59,7 +59,10 @@ export default { * which is marked as 'isBaseLayer'. */ show () { - return this.layers + if (!this.layers) { + return false; + } + return this.layers.getArray() .filter(layer => layer.get('isBaseLayer')) .length > 1; } diff --git a/src/components/bglayerswitcher/LayerPreviewImage.vue b/src/components/bglayerswitcher/LayerPreviewImage.vue index 6a0edc83..2c501cbb 100644 --- a/src/components/bglayerswitcher/LayerPreviewImage.vue +++ b/src/components/bglayerswitcher/LayerPreviewImage.vue @@ -37,7 +37,7 @@ export default { */ previewURL () { return this.layer.get('previewImage') || LayerPreview.getUrl( - this.layer, + this.layer.toRaw(), this.mapView.getCenter(), this.mapView.getResolution(), this.mapView.getProjection() diff --git a/src/components/layerlist/LayerLegendImage.vue b/src/components/layerlist/LayerLegendImage.vue index ab02fcbd..824cbb39 100644 --- a/src/components/layerlist/LayerLegendImage.vue +++ b/src/components/layerlist/LayerLegendImage.vue @@ -54,7 +54,8 @@ export default { ...this.layer.get('legendOptions') }; return legendUtil.getUrl( - this.layer, this.resolution, options, this.layer.get('legendUrl')); + this.layer.toRaw(), this.resolution, options, + this.layer.get('legendUrl')); } } }; diff --git a/src/components/layerlist/LayerList.vue b/src/components/layerlist/LayerList.vue index 0fb2bee8..2600f5a0 100644 --- a/src/components/layerlist/LayerList.vue +++ b/src/components/layerlist/LayerList.vue @@ -35,12 +35,15 @@ export default { } }, computed: { - /** - * Reactive property to return the OpenLayers layers to be shown in the control. - * Remarks: The 'displayInLayerList' attribute should default to true per convention. - */ + /** + * Reactive property to return the OpenLayers layers to be shown in the control. + * Remarks: The 'displayInLayerList' attribute should default to true per convention. + */ displayedLayers () { - return this.layers + if (!this.layers) { + return []; + } + return this.layers.getArray() .filter(layer => layer.get('displayInLayerList') !== false && !layer.get('isBaseLayer')) .reverse(); } diff --git a/src/components/overviewmap/OverviewMapPanel.vue b/src/components/overviewmap/OverviewMapPanel.vue index 8197bc21..1628f24c 100644 --- a/src/components/overviewmap/OverviewMapPanel.vue +++ b/src/components/overviewmap/OverviewMapPanel.vue @@ -45,6 +45,7 @@ export default { const panel = this.$refs.overviewmapPanel; if (this.map && panel && !this.overviewMap) { this.overviewMap = new OverviewMapController(this.map, panel.$el, this.$props); + this.overviewMap.setLayer(this.selectedBgLayer.toRaw()); } }, /** @@ -64,7 +65,10 @@ export default { * this returns the first in the list of background layers. */ selectedBgLayer () { - return this.layers + if (!this.layers) { + return {}; + } + return this.layers.getArray() .filter(layer => layer.get('isBaseLayer')) .reverse() .find(layer => layer.getVisible()); @@ -86,9 +90,9 @@ export default { /** * Watch for background layer selection change. */ - selectedBgLayer () { + selectedBgLayer (newLayer) { if (this.overviewMap) { - this.overviewMap.setLayer(this.selectedBgLayer); + this.overviewMap.setLayer(newLayer.toRaw()); } } } diff --git a/src/composables/Map.js b/src/composables/Map.js index af74b17c..43fb91c2 100644 --- a/src/composables/Map.js +++ b/src/composables/Map.js @@ -1,10 +1,11 @@ -import { ref, shallowRef, computed } from 'vue'; +import { shallowRef, computed } from 'vue'; +import { LayerCollectionProxy } from '@/util/Layer'; /** * Composable which encapsulate OL map management. */ const map = shallowRef(); -const layers = ref([]); +const layers = shallowRef(); /** * Init function of the composable. @@ -13,11 +14,7 @@ const layers = ref([]); */ export function bindMap (olMap) { map.value = olMap; - layers.value = map.value.getLayers().getArray(); - - map.value.getLayers().on('change:length', (event) => { - layers.value = [...event.target.getArray()]; - }); + layers.value = new LayerCollectionProxy(olMap.getLayers()); }; /** @@ -26,7 +23,10 @@ export function bindMap (olMap) { * disable map and layers reactivity. */ export function unbindMap () { - layers.value = []; + if (layers.value) { + layers.value.destroy(); + layers.value = undefined; + } map.value = undefined; }; @@ -37,8 +37,6 @@ export function unbindMap () { export function useMap () { return { map: computed(() => map.value), - layers: computed(() => { - return layers.value; - }) + layers: computed(() => layers.value) }; }; diff --git a/src/util/Layer.js b/src/util/Layer.js index 2ba5d0e4..35db0972 100644 --- a/src/util/Layer.js +++ b/src/util/Layer.js @@ -1,4 +1,5 @@ import ViewAnimationUtil from './ViewAnimation'; +import { reactive, toRaw, markRaw } from 'vue' /** * Util class for OL layers @@ -57,3 +58,221 @@ const LayerUtil = { } export default LayerUtil; + +/** + * Transparent proxy around an OpenLayers layer to be used in Vue components + * when reactive layer properties are required. + */ +export class LayerProxy { + /** + * @param {ol.layer.Base} layer OL layer + */ + constructor (layer) { + this.layer = layer; + this.properties = reactive({}); + + // Map getXXX methods declared in ol/layer/Base to property keys. + // For mappings see ol/layer/Property.js. + this.getterMap = { + getOpacity: 'opacity', + getVisible: 'visible', + getExtent: 'extent', + getZIndex: 'zIndex', + getMaxResolution: 'maxResolution', + getMinResolution: 'minResolution', + getMaxZoom: 'maxZoom', + getMinZoom: 'minZoom' + }; + + // Set up listener to detect any property changes + this.propertyChangeListener = (event) => { + const key = event.key; + const value = this.layer.get(key); + + if (value === undefined) { + if (key in this.properties) { + delete this.properties[key]; + } + } else { + this.properties[key] = value; + } + }; + this.layer.on('propertychange', this.propertyChangeListener); + + // Track existing properties + Object.keys(layer.getProperties()).forEach(key => { + this.properties[key] = layer.get(key); + }); + + // Forward everything transparently to the underlying OL layer. The get() + // and getProperties() methods are trapped and handled by the proxy. + // Remarks: Neither set() nor setProperties() have to be handled. Property + // setters operate on the OL layer and then get synced into the proxy via + // observables. + const proxy = new Proxy(this, { + get: function (target, prop, receiver) { + // Intercept getXXX() calls and map to reactive properties + if (prop in target.getterMap) { + const key = target.getterMap[prop]; + return () => target.get(key); + } + + // Forward OL layer API calls except get/getProperties + if (prop in target.layer && !['get', 'getProperties'].includes(prop)) { + const p = target.layer[prop]; + return (typeof p === 'function') ? p.bind(target.layer) : p; + } + return Reflect.get(target, prop, receiver); + } + }); + + // Avoid Vue wrapping the proxy again. + return markRaw(proxy); + } + + /** + * Gets a value of the layer. + * @param {String} key Key name. + * @returns {any} Value. + */ + get (key) { + return this.properties[key]; + } + + /** + * Get all property names and values of the layer. + * @returns {Object} Object. + */ + getProperties () { + return this.properties; + } + + /** + * Get the raw OL layer object wrapped by this proxy. + * @returns {ol.layer.Base} OL layer + */ + toRaw () { + return toRaw(this.layer); + } + + /** + * Destroy the proxy object. This must be invoked before the proxy goes out + * of scope to prevent dangling change notifications. + */ + destroy () { + this.layer.un('propertychange', this.propertyChangeListener); + } +} + +/** + * Transparent proxy around an OpenLayers collection for layers to be used in + * Vue components when reactive layer properties are required. + */ +export class LayerCollectionProxy { + /** + * @param {ol.Collection} collection OL collection of layers + */ + constructor (collection) { + this.collection = reactive(collection); + this.layerProxies = reactive([]); + + const createLayerProxy = (layer) => new LayerProxy(layer); + + // Sync against the underlying collection while retaining the order of + // elements. + // Remarks: A layer proxy must be destroyed before it goes out of scope. + // To minimize the overhead of creating layerProxies preserve existing + // instances by merging. + this.syncLayers = () => { + const newLayerProxies = []; + this.collection.forEach(layer => { + let layerProxy = this.layerProxies.find( + proxy => proxy.toRaw() === toRaw(layer)); + if (!layerProxy) { + layerProxy = createLayerProxy(layer); + } + newLayerProxies.push(layerProxy); + }); + + this.layerProxies.forEach(layerProxy => { + if (!newLayerProxies.includes(layerProxy)) { + layerProxy.destroy(); + } + }); + + this.layerProxies.splice(0); + this.layerProxies.push(...newLayerProxies); + }; + + this.syncLayers(); + + this.collection.on('change:length', this.syncLayers); + + // Forward everything transparently to the underlying OL collection. + // The forEach() and getArray() and item() methods are trapped and handled + // by the proxy. + // Remarks: Methods which alter the collection are not handled. + // These operate on the OL collection and changes get synced into the + // proxy via observables. The methods pop(), push(), remove(), + // removeAt(), setAt() will operate on OL base layer arguments. Therefore + // returned objects by these methods will not properly support reactivity. + const proxy = new Proxy(this, { + get: function (target, prop, receiver) { + if (prop in target.collection && + !['forEach', 'getArray', 'item'].includes(prop)) { + const p = target.collection[prop]; + return (typeof p === 'function') ? p.bind(target.collection) : p; + } + return Reflect.get(target, prop, receiver); + } + }); + + // Avoid Vue wrapping the proxy again. + return markRaw(proxy); + } + + /** + * Iterate over each element, calling the provided callback. + * @param {function} f The function to call for every element. This function + * takes 3 arguments (the element, the index and the array). The return value + * is ignored. + */ + forEach (f) { + this.layerProxies.forEach(f) + } + + /** + * Get an array of LayerProxy objects for all layers in the collection. + * @returns {Array} Array of LayerProxy objects. + */ + getArray () { + return this.layerProxies; + } + + /** + * Get the LayerProxy at the provided index. + * @param {Number} index Index. + * @returns Element. + */ + item (index) { + return this.layerProxies[index]; + } + + /** + * Get the raw collection object wrapped by this proxy. + * @returns {ol.Collection} OL collection of layers + */ + toRaw () { + return toRaw(this.collection); + } + + /** + * Destroy the proxy object. This must be invoked before the proxy goes out of scope. + */ + destroy () { + this.collection.un('change:length', this.syncLayers); + this.layerProxies.forEach(layerProxy => { + layerProxy.destroy(); + }); + } +} diff --git a/tests/unit/specs/components/bglayerswitcher/BgLayerList.spec.js b/tests/unit/specs/components/bglayerswitcher/BgLayerList.spec.js index 4c0637ec..bf6e15e5 100644 --- a/tests/unit/specs/components/bglayerswitcher/BgLayerList.spec.js +++ b/tests/unit/specs/components/bglayerswitcher/BgLayerList.spec.js @@ -1,4 +1,3 @@ -import { toRaw } from 'vue'; import { shallowMount } from '@vue/test-utils'; import { bindMap, unbindMap } from '@/composables/Map'; import BgLayerList from '@/components/bglayerswitcher/BgLayerList.vue'; @@ -74,7 +73,7 @@ describe('bglayerswitcher/BgLayerList.vue', () => { expect(vm.displayedLayers).to.have.lengthOf(1); const li = vm.displayedLayers[0]; - expect(toRaw(li)).to.equal(layerIn); + expect(li.toRaw()).to.equal(layerIn); expect(li.getVisible()).to.equal(true); expect(vm.selectedLid).to.equal(layerIn.get('lid')); }); diff --git a/tests/unit/specs/components/bglayerswitcher/LayerPreviewImage.spec.js b/tests/unit/specs/components/bglayerswitcher/LayerPreviewImage.spec.js index 99337019..daeb4f1f 100644 --- a/tests/unit/specs/components/bglayerswitcher/LayerPreviewImage.spec.js +++ b/tests/unit/specs/components/bglayerswitcher/LayerPreviewImage.spec.js @@ -1,14 +1,17 @@ import { toRaw } from 'vue'; import { shallowMount } from '@vue/test-utils'; import LayerPreviewImage from '@/components/bglayerswitcher/LayerPreviewImage.vue'; +import { LayerProxy } from '@/util/Layer'; import TileLayer from 'ol/layer/Tile'; import OSM from 'ol/source/OSM'; import View from 'ol/View'; -const osmLayer = new TileLayer({ - lid: 'osm', - source: new OSM() -}); +const osmLayer = new LayerProxy( + new TileLayer({ + lid: 'osm', + source: new OSM() + }) +) const view = new View({ projection: 'EPSG:3857', @@ -46,7 +49,7 @@ describe('bglayerswitcher/LayerPreviewImage.vue', () => { it('has correct props', () => { expect(toRaw(vm.mapView)).to.equal(view); - expect(toRaw(vm.layer)).to.equal(osmLayer); + expect(vm.layer).to.equal(osmLayer); expect(vm.width).to.equal(152); expect(vm.height).to.equal(114); expect(vm.previewIcon).to.equal('md:map'); @@ -83,11 +86,13 @@ describe('bglayerswitcher/LayerPreviewImage.vue', () => { }); it('has correct previewURL for static layer image', async () => { - const osmLayer2 = new TileLayer({ - lid: 'osm2', - source: new OSM(), - previewImage: 'http://my-image.png' - }); + const osmLayer2 = new LayerProxy( + new TileLayer({ + lid: 'osm2', + source: new OSM(), + previewImage: 'http://my-image.png' + }) + ); await comp.setProps({ layer: osmLayer2 }); expect(vm.previewURL).to.equal('http://my-image.png'); diff --git a/tests/unit/specs/components/layerlist/LayerLegendImage.spec.js b/tests/unit/specs/components/layerlist/LayerLegendImage.spec.js index e13e68d5..d1e6a2cd 100644 --- a/tests/unit/specs/components/layerlist/LayerLegendImage.spec.js +++ b/tests/unit/specs/components/layerlist/LayerLegendImage.spec.js @@ -3,26 +3,31 @@ import { shallowMount } from '@vue/test-utils'; import { createI18n } from 'vue-i18n'; import LayerLegendImage from '@/components/layerlist/LayerLegendImage.vue'; import i18nMessages from '@/locales/en.json'; +import { LayerProxy } from '@/util/Layer'; import TileLayer from 'ol/layer/Tile'; import OSM from 'ol/source/OSM'; import TileWmsSource from 'ol/source/TileWMS'; import View from 'ol/View'; -const osmLayer = new TileLayer({ - lid: 'osm', - source: new OSM() -}); - -const wmsLayer = new TileLayer({ - lid: 'ahocevar-wms', - source: new TileWmsSource({ - url: 'https://ahocevar.com/geoserver/wms', - params: { - LAYERS: 'topp:states', - TILED: true - } +const osmLayer = new LayerProxy( + new TileLayer({ + lid: 'osm', + source: new OSM() }) -}); +); + +const wmsLayer = new LayerProxy( + new TileLayer({ + lid: 'ahocevar-wms', + source: new TileWmsSource({ + url: 'https://ahocevar.com/geoserver/wms', + params: { + LAYERS: 'topp:states', + TILED: true + } + }) + }) +) const view = new View({ projection: 'EPSG:3857', @@ -83,7 +88,7 @@ describe('layerlist/LayerLegendImage.vue', () => { it('has correct props', () => { expect(toRaw(vm.mapView)).to.equal(view); - expect(toRaw(vm.layer)).to.equal(osmLayer); + expect(vm.layer).to.equal(osmLayer); }); afterEach(() => { @@ -118,26 +123,30 @@ describe('layerlist/LayerLegendImage.vue', () => { }); it('has correct legendURL for static legend URL', async () => { - const layer = new TileLayer({ - lid: 'osm2', - source: new OSM(), - legendUrl: 'http://my-image.png' - }); + const layer = new LayerProxy( + new TileLayer({ + lid: 'osm2', + source: new OSM(), + legendUrl: 'http://my-image.png' + }) + ); await comp.setProps({ layer }); expect(vm.legendURL).to.equal('http://my-image.png'); }); it('has correct legendURL for legend format URL', async () => { - const layer = new TileLayer({ - lid: 'osm2', - source: new OSM(), - legendUrl: 'http://my-image.png?transparent={{TRANSPARENT}}&width={{WIDTH}}&SCALE={{SCALE}}&language={{LANGUAGE}}', - legendOptions: { - transparent: true, - width: 14 - } - }); + const layer = new LayerProxy( + new TileLayer({ + lid: 'osm2', + source: new OSM(), + legendUrl: 'http://my-image.png?transparent={{TRANSPARENT}}&width={{WIDTH}}&SCALE={{SCALE}}&language={{LANGUAGE}}', + legendOptions: { + transparent: true, + width: 14 + } + }) + ); await comp.setProps({ layer }); expect(vm.legendURL).to.equal('http://my-image.png?transparent=true&width=14&SCALE=139770566.00717944&language=en'); diff --git a/tests/unit/specs/components/layerlist/LayerList.spec.js b/tests/unit/specs/components/layerlist/LayerList.spec.js index c20f2930..2bdfb552 100644 --- a/tests/unit/specs/components/layerlist/LayerList.spec.js +++ b/tests/unit/specs/components/layerlist/LayerList.spec.js @@ -88,7 +88,7 @@ describe('layerlist/LayerList.vue', () => { expect(vm.displayedLayers).to.have.lengthOf(1); const li = vm.displayedLayers[0]; - expect(toRaw(li)).to.equal(layerIn); + expect(li.toRaw()).to.equal(layerIn); expect(li.getVisible()).to.be.true; }); diff --git a/tests/unit/specs/components/layerlist/LayerListItem.spec.js b/tests/unit/specs/components/layerlist/LayerListItem.spec.js index c940e6c6..565664b9 100644 --- a/tests/unit/specs/components/layerlist/LayerListItem.spec.js +++ b/tests/unit/specs/components/layerlist/LayerListItem.spec.js @@ -1,14 +1,17 @@ import { toRaw } from 'vue'; import { shallowMount } from '@vue/test-utils'; import LayerListItem from '@/components/layerlist/LayerListItem.vue'; +import { LayerProxy } from '@/util/Layer'; import TileLayer from 'ol/layer/Tile'; import OSM from 'ol/source/OSM'; import View from 'ol/View'; -const osmLayer = new TileLayer({ - lid: 'osm', - source: new OSM() -}); +const osmLayer = new LayerProxy( + new TileLayer({ + lid: 'osm', + source: new OSM() + }) +); const view = new View({ projection: 'EPSG:3857', @@ -104,11 +107,13 @@ describe('layerlist/LayerListItem.vue', () => { it('has correct showLegend property for layer', async () => { expect(vm.showLegend).to.be.false; - const osmLayer2 = new TileLayer({ - lid: 'osm2', - source: new OSM(), - legend: true - }); + const osmLayer2 = new LayerProxy( + new TileLayer({ + lid: 'osm2', + source: new OSM(), + legend: true + }) + ); await comp.setProps({ layer: osmLayer2 }); expect(vm.showLegend).to.be.true; @@ -117,11 +122,13 @@ describe('layerlist/LayerListItem.vue', () => { it('has correct showOpacityControl property for layer', async () => { expect(vm.showOpacityControl).to.be.false; - const osmLayer2 = new TileLayer({ - lid: 'osm2', - source: new OSM(), - opacityControl: true - }); + const osmLayer2 = new LayerProxy( + new TileLayer({ + lid: 'osm2', + source: new OSM(), + opacityControl: true + }) + ); await comp.setProps({ layer: osmLayer2 }); expect(vm.showOpacityControl).to.be.true; @@ -130,20 +137,24 @@ describe('layerlist/LayerListItem.vue', () => { it('has correct showDetails property for layer', async () => { expect(vm.showDetails).to.be.false; - const osmLayer2 = new TileLayer({ - lid: 'osm2', - source: new OSM(), - legend: true - }); + const osmLayer2 = new LayerProxy( + new TileLayer({ + lid: 'osm2', + source: new OSM(), + legend: true + }) + ); await comp.setProps({ layer: osmLayer2 }); expect(vm.showDetails).to.be.true; - const osmLayer3 = new TileLayer({ - lid: 'osm3', - source: new OSM(), - opacityControl: true - }); + const osmLayer3 = new LayerProxy( + new TileLayer({ + lid: 'osm3', + source: new OSM(), + opacityControl: true + }) + ); comp.setProps({ layer: osmLayer3 }); expect(vm.showDetails).to.be.true; @@ -152,11 +163,13 @@ describe('layerlist/LayerListItem.vue', () => { it('has correct layerLid property for layer', async () => { expect(vm.layerLid).to.equal('osm'); - const osmLayer2 = new TileLayer({ - lid: 'osm2', - source: new OSM(), - legend: true - }); + const osmLayer2 = new LayerProxy( + new TileLayer({ + lid: 'osm2', + source: new OSM(), + legend: true + }) + ); await comp.setProps({ layer: osmLayer2 }); expect(vm.layerLid).to.equal('osm2'); diff --git a/tests/unit/specs/components/layerlist/LayerOpacityControl.spec.js b/tests/unit/specs/components/layerlist/LayerOpacityControl.spec.js index 93860cc4..0c6e23c0 100644 --- a/tests/unit/specs/components/layerlist/LayerOpacityControl.spec.js +++ b/tests/unit/specs/components/layerlist/LayerOpacityControl.spec.js @@ -1,13 +1,15 @@ -import { toRaw } from 'vue'; import { shallowMount } from '@vue/test-utils'; import LayerOpacityControl from '@/components/layerlist/LayerOpacityControl.vue'; +import { LayerProxy } from '@/util/Layer'; import TileLayer from 'ol/layer/Tile'; import OSM from 'ol/source/OSM'; -const osmLayer = new TileLayer({ - lid: 'osm', - source: new OSM() -}); +const osmLayer = new LayerProxy( + new TileLayer({ + lid: 'osm', + source: new OSM() + }) +) const moduleProps = { layer: osmLayer @@ -34,7 +36,7 @@ describe('layerlist/LayerOpacityControl.vue', () => { }); it('has correct props', () => { - expect(toRaw(vm.layer)).to.equal(osmLayer); + expect(vm.layer).to.equal(osmLayer); }); afterEach(() => { diff --git a/tests/unit/specs/components/overviewmap/OverviewMapPanel.spec.js b/tests/unit/specs/components/overviewmap/OverviewMapPanel.spec.js index b4a64427..d578992b 100644 --- a/tests/unit/specs/components/overviewmap/OverviewMapPanel.spec.js +++ b/tests/unit/specs/components/overviewmap/OverviewMapPanel.spec.js @@ -1,4 +1,3 @@ -import { toRaw } from 'vue'; import { shallowMount } from '@vue/test-utils'; import { bindMap, unbindMap } from '@/composables/Map'; import OverviewMapPanel from '@/components/overviewmap/OverviewMapPanel.vue'; @@ -81,7 +80,7 @@ describe('overviewmap/OverviewMapPanel.vue', () => { }); bindMap(map); - expect(toRaw(vm.selectedBgLayer)).to.equal(layerIn); + expect(vm.selectedBgLayer.toRaw()).to.equal(layerIn); }); it('selectedBgLayer is synced with the layer stack', async () => { @@ -102,12 +101,12 @@ describe('overviewmap/OverviewMapPanel.vue', () => { }); bindMap(map); - expect(toRaw(vm.selectedBgLayer)).to.equal(layerIn); + expect(vm.selectedBgLayer.toRaw()).to.equal(layerIn); layerIn.setVisible(false); map.addLayer(layerOut); - expect(toRaw(vm.selectedBgLayer)).to.equal(layerOut); + expect(vm.selectedBgLayer.toRaw()).to.equal(layerOut); }); afterEach(() => { diff --git a/tests/unit/specs/util/Layer.spec.js b/tests/unit/specs/util/Layer.spec.js index c1ed1a9d..2fe293f9 100644 --- a/tests/unit/specs/util/Layer.spec.js +++ b/tests/unit/specs/util/Layer.spec.js @@ -1,4 +1,5 @@ -import LayerUtil from '@/util/Layer'; +import LayerUtil, { LayerProxy, LayerCollectionProxy } from '@/util/Layer'; + import Map from 'ol/Map'; import View from 'ol/View'; import TileLayer from 'ol/layer/Tile'; @@ -179,3 +180,186 @@ describe('LayerUtil', () => { expect(olMap.getView().getCenter()[1]).to.equal(0); }); }); + +describe('LayerProxy', () => { + it('is defined', () => { + expect(LayerProxy).to.not.be.an('undefined'); + }); + + it('has the correct functions', () => { + expect(LayerProxy.prototype.get).to.be.a('function'); + expect(LayerProxy.prototype.getProperties).to.be.a('function'); + expect(LayerProxy.prototype.toRaw).to.be.a('function'); + expect(LayerProxy.prototype.destroy).to.be.a('function'); + }); + + describe('methods', () => { + let layer; + let proxy; + + beforeEach(() => { + layer = new TileLayer({ + foo: 'bar', + source: new OSM() + }) + proxy = new LayerProxy(layer); + }); + + it('get reflects layer properties', () => { + expect(proxy.get('foo')).to.eql('bar'); + + layer.set('foo', 'bar2'); + expect(proxy.get('foo')).to.eql('bar2'); + }); + + it('getProperties reflects layer properties', () => { + expect(proxy.getProperties()).to.deep.equal(layer.getProperties()); + + layer.set('foo', 'bar2'); + expect(proxy.getProperties()).to.deep.equal(layer.getProperties()); + }); + + it('getter methods reflect layer getter methods', () => { + expect(proxy.getOpacity()).to.eql(layer.getOpacity()); + expect(proxy.getVisible()).to.eql(layer.getVisible()); + expect(proxy.getExtent()).to.eql(layer.getExtent()); + expect(proxy.getZIndex()).to.eql(layer.getZIndex()); + expect(proxy.getMaxResolution()).to.eql(layer.getMaxResolution()); + expect(proxy.getMinResolution()).to.eql(layer.getMinResolution()); + expect(proxy.getMaxZoom()).to.eql(layer.getMaxZoom()); + expect(proxy.getMinZoom()).to.eql(layer.getMinZoom()); + + layer.setOpacity(0.5); + layer.setVisible(false); + layer.setExtent([0, 0, 100, 100]); + layer.setZIndex(15); + layer.setMaxResolution(30); + layer.setMinResolution(5); + layer.setMaxZoom(10); + layer.setMinZoom(1); + + expect(proxy.getOpacity()).to.eql(layer.getOpacity()); + expect(proxy.getVisible()).to.eql(layer.getVisible()); + expect(proxy.getExtent()).to.eql(layer.getExtent()); + expect(proxy.getZIndex()).to.eql(layer.getZIndex()); + expect(proxy.getMaxResolution()).to.eql(layer.getMaxResolution()); + expect(proxy.getMinResolution()).to.eql(layer.getMinResolution()); + expect(proxy.getMaxZoom()).to.eql(layer.getMaxZoom()); + expect(proxy.getMinZoom()).to.eql(layer.getMinZoom()); + }); + + it('toRaw returns the raw layer', () => { + expect(proxy.toRaw()).to.equal(layer); + }); + + afterEach(() => { + proxy.destroy(); + }); + }); +}); + +describe('LayerCollectionProxy', () => { + it('is defined', () => { + expect(LayerCollectionProxy).to.not.be.an('undefined'); + }); + + it('has the correct functions', () => { + expect(LayerCollectionProxy.prototype.forEach).to.be.a('function'); + expect(LayerCollectionProxy.prototype.getArray).to.be.a('function'); + expect(LayerCollectionProxy.prototype.item).to.be.a('function'); + expect(LayerCollectionProxy.prototype.toRaw).to.be.a('function'); + expect(LayerCollectionProxy.prototype.destroy).to.be.a('function'); + }); + + describe('methods', () => { + let collection; + let proxy; + + const layer1 = new TileLayer({ + foo: 'bar', + source: new OSM() + }); + const layer2 = new TileLayer({ + foo: 'bar2', + source: new OSM() + }) + const layer3 = new TileLayer({ + foo: 'bar3', + source: new OSM() + }); + + beforeEach(() => { + const map = new Map({ + layers: [layer1, layer2] + }); + + collection = map.getLayers(); + proxy = new LayerCollectionProxy(collection); + }); + + it('getArray reflects layer collection', () => { + const layerProxies = proxy.getArray(); + const rawLayers = collection.getArray(); + + expect(layerProxies.length).to.equal(rawLayers.length); + for (let i = 0; i < rawLayers.length; i++) { + expect(layerProxies[i].toRaw()).to.equal(rawLayers[i]); + } + + collection.push(layer3); + expect(layerProxies.length).to.equal(rawLayers.length); + for (let i = 0; i < rawLayers.length; i++) { + expect(layerProxies[i].toRaw()).to.equal(rawLayers[i]); + } + }); + + it('forEach reflects layer collection', () => { + const rawLayers = collection.getArray(); + let layerProxies = []; + + proxy.forEach(layerProxy => { + layerProxies.push(layerProxy); + }); + expect(layerProxies.length).to.equal(rawLayers.length); + + for (let i = 0; i < rawLayers.length; i++) { + expect(layerProxies[i].toRaw()).to.equal(rawLayers[i]); + } + + layerProxies = []; + collection.push(layer3); + + proxy.forEach(layerProxy => { + layerProxies.push(layerProxy); + }); + expect(layerProxies.length).to.equal(rawLayers.length); + + for (let i = 0; i < rawLayers.length; i++) { + expect(layerProxies[i].toRaw()).to.equal(rawLayers[i]); + } + }); + + it('item reflects layer collection', () => { + const rawLayers = collection.getArray(); + + for (let i = 0; i < rawLayers.length; i++) { + const layerProxy = proxy.item(i); + expect(layerProxy.toRaw()).to.equal(rawLayers[i]); + } + + collection.push(layer3); + for (let i = 0; i < rawLayers.length; i++) { + const layerProxy = proxy.item(i); + expect(layerProxy.toRaw()).to.equal(rawLayers[i]); + } + }); + + it('toRaw returns the raw collection', () => { + expect(proxy.toRaw()).to.equal(collection); + }); + + afterEach(() => { + proxy.destroy(); + }); + }); +});