diff --git a/package-lock.json b/package-lock.json index b84048c..7176a9a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@angular/core": "^16.1.0", "@angular/platform-browser": "^16.1.0", "@angular/platform-browser-dynamic": "^16.1.0", + "@angular/router": "^16.1.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", "uuid": "^9.0.0", @@ -183,18 +184,6 @@ } } }, - "node_modules/@angular-devkit/build-angular/node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz", - "integrity": "sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@angular-devkit/build-angular/node_modules/tslib": { "version": "2.5.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", @@ -433,9 +422,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.6.tgz", - "integrity": "sha512-29tfsWTq2Ftu7MXmimyC0C5FDZv5DYxOZkh3XD3+QW4V/BYuv/LyEsjj3c0hqedEaDt6DBfDvexMKU8YevdqFg==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", + "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -520,16 +509,16 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.6.tgz", - "integrity": "sha512-534sYEqWD9VfUm3IPn2SLcH4Q3P86XL+QvqdC7ZsFrzyyPF3T4XGiVghF6PTYNdWg6pXuoqXxNQAhbYeEInTzA==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz", + "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.22.6", + "@babel/compat-data": "^7.22.9", "@babel/helper-validator-option": "^7.22.5", - "@nicolo-ribaudo/semver-v6": "^6.3.3", "browserslist": "^4.21.9", - "lru-cache": "^5.1.1" + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -538,10 +527,19 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.6.tgz", - "integrity": "sha512-iwdzgtSiBxF6ni6mzVnZCF3xt5qE6cEA0J7nFt8QOAWZ0zjCFceEgpn3vtb2V7WFR6QzP2jmIFOHMTRo7eNJjQ==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.9.tgz", + "integrity": "sha512-Pwyi89uO4YrGKxL/eNJ8lfEH55DnRloGPOseaA8NFNL6jAUnn+KccaISiFazCj5IolPPDjGSdzQzXVzODVRqUQ==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", @@ -549,10 +547,10 @@ "@babel/helper-function-name": "^7.22.5", "@babel/helper-member-expression-to-functions": "^7.22.5", "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@nicolo-ribaudo/semver-v6": "^6.3.3" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -561,15 +559,36 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-create-regexp-features-plugin": { + "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-split-export-declaration": { "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.6.tgz", - "integrity": "sha512-nBookhLKxAWo/TUCmhnaEJyLz2dekjQvv5SRpE9epWQBcpedWLKt8aZdsuT9XV5ovzR3fENLjRXVT0GsSlGGhA==", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz", + "integrity": "sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@nicolo-ribaudo/semver-v6": "^6.3.3", - "regexpu-core": "^5.3.1" + "regexpu-core": "^5.3.1", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -578,6 +597,15 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/helper-define-polyfill-provider": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.1.tgz", @@ -653,18 +681,30 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz", - "integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", + "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-module-imports": "^7.22.5", "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-module-transforms/node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { "@babel/types": "^7.22.5" }, "engines": { @@ -693,15 +733,14 @@ } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.5.tgz", - "integrity": "sha512-cU0Sq1Rf4Z55fgz7haOakIyM7+x/uCFwXpLPaeRzfoUtAEAuUZjZvFPjL/rk5rW693dIgn2hng1W7xbT7lWT4g==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz", + "integrity": "sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-wrap-function": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/helper-wrap-function": "^7.22.9" }, "engines": { "node": ">=6.9.0" @@ -711,20 +750,20 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz", - "integrity": "sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz", + "integrity": "sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-member-expression-to-functions": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/helper-optimise-call-expression": "^7.22.5" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-simple-access": { @@ -752,9 +791,9 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz", + "integrity": "sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==", "dev": true, "dependencies": { "@babel/types": "^7.22.5" @@ -791,14 +830,13 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.5.tgz", - "integrity": "sha512-bYqLIBSEshYcYQyfks8ewYA8S30yaGSeRslcvKMvoUk6HHPySbxHq9YRi6ghhzEU+yhQv9bP/jXnygkStOcqZw==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.9.tgz", + "integrity": "sha512-sZ+QzfauuUEfxSEjKFmi3qDSHgLsTPK/pEpoD/qonZKOtTPTLbf59oabPQ4rKekt9lFcj/hTZaOhWwFYrgjk+Q==", "dev": true, "dependencies": { "@babel/helper-function-name": "^7.22.5", "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", "@babel/types": "^7.22.5" }, "engines": { @@ -1294,6 +1332,18 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/plugin-transform-computed-properties": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", @@ -2131,372 +2181,48 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/types": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", - "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", + "node_modules/@babel/traverse/node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", - "dev": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", - "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", - "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", - "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", - "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", - "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", - "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", - "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", - "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", - "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", - "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", - "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", - "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", - "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", - "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", - "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", - "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", - "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", - "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", - "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], + "node_modules/@babel/types": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", + "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", + "to-fast-properties": "^2.0.0" + }, "engines": { - "node": ">=12" + "node": ">=6.9.0" } }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", - "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", - "cpu": [ - "arm64" - ], + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true, - "optional": true, - "os": [ - "win32" - ], "engines": { - "node": ">=12" + "node": ">=0.1.90" } }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", - "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", - "cpu": [ - "ia32" - ], + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "dev": true, - "optional": true, - "os": [ - "win32" - ], "engines": { - "node": ">=12" + "node": ">=10.0.0" } }, "node_modules/@esbuild/win32-x64": { @@ -3056,30 +2782,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@tufjs/models/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@tufjs/models/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", @@ -3134,9 +2836,9 @@ } }, "node_modules/@types/eslint": { - "version": "8.40.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.40.2.tgz", - "integrity": "sha512-PRVjQ4Eh9z9pmmtaq8nTjZjQwKFk7YIHIud3lRoKRBgUQjgjRmoGxxGEPXQkF+lH7QkHJRNr5F4aBgYCW0lqpQ==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.0.tgz", + "integrity": "sha512-gsF+c/0XOguWgaOgvFs+xnnRqt9GwgTvIks36WpE6ueeI4KCEHHd8K/CKHqhOqrJKsYH8m27kRzQEvWXAwXUTw==", "dev": true, "dependencies": { "@types/estree": "*", @@ -3217,9 +2919,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.4.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.0.tgz", - "integrity": "sha512-jfT7iTf/4kOQ9S7CHV9BIyRaQqHu67mOjsIQBC3BKZvzvUB6zLxEwJ6sBE3ozcvP8kF6Uk5PXN0Q+c0dfhGX0g==", + "version": "20.4.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.2.tgz", + "integrity": "sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==", "dev": true }, "node_modules/@types/qs": { @@ -3970,13 +3672,12 @@ "dev": true }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { @@ -4106,37 +3807,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/cacache/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/cacache/node_modules/glob": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.1.tgz", - "integrity": "sha512-9BKYcEeIs7QwlCYs+Y3GBvqAMISufUS0i2ELd11zpZjxI5V9iyRj0HgzB5/cLf2NY4vcYBTYzJ7GIui7j/4DOw==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2", - "path-scurry": "^1.10.0" - }, - "bin": { - "glob": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/cacache/node_modules/lru-cache": { "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", @@ -4146,21 +3816,6 @@ "node": ">=12" } }, - "node_modules/cacache/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -4193,9 +3848,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001513", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001513.tgz", - "integrity": "sha512-pnjGJo7SOOjAGytZZ203Em95MRM8Cr6jhCXNF/FAXTpCTRTECnqQWLpiTRqrFtdYcth8hf4WECUpkezuYsMVww==", + "version": "1.0.30001517", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz", + "integrity": "sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==", "dev": true, "funding": [ { @@ -4769,21 +4424,6 @@ "node": ">= 8" } }, - "node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/css-loader": { "version": "6.8.1", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz", @@ -5086,9 +4726,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.453", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.453.tgz", - "integrity": "sha512-BU8UtQz6CB3T7RIGhId4BjmjJVXQDujb0+amGL8jpcluFJr6lwspBOvkUbnttfpZCm4zFMHmjrX1QrdPWBBMjQ==", + "version": "1.4.464", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.464.tgz", + "integrity": "sha512-guZ84yoou4+ILNdj0XEbmGs6DEWj6zpVOWYpY09GU66yEb0DSYvP/biBPzHn0GuW/3RC/pnaYNUWlQE1fJYtgA==", "dev": true }, "node_modules/emoji-regex": { @@ -5439,6 +5079,12 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/exponential-backoff": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", @@ -5796,18 +5442,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", - "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -5877,20 +5511,6 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -5916,6 +5536,12 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/gauge/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -5971,20 +5597,22 @@ } }, "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz", + "integrity": "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^2.0.3", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/cjs/src/bin.js" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -6427,30 +6055,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/ignore-walk/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/ignore-walk/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/image-size": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", @@ -6465,9 +6069,9 @@ } }, "node_modules/immutable": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.0.tgz", - "integrity": "sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.1.tgz", + "integrity": "sha512-lj9cnmB/kVS0QHsJnYKD1uo3o39nrbKxszjnqS9Fr6NB7bZzW45U6WSGBPKXDL/CvDKqDNPA4r3DoDQ8GTxo2A==", "dev": true }, "node_modules/import-fresh": { @@ -7085,13 +6689,10 @@ } }, "node_modules/json-parse-even-better-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", - "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true }, "node_modules/json-schema-traverse": { "version": "1.0.0", @@ -7182,6 +6783,18 @@ "which": "^1.2.1" } }, + "node_modules/karma-chrome-launcher/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/karma-coverage": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.2.1.tgz", @@ -7199,6 +6812,28 @@ "node": ">=10.0.0" } }, + "node_modules/karma-coverage/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/karma-coverage/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/karma-jasmine": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz", @@ -7234,15 +6869,57 @@ "source-map-support": "^0.5.5" } }, - "node_modules/karma/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "node_modules/karma/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/karma/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/karma/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/karma/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, "node_modules/karma/node_modules/source-map": { @@ -7793,15 +7470,18 @@ "dev": true }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { @@ -8196,28 +7876,6 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, - "node_modules/nice-napi": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", - "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "!win32" - ], - "dependencies": { - "node-addon-api": "^3.0.0", - "node-gyp-build": "^4.2.2" - } - }, - "node_modules/node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", - "dev": true, - "optional": true - }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -8252,31 +7910,46 @@ "node": "^12.13 || ^14.13 || >=16" } }, - "node_modules/node-gyp-build": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", - "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", + "node_modules/node-gyp/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "optional": true, - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/node-gyp/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/node-gyp/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { - "isexe": "^2.0.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, - "bin": { - "node-which": "bin/node-which" + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">= 8" + "node": "*" } }, "node_modules/node-releases": { @@ -8794,12 +8467,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parse-json/node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, "node_modules/parse-node-version": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", @@ -8890,13 +8557,13 @@ "dev": true }, "node_modules/path-scurry": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.0.tgz", - "integrity": "sha512-tZFEaRQbMLjwrsmidsGJ6wDMv0iazJWk6SfIKnY4Xru8auXgmJkOBa5DUbYFcFD2Rzk2+KDlIiF0GVXNCbgC7g==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", "dev": true, "dependencies": { "lru-cache": "^9.1.1 || ^10.0.0", - "minipass": "^5.0.0 || ^6.0.2" + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -9124,6 +8791,16 @@ "postcss": "^8.0.0" } }, + "node_modules/postcss-url/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/postcss-url/node_modules/mime": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", @@ -9343,50 +9020,22 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/read-package-json/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/read-package-json/node_modules/glob": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.1.tgz", - "integrity": "sha512-9BKYcEeIs7QwlCYs+Y3GBvqAMISufUS0i2ELd11zpZjxI5V9iyRj0HgzB5/cLf2NY4vcYBTYzJ7GIui7j/4DOw==", + "node_modules/read-package-json-fast/node_modules/json-parse-even-better-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", + "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2", - "path-scurry": "^1.10.0" - }, - "bin": { - "glob": "dist/cjs/src/bin.js" - }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/read-package-json/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "node_modules/read-package-json/node_modules/json-parse-even-better-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", + "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/readable-stream": { @@ -9600,6 +9249,12 @@ "node": ">=8" } }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", @@ -9640,10 +9295,52 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/rollup": { - "version": "3.26.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.26.2.tgz", - "integrity": "sha512-6umBIGVz93er97pMgQO08LuH3m6PUb3jlDUUGFsNJB6VgTCUaDFpupf5JfU30529m/UKOgmiX+uY6Sx8cOYpLA==", + "version": "3.26.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.26.3.tgz", + "integrity": "sha512-7Tin0C8l86TkpcMtXvQu6saWH93nhG3dGQ1/+l5V2TDMceTxO7kDiK6GzbfLWNNxqJXm591PcEZUozZm51ogwQ==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -10082,10 +9779,16 @@ } }, "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", + "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/sigstore": { "version": "1.7.0", @@ -10681,6 +10384,48 @@ "node": ">=8" } }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -11341,12 +11086,6 @@ "ajv": "^6.9.1" } }, - "node_modules/webpack/node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, "node_modules/webpack/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -11395,15 +11134,18 @@ } }, "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "dependencies": { "isexe": "^2.0.0" }, "bin": { - "which": "bin/which" + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, "node_modules/wide-align": { diff --git a/package.json b/package.json index 5efc1f2..3e2e4bb 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "@angular/core": "^16.1.0", "@angular/platform-browser": "^16.1.0", "@angular/platform-browser-dynamic": "^16.1.0", + "@angular/router": "^16.1.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", "uuid": "^9.0.0", diff --git a/projects/ngx-xapi/README.md b/projects/ngx-xapi/README.md index 59a1494..5135c1d 100644 --- a/projects/ngx-xapi/README.md +++ b/projects/ngx-xapi/README.md @@ -21,17 +21,25 @@ The package contains the following entry-points: @berry-cloud/ngx-xapi/model @berry-cloud/ngx-xapi/client @berry-cloud/ngx-xapi/profiles/cmi5 +@berry-cloud/ngx-xapi/course ``` `@berry-cloud/ngx-xapi/model` contains the core types for xAPI. (Statement, Actor, Verb, etc.) -`@berry-cloud/ngx-xapi/client` contains utility methods for communicating with an LRS. +`@berry-cloud/ngx-xapi/client` contains utility functions for communicating with an LRS. `@berry-cloud/ngx-xapi/profiles/cmi5` contains types and extensions for the cmi5 profile. +`@berry-cloud/ngx-xapi/course` contains utility functions for a tincan or cmi5 course player. -All of the exported types and methods from model and client can be accessed directly from `@berry-cloud/ngx-xapi` entry point too. +## Samples + +See [BerryCloud/ngx-xapi GitHub repository](https://github.com/BerryCloud/ngx-xapi) for [Sample application](https://github.com/BerryCloud/ngx-xapi/tree/main/projects/samples) + +It contains simple examples for the client and the course utilities. -## Configuration injection +## Client Utilities -If you plan to use the client methods, you must provide an `XapiConfig` to be injected into the `XapiClient`. +The client utilities can be accessed via the injectable `XapiClient` service. + +If you plan to use this service, you must provide an `XapiConfig` to be injected into the `XapiClient`. The HttpClientModule must also be imported. For example: @@ -116,11 +124,7 @@ function xapiConfigFactory(userService: UserService) { export class AppModule {} ``` -## Samples - -See [BerryCloud/ngx-xapi GitHub repository](https://github.com/BerryCloud/ngx-xapi) for [Sample application](https://github.com/BerryCloud/ngx-xapi/tree/main/projects/samples) - -## Post Statement +### Post Statement ```TypeScript postPassedStatement() { @@ -144,7 +148,7 @@ postPassedStatement() { } ``` -## Post State +### Post State ```TypeScript postState(state: any) { @@ -184,11 +188,263 @@ Example:

``` -## Handling Responses +### Handling Responses -Most of the `XapiClient` utility methods return a `Observable>` object. Although in most cases `Observable` would be enough, some important properties of the response can be gathered only from the response headers: +Most of the `XapiClient` utility functions return a `Observable>` object. Although in most cases `Observable` would be enough, some important properties of the response can be gathered only from the response headers: - ETag - X-Experience-API-Consistent-Through You can access the response object itself via `response.body`; + +## Course Utilities + +The course utilities can be accessed via the injectable `XapiCourseService`. +It contains useful utility functions for a tincan or cmi5 compatible course player. +You can turn an angular application into a tincan and/or cmi5 compatible course within minutes. + +If you plan to use this service, you must provide an `Activity` object to be injected into the `XapiCourseService`. +This will be the activity object of the tincan/cmi5 course. + +For example: + +```TypeScript +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { XAPI_ACTIVITY } from '@berry-cloud/ngx-xapi/course'; +import { Activity } from '@berry-cloud/ngx-xapi/model'; +import { AppRoutingModule } from './app-routing.module'; +import { AppComponent } from './app.component'; + +@NgModule({ + declarations: [AppComponent], + imports: [ + BrowserModule, + // import HttpClientModule after BrowserModule. + HttpClientModule, + AppRoutingModule, + ], + providers: [ + provide: XAPI_ACTIVITY, + useValue: { + id: 'https://berrycloud.co.uk/xapi/sample', + definition: { + type: "http://adlnet.gov/expapi/activities/course", + name: { + 'en-US': 'BerryCloud Sample Course', + }, + }, + } as Activity, + ], + bootstrap: [AppComponent], +}) +export class AppModule {} +``` + +The `XapiCourseService` also automatically picks up the launch parameters from the URL when it is initialized. If the launch parameters come from a different source, or you want to hide the URL parameters before the service is initialized, you can provide this parameters via injection too: + +```TypeScript +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { XAPI_ACTIVITY, XAPI_LAUNCH, } from '@berry-cloud/ngx-xapi/course'; +import { Launch } from '@berry-cloud/ngx-xapi/profiles/cmi5'; +import { Activity } from '@berry-cloud/ngx-xapi/model'; +import { AppRoutingModule } from './app-routing.module'; +import { AppComponent } from './app.component'; + +@NgModule({ + declarations: [AppComponent], + imports: [ + BrowserModule, + // import HttpClientModule after BrowserModule. + HttpClientModule, + AppRoutingModule, + ], + providers: [ + provide: XAPI_ACTIVITY, + useValue: { + id: 'https://berrycloud.co.uk/xapi/sample', + definition: { + name: { + 'en-US': 'BerryCloud Sample Course', + }, + }, + } as Activity, + provide: XAPI_LAUNCH, + useValue: { + endpoint: 'https://mylrs.com/xapi'; + actor: { + name: 'test user' + mbox: 'mailto:test@example.com'; + }; + registration: 'bc6c2d1e-6f5e-4023-83fd-01d89d5bfa32'; + activityId: 'http://example.com/activity-from-launc-parameter'; + auth: 'Basic *******'; + fetch: 'https://mylms.com/token/12345678901234567890' + } as Launch, + ], + bootstrap: [AppComponent], +}) +export class AppModule {} +``` + +`XAPI_LAUNCH` can be a `Launch` or `Observable`, so you can also provide it via a factory function which loads it asynchronously from a file or the URL. + +The `XapiCourseService` can handle both the tincan `auth` parameter or the `cmi5` launch parameter. If the latter is provided it automatically fetches the auth token from the provided endpoint. In this case it also automatically loads the cmi5 launch-data, and will use it to decorate each further statements to be sent to the LRS. It also automatically sends the mandatory cmi5 `initialized` statement during initialization. + +If the launch parameters are not provided and neither cannot be picked from the URL bar the `XapiCourseService` service still initializes itself, but also throws an `Error`. +This error is logged into the console, but does not cease the application. It must be handled manually if needed. +If it's ignored then the course will still run, but it will silently ignore any http requests to the LRS. (Get functions will return 404, put/post functions will return 200 or 204.) It can be useful for testing or when a course is launched locally and no need to store progress data. + +If neither of the above initialization methods are suitable, you can create the `XapiCourseService` manually: + +``` + const courseService = new XapiCourseService(activity, launch); +``` + +The `XapiCourseService` uses the `XapiClient` internally, but extends its functionality with some useful convenience functions. +If these functions don't fit for your usecase, you can still get an `Observable` via the `getXapiClient()` function. It returns `undefined` if the launch parameters were not provided or were deficient. See the `XapiClient` examples above. + +## Sending a State + +After the `XapiCourseService` was successfully initialized, you can send a state with the `putState` or `postState` functions. They have only one mandatory argument, the state to be sent. All parameters needed for the LRS request are filled or defaulted in by the `XapiCourseService`. + +default stateId: `progress` +default content-type: `application/json` + +If you want to use different `stateId` or send a state with any other properties, you can override any of the defaults by providing the `StateParams` and `StateOptions` arguments: + +```TypeScript +this.courseService.putState( + state, + { + stateId: 'sample-state', + activityId: 'http://example.com/activities/sample-activity', + registration: '123456789-1234-1234-1234-123456789012', + agent: { + mbox: 'mailto:test@example.com', + }, + }, + { + contentType: 'application/json', + etag: '"123456789012345678901234567890123456789012"', + match: true + } +).subscribe( ... ); +``` + +## Getting a State + +You can use the `getState` function the same way as the above functions. Without any arguments it will try to get the state by the default parameters, but you can override any of them: + +```TypeScript +this.xapiCourseService.getState( + { + stateId: 'sample-state', + activityId: 'http://example.com/activities/sample-activity', + registration: '123456789-1234-1234-1234-123456789012', + agent: { + mbox: 'mailto:test@example.com', + }, + } +).subscribe( ... ); +``` + +## Sending a Statement + +You can send a default statement using the `postStatement` function: + +```TypeScript +this.xapiCourseService.postStatement(); +``` + +The default verb is `experienced`, the actor, object and registration properties are picked up from the launch parameters. +You can provide a `Partial` argument to the `postStatement` function where you can override any of these parameters: + +```TypeScript +this.courseService.postStatement( + { + verb: attempted, + object: { + id: 'http://example.com/activities/sample-activity', + definition: { + name: { + 'en-US': 'Sample Activity', + }, + }, + }, + actor: { + mbox: 'mailto:other@example.com', + }, + context: { + registration: '00000000-0000-0000-0000-000000000000', + language: 'en-US', + extensions: { + 'http://example.com/profiles/meetings/context/extensions/meeting-id': + '123456789', + 'http://example.com/profiles/meetings/context/extensions/meeting-name': + 'Example Meeting', + } + }, + } +) +``` + +If the `XapiCourseService` was initialized as a cmi5 course, then the properties from cmi5 `contextTemplate` are also added to the `Statement`. The mandatory parameters from the `contextTemplate` cannot be overridden. (these are the sessionId extension and the contextActivities arrays) +If you want full control over the `Statement`, you can configure it via a callback function. The incoming `defaultStatement` argument contains the prefilled Statement, but you can override any or all of the properties. (Do it only if you **really know** what are you doing) + +```TypeScript +this.courseService.postStatement( + (defaultStatement) => + ({ + ...defaultStatement, + verb: attempted, + context: { + contextActivities: { + grouping: [ + { + id: 'http://example.com/activities/world-domination', + definition: { + name: { + 'en-US': 'Nothing to see here', + }, + }, + }, + ], + parent: undefined + }, + extensions: undefined + }, + } as Statement) +); +``` + +## Convenience functions for sending Statements + +You can use the following functions for sending the most common tincan or cmi5 statements. All of them can be used with an extra statement-template or a callback function argument, like the `postStatement` function above: + +```TypeScript +sendCompletedStatement(); +sendPassedStatement(); +sendFailedStatement(); +sendProgressedStatement(progressValue); +sendScoredStatement(scoreValue, scaledValue); +``` + +In the latter functions the progress and score values are merged into the statement after the callback function was used. + +eg. sending a score for a test which has its own activity: + +```TypeScript +// For an IQ test, the scaled score is not applicable, so we send undefined +this.courseService.sendScoredStatement(152, undefined, { + object: { + id: 'http://example.com/activities/iq-test', + definition: { + name: { + 'en-US': 'IQ Test', + }, + }, + }, +}); +``` diff --git a/projects/ngx-xapi/client/src/lib/state-options.ts b/projects/ngx-xapi/client/src/lib/state-options.ts new file mode 100644 index 0000000..b83e683 --- /dev/null +++ b/projects/ngx-xapi/client/src/lib/state-options.ts @@ -0,0 +1,7 @@ +export interface StateOptions { + contentType: string; + etag?: string; + match?: boolean; +} + +export type DeleteStateOptions = Omit; diff --git a/projects/ngx-xapi/client/src/lib/xapi-client.ts b/projects/ngx-xapi/client/src/lib/xapi-client.ts index 565b263..dd987ef 100644 --- a/projects/ngx-xapi/client/src/lib/xapi-client.ts +++ b/projects/ngx-xapi/client/src/lib/xapi-client.ts @@ -28,6 +28,7 @@ import { ActivityProfileParams, ActivityProfilesParams, } from './activity-profile-params'; +import { DeleteStateOptions, StateOptions } from './state-options'; export interface XapiConfig { endpoint: string; @@ -110,7 +111,7 @@ export class XapiClient { putState( object: any, stateParams: StateParams, - options: { contentType: string; etag?: string; match?: boolean } + options: StateOptions ): Observable> { return this.config$.pipe( mergeMap((config) => { @@ -134,7 +135,7 @@ export class XapiClient { postState( object: any, stateParams: StateParams, - options: { contentType: string; etag?: string; match?: boolean } + options: StateOptions ): Observable> { return this.config$.pipe( mergeMap((config) => { @@ -159,7 +160,7 @@ export class XapiClient { */ deleteState( stateParams: StateParams, - options: { etag?: string; match?: boolean } + options: DeleteStateOptions ): Observable> { return this.config$.pipe( mergeMap((config) => { @@ -183,7 +184,7 @@ export class XapiClient { */ deleteStates( statesParams: DeleteStatesParams, - options: { etag?: string; match?: boolean } + options: DeleteStateOptions ): Observable> { return this.config$.pipe( mergeMap((config) => { @@ -699,7 +700,7 @@ export class XapiClient { } if (params.since) { - httpParams = httpParams.set('registration', params.since); + httpParams = httpParams.set('since', params.since); } if (params.stateId) { diff --git a/projects/ngx-xapi/client/src/public-api.ts b/projects/ngx-xapi/client/src/public-api.ts index fc4c669..8786e96 100644 --- a/projects/ngx-xapi/client/src/public-api.ts +++ b/projects/ngx-xapi/client/src/public-api.ts @@ -13,4 +13,5 @@ export { GetStatesParams, DeleteStatesParams, } from './lib/state-params'; +export * from './lib/state-options'; export * from './lib/statements-params'; diff --git a/projects/ngx-xapi/course/index.ts b/projects/ngx-xapi/course/index.ts new file mode 100644 index 0000000..49f243d --- /dev/null +++ b/projects/ngx-xapi/course/index.ts @@ -0,0 +1 @@ +export * from './src/public-api'; diff --git a/projects/ngx-xapi/course/ng-package.json b/projects/ngx-xapi/course/ng-package.json new file mode 100644 index 0000000..1f7447f --- /dev/null +++ b/projects/ngx-xapi/course/ng-package.json @@ -0,0 +1,6 @@ +{ + "dest": "../../../dist/ngx-xapi/course", + "lib": { + "entryFile": "src/public-api.ts" + } +} diff --git a/projects/ngx-xapi/course/src/lib/xapi-course.ts b/projects/ngx-xapi/course/src/lib/xapi-course.ts new file mode 100644 index 0000000..8995c36 --- /dev/null +++ b/projects/ngx-xapi/course/src/lib/xapi-course.ts @@ -0,0 +1,550 @@ +import { HttpClient, HttpResponse } from '@angular/common/http'; +import { + Activity, + Context, + Statement, + Verb, + completed, + experienced, + failed, + initialized, + passed, + progressed, + scored, +} from '@berry-cloud/ngx-xapi/model'; +import { + XapiClient, + StateParams, + StateOptions, +} from '@berry-cloud/ngx-xapi/client'; +import { + Observable, + ReplaySubject, + catchError, + forkJoin, + map, + mergeMap, + of, + take, + tap, +} from 'rxjs'; +import { v4 } from 'uuid'; +import { + FetchResult, + Launch, + LaunchData, +} from '@berry-cloud/ngx-xapi/profiles/cmi5'; +import { Inject, Injectable, InjectionToken, Optional } from '@angular/core'; +import { PlatformLocation } from '@angular/common'; + +export const DEFAULT_STATE_ID = 'state'; + +export const XAPI_LAUNCH = new InjectionToken>( + 'xapi.launch' +); + +export const XAPI_ACTIVITY = new InjectionToken< + Activity | Observable +>('xapi.activity'); + +@Injectable({ + providedIn: 'root', +}) +export class XapiCourseService { + public client = new ReplaySubject(1); + private course?: Activity; + private contextTemplate?: Context; + + private launch?: Launch; + private launchData?: LaunchData; + + constructor( + private httpClient: HttpClient, + @Inject(XAPI_ACTIVITY) + activity: Activity | Observable, + @Optional() + @Inject(XAPI_LAUNCH) + launchParameters?: Launch | Observable | null, + @Optional() + platformLocation?: PlatformLocation + ) { + const activity$ = this.calculateActivity(activity); + const launch$ = this.calculateLaunch(launchParameters, platformLocation); + + forkJoin([activity$, launch$]) + .pipe( + mergeMap(([activity, launch]) => { + this.course = { + ...activity, + }; + if (launch.activityId) { + this.course.id = launch.activityId; + } + if ( + launch.endpoint && + launch.actor && + (launch.auth || launch.fetch) + ) { + this.launch = launch; + + if (launch.fetch) { + // fetch authorization from the LMS + return fetchCmi5Authorization(httpClient, launch.fetch).pipe( + // create a client from the authorization token + map( + (authorization: string) => + new XapiClient(this.httpClient, { + endpoint: launch.endpoint, + authorization, + }) + ), + // load cmi5 launch data + mergeMap((client: XapiClient) => + this.getCmi5launchData(client).pipe( + map((launchData: LaunchData) => { + this.launchData = launchData; + this.contextTemplate = launchData.contextTemplate; + return client; + }) + ) + ), + // send cmi5 initialized statement + mergeMap((client: XapiClient) => + this.sendCmi5Initialization(client).pipe( + // initialize client only after the cmi5 initialized statement is sent + tap(() => { + this.client.next(client); + this.client.complete(); + }) + ) + ) + ); + } else if (launch.auth) { + // initialize only if all required parameters are provided + this.client.next( + new XapiClient(this.httpClient, { + endpoint: launch.endpoint, + authorization: launch.auth, + }) + ); + this.client.complete(); + } + return of(undefined); + } else if ( + launch.endpoint || + launch.actor || + launch.auth || + launch.fetch + ) { + throw new Error( + 'Cannot initialize XapiCourseService. Missing required launch parameters.' + ); + } else { + // If NO launch parameters are provided, + // we assume that the service is used for testing purposes, + // (eg. was launched manually from the browser) + // or was launched from a non-cmi5/non-xapi LMS. + // In this case, the service will not get/send any statements or states. + this.client.next(undefined); + this.client.complete(); + return of(undefined); + } + }), + catchError((error) => { + // In case of error, the client will not be initialized. + // This means that the service will not get/send any statements or states. + // But otherwise it will work normally. + this.client.next(undefined); + this.client.complete(); + // We rethrow the error so that the app can handle it. + throw error; + }), + take(1) + ) + .subscribe(); + } + + calculateLaunch( + launchParameters: Launch | Observable | null | undefined, + platformLocation: PlatformLocation | undefined + ): Observable { + if (launchParameters) { + if (launchParameters instanceof Observable) { + return launchParameters; + } else { + return of(launchParameters); + } + } else if (platformLocation?.search) { + const params = new URLSearchParams(platformLocation.search); + return of({ + endpoint: params.get('endpoint'), + auth: params.get('auth'), + actor: params.get('actor') && JSON.parse(params.get('actor')!), + activityId: params.get('activityId'), + registration: params.get('registration'), + fetch: params.get('fetch'), + } as Launch); + } else { + return of({} as Launch); + } + } + + calculateActivity( + activity: Activity | Observable + ): Observable { + return activity instanceof Observable ? activity : of(activity); + } + + private getCmi5launchData(client: XapiClient): Observable { + return client + .getState({ + activityId: this.course!.id, + agent: this.launch!.actor, + registration: this.launch!.registration, + stateId: 'LMS.LaunchData', + }) + .pipe( + map((response: HttpResponse) => { + if (response.body) { + return response.body; + } + throw new Error('Cannot fetch cmi5 launch data.'); + }) + ); + } + + private sendCmi5Initialization( + client: XapiClient + ): Observable> { + return client.postStatement(this.fillStatement(initialized)); + } + + getXapiClient(): Observable { + return this.client; + } + + getCmi5LaunchData() { + return this.launchData; + } + + getState(params?: Partial) { + return this.client.pipe( + mergeMap((client) => + client + ? client.getState(this.fillStateParams(params)) + : // return not found if the client is not initialized + // (no launch parameters were provided) + of(new HttpResponse({ body: null, status: 404 })) + ) + ); + } + + postState( + state: T, + params?: Partial, + options?: Partial + ) { + return this.client.pipe( + mergeMap((client) => + client + ? client.postState(state, this.fillStateParams(params), { + ...options, + contentType: options?.contentType ?? 'application/json', + }) + : of(new HttpResponse({ status: 204 })) + ) + ); + } + + putState( + state: T, + params?: Partial, + options?: Partial + ) { + return this.client.pipe( + mergeMap((client) => + client + ? client.putState(state, this.fillStateParams(params), { + ...options, + contentType: options?.contentType ?? 'application/json', + }) + : of(new HttpResponse({ status: 204 })) + ) + ); + } + + private fillStateParams(partial?: Partial): StateParams { + return { + ...partial, + ...(!partial?.activityId && { activityId: this.course?.id }), + ...(!partial?.agent && { agent: this.launch?.actor }), + ...(!partial?.registration && { + registration: this.launch?.registration, + }), + ...(!partial?.stateId && { stateId: DEFAULT_STATE_ID }), + } as StateParams; + } + + /** + * Sends a statement to the LRS. + * If the client is not initialized (no launch parameters were provided), + * returns OK and a random uuid. + * + * @param param Either a partial statement which will be extended by the default statement, + * or a function that takes the default statement as a parameter and returns a partial statement. + * If not provided, a default statement will be sent. + * + * The default statement contains the following fields: + * + * actor: the actor from the launch parameters + * + * verb: experienced + * + * object: the XAPI_ACTIVITY + optional id from the launch parameters + * + * context: the context template from the launch parameters + * + * context.registration: the registration from the launch parameters + * + * @remarks Beware! If you use a callback function, you can override the mandatory properties + * come from the cmi5 context template or launch parameters. + * (eg. registration, contextActivities, extensions etc.) + * In common scenarios, this is a bad practice. However, it can be useful in some cases. + * (eg. if you want to send a statement about a side effect with a different properties + * than the cmi5 context template or launch parameters provide for the main statement) + * + * If you use a partial statement, these mandatory properties will be merged with the partial statement. + * + * @returns An observable of the HttpResponse from the LRS. (The body is the uuid of the statement) + */ + postStatement( + param?: Partial | ((defaultStatement: Statement) => Statement) + ): Observable> { + return this.postStatementWithVerb(experienced, param); + } + + private postStatementWithVerb( + verb: Verb, + param?: Partial | ((defaultStatement: Statement) => Statement) + ): Observable> { + const statement = + typeof param === 'function' + ? param(this.fillStatement(verb)) + : this.fillStatement(verb, param); + + return this.client.pipe( + mergeMap((client) => + client + ? client.postStatement(statement) + : // return OK and a random uuid if the client is not initialized + // (no launch parameters were provided) + of(new HttpResponse({ status: 200, body: v4() })) + ) + ); + } + + private fillStatement(verb: Verb, partial?: Partial): Statement { + const statement = { + ...partial, + ...(!partial?.actor && { actor: this.launch?.actor }), + ...(!partial?.verb && { verb }), + ...(!partial?.object && { object: this.course }), + }; + + if (this.contextTemplate || this.launch?.registration) { + // if context template or registration is provided, we need to add a context + if (!partial?.context) { + statement.context = { + ...this.contextTemplate, + registration: this.launch?.registration, + }; + } else { + // merge context template with the provided context + statement.context = { + ...partial.context, + ...this.contextTemplate, + registration: this.launch?.registration, + ...(partial.context.extensions && { + extensions: { + ...partial.context?.extensions, + ...this.contextTemplate?.extensions, + }, + }), + ...(partial.context.contextActivities && { + contextActivities: { + ...partial.context.contextActivities, + ...this.contextTemplate?.contextActivities, + ...(partial.context.contextActivities.category && + this.contextTemplate?.contextActivities?.category && { + category: [ + ...partial.context.contextActivities.category, + ...this.contextTemplate.contextActivities.category, + ], + }), + ...(partial.context.contextActivities.parent && + this.contextTemplate?.contextActivities?.parent && { + parent: [ + ...partial.context.contextActivities.parent, + ...this.contextTemplate.contextActivities.parent, + ], + }), + ...(partial.context.contextActivities.grouping && + this.contextTemplate?.contextActivities?.grouping && { + grouping: [ + ...partial.context.contextActivities.grouping, + ...this.contextTemplate.contextActivities.grouping, + ], + }), + ...(partial.context.contextActivities.other && + this.contextTemplate?.contextActivities?.other && { + other: [ + ...partial.context.contextActivities.other, + ...this.contextTemplate.contextActivities.other, + ], + }), + }, + }), + }; + } + } + + return statement as Statement; + } + + /** + * Convenience method for sending a default completed statement. + * @see {@link postStatement} + */ + sendCompletedStatement( + param?: Partial | ((defaultStatement: Statement) => Statement) + ) { + return this.postStatementWithVerb(completed, param); + } + + /** + * Convenience method for sending a default passed statement. + * @see {@link postStatement} + */ + sendPassedStatement( + param?: Partial | ((defaultStatement: Statement) => Statement) + ) { + return this.postStatementWithVerb(passed, param); + } + + /** + * Convenience method for sending a default failed statement. + * @see {@link postStatement} + */ + sendFailedStatement( + param?: Partial | ((defaultStatement: Statement) => Statement) + ) { + return this.postStatementWithVerb(failed, param); + } + + /** + * Convenience method for sending a default progressed statement. + * @param progress The progress value (0-100) + * @see {@link postStatement} + */ + sendProgressedStatement( + progress: number, + param?: Partial | ((defaultStatement: Statement) => Statement) + ) { + let statement = + typeof param === 'function' + ? param(this.fillStatement(progressed)) + : this.fillStatement(progressed, param); + + statement = { + ...statement, + result: { + ...statement?.result, + extensions: { + ...statement?.result?.extensions, + 'https://w3id.org/xapi/cmi5/result/extensions/progress': progress, + }, + }, + }; + + return this.client.pipe( + mergeMap((client) => + client + ? client.postStatement(statement) + : of(new HttpResponse({ status: 200, body: v4() })) + ) + ); + } + + /** + * Convenience method for sending a default scored statement. + * @param score The score value (0-100) + * @see {@link postStatement} + */ + sendScoredStatement( + score: number, + scaled?: number, + param?: Partial | ((defaultStatement: Statement) => Statement) + ) { + let statement = + typeof param === 'function' + ? param(this.fillStatement(scored)) + : this.fillStatement(scored, param); + + const calculatedScaled = + statement?.result?.score?.min && statement?.result?.score?.max + ? (score - statement.result.score.min) / + (statement.result.score.max - statement.result.score.min) + : undefined; + + statement = { + ...statement, + result: { + ...statement?.result, + score: { + ...statement?.result?.score, + raw: score, + scaled: scaled ?? calculatedScaled, + }, + }, + }; + + return this.client.pipe( + mergeMap((client) => + client + ? client.postStatement(statement) + : of(new HttpResponse({ status: 200, body: v4() })) + ) + ); + } +} + +/** + * Convenience method for manually fetching cmi5 authorization token from the LMS. + * + * @remarks + * If this method is used, the XapiCourseService should be initialized with the returned token as an auth parameter. + * Also note that the XapiCourseService won't send the cmi5 initialized statement automatically in this case. + * Using this method is not recommended, but it can be useful if some non-common initialization is required. + */ +export function fetchCmi5Authorization( + httpClient: HttpClient, + fetch: string +): Observable { + return httpClient.post(fetch, null).pipe( + map((response) => { + if (response['auth-token']) { + return response['auth-token']; + } + if (response['error-text']) { + throw new Error(response['error-text']); + } + if (response['error-code']) { + throw new Error( + 'Cannot fetch cmi5 authorization token. Error code:' + + response['error-code'] + ); + } + throw new Error('Cannot fetch cmi5 authorization token.'); + }) + ); +} diff --git a/projects/ngx-xapi/course/src/public-api.ts b/projects/ngx-xapi/course/src/public-api.ts new file mode 100644 index 0000000..fa91456 --- /dev/null +++ b/projects/ngx-xapi/course/src/public-api.ts @@ -0,0 +1,5 @@ +/* + * Public API Surface of ngx-xapi/course + */ + +export * from './lib/xapi-course'; diff --git a/projects/ngx-xapi/package-lock.json b/projects/ngx-xapi/package-lock.json new file mode 100644 index 0000000..c44a0f4 --- /dev/null +++ b/projects/ngx-xapi/package-lock.json @@ -0,0 +1,85 @@ +{ + "name": "@berry-cloud/ngx-xapi", + "version": "0.3.3", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@berry-cloud/ngx-xapi", + "version": "0.3.3", + "license": " Apache-2.0", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": "^16.1.0", + "@angular/core": "^16.1.0", + "uuid": "^9.0.0" + } + }, + "node_modules/@angular/common": { + "version": "16.1.8", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-16.1.8.tgz", + "integrity": "sha512-Zm+Ysxdf74VwG3mbAqs2v1QFUR+h9RyJBXF5VFABEpgFw7NUOBKrayjJmKjgZ0TBAmL2+nXehJgcPph3zNp3sg==", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0" + }, + "peerDependencies": { + "@angular/core": "16.1.8", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/core": { + "version": "16.1.8", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-16.1.8.tgz", + "integrity": "sha512-XtOpY9HA85hPGrPwe1rgE8NJ3bFWbuJFx4SUlzB66k9B5jo8bD2Dxl/0id55RFS5gmvCe/Qhh0zoGyMpkWjMHA==", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.13.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "peer": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/tslib": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" + }, + "node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "peer": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/zone.js": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.13.1.tgz", + "integrity": "sha512-+bIeDAFEBYuXRuU3qGQvzdPap+N1zjM4KkBAiiQuVVCrHrhjDuY6VkUhNa5+U27+9w0q3fbKiMCbpJ0XzMmSWA==", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + } + } + } +} diff --git a/projects/ngx-xapi/profiles/cmi5/src/lib/fetch-result.ts b/projects/ngx-xapi/profiles/cmi5/src/lib/fetch-result.ts new file mode 100644 index 0000000..df0d42a --- /dev/null +++ b/projects/ngx-xapi/profiles/cmi5/src/lib/fetch-result.ts @@ -0,0 +1,5 @@ +export interface FetchResult { + 'auth-token': string; + 'error-text': string; + 'error-code': number; +} diff --git a/projects/ngx-xapi/profiles/cmi5/src/lib/launch.ts b/projects/ngx-xapi/profiles/cmi5/src/lib/launch.ts index ccc7f9a..06fb4ab 100644 --- a/projects/ngx-xapi/profiles/cmi5/src/lib/launch.ts +++ b/projects/ngx-xapi/profiles/cmi5/src/lib/launch.ts @@ -1,9 +1,10 @@ -import { Actor } from '@berry-cloud/ngx-xapi/model'; +import { Agent } from '@berry-cloud/ngx-xapi/model'; export interface Launch { endpoint: string; // URL - fetch: string; // URL - actor: Actor; // Agent - registration: string; // UUID - activityId: string; // URL + fetch?: string; // URL + actor: Agent; // Agent + registration?: string; // UUID + activityId?: string; // URL + auth?: string; // Authorization for xAPI } diff --git a/projects/ngx-xapi/profiles/cmi5/src/public-api.ts b/projects/ngx-xapi/profiles/cmi5/src/public-api.ts index 178a316..e154755 100644 --- a/projects/ngx-xapi/profiles/cmi5/src/public-api.ts +++ b/projects/ngx-xapi/profiles/cmi5/src/public-api.ts @@ -3,6 +3,7 @@ */ export * from './lib/cmi5-statements'; +export * from './lib/fetch-result'; export * from './lib/launch-data'; export * from './lib/launch'; export * from './lib/learner-preferences'; diff --git a/projects/ngx-xapi/src/public-api.ts b/projects/ngx-xapi/src/public-api.ts index 49cdfef..4037539 100644 --- a/projects/ngx-xapi/src/public-api.ts +++ b/projects/ngx-xapi/src/public-api.ts @@ -3,6 +3,3 @@ */ export const XAPI_VERSION = '1.0.3'; - -export * from '@berry-cloud/ngx-xapi/model'; -export * from '@berry-cloud/ngx-xapi/client'; diff --git a/projects/samples/src/app/app-routing.module.ts b/projects/samples/src/app/app-routing.module.ts new file mode 100644 index 0000000..5dfc1a4 --- /dev/null +++ b/projects/samples/src/app/app-routing.module.ts @@ -0,0 +1,21 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { XapiCourseComponent } from './xapi-course/xapi-course.component'; +import { SamplesComponent } from './samples.component'; + +const routes: Routes = [ + { + path: '', + component: SamplesComponent, + }, + { + path: 'xapi-course', + component: XapiCourseComponent, + }, +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes)], + exports: [RouterModule], +}) +export class AppRoutingModule {} diff --git a/projects/samples/src/app/app.component.html b/projects/samples/src/app/app.component.html index 83842d3..0680b43 100644 --- a/projects/samples/src/app/app.component.html +++ b/projects/samples/src/app/app.component.html @@ -1,9 +1 @@ -

Samples

- - - - - - - - + diff --git a/projects/samples/src/app/app.module.ts b/projects/samples/src/app/app.module.ts index 74fee46..81e0cdd 100644 --- a/projects/samples/src/app/app.module.ts +++ b/projects/samples/src/app/app.module.ts @@ -1,31 +1,49 @@ import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; - import { HttpClientModule } from '@angular/common/http'; -import { XAPI_CONFIG, XapiConfig } from '@berry-cloud/ngx-xapi'; import { AppComponent } from './app.component'; import { GetStateComponent } from './get-state/get-state.component'; import { GetStatementComponent } from './get-statement/get-statement.component'; import { PostStateComponent } from './post-state/post-state.component'; import { PostStatementComponent } from './post-statement/post-statement.component'; +import { AppRoutingModule } from './app-routing.module'; +import { SamplesComponent } from './samples.component'; +import { XapiCourseComponent } from './xapi-course/xapi-course.component'; +import { XAPI_ACTIVITY } from '@berry-cloud/ngx-xapi/course'; +import { XAPI_CONFIG, XapiConfig } from '@berry-cloud/ngx-xapi/client'; +import { Activity } from '@berry-cloud/ngx-xapi/model'; @NgModule({ declarations: [ AppComponent, + SamplesComponent, PostStatementComponent, GetStatementComponent, PostStateComponent, GetStateComponent, + XapiCourseComponent, ], - imports: [BrowserModule, HttpClientModule], + imports: [BrowserModule, HttpClientModule, AppRoutingModule], providers: [ { provide: XAPI_CONFIG, useValue: { + // You can create a free account here for testing endpoint: 'https://lrs.adlnet.gov/xapi/', - authorization: '', + authorization: 'Basic ***************', } as XapiConfig, }, + { + provide: XAPI_ACTIVITY, + useValue: { + id: 'https://berrycloud.co.uk/xapi/sample', + definition: { + name: { + 'en-US': 'BerryCloud Sample Course', + }, + }, + } as Activity, + }, ], bootstrap: [AppComponent], }) diff --git a/projects/samples/src/app/samples.component.html b/projects/samples/src/app/samples.component.html new file mode 100644 index 0000000..154a753 --- /dev/null +++ b/projects/samples/src/app/samples.component.html @@ -0,0 +1,27 @@ + + +

xAPI Client Samples

+ + + + + + + + diff --git a/projects/samples/src/app/samples.component.ts b/projects/samples/src/app/samples.component.ts new file mode 100644 index 0000000..c4d40bb --- /dev/null +++ b/projects/samples/src/app/samples.component.ts @@ -0,0 +1,7 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'samples-root', + templateUrl: './samples.component.html', +}) +export class SamplesComponent {} diff --git a/projects/samples/src/app/xapi-course/xapi-course.component.ts b/projects/samples/src/app/xapi-course/xapi-course.component.ts new file mode 100644 index 0000000..c473e9b --- /dev/null +++ b/projects/samples/src/app/xapi-course/xapi-course.component.ts @@ -0,0 +1,57 @@ +import { Component } from '@angular/core'; +import { Statement, answered, experienced } from '@berry-cloud/ngx-xapi/model'; +import { XapiCourseService } from '@berry-cloud/ngx-xapi/course'; +import { Observable } from 'rxjs'; + +@Component({ + selector: 'xapi-course', + template: ` +

xAPI Course Sample

+ +

+ Try deleting/modifiying/extending the launch parameters in the URL bar and + check the result in the developer tools' console and network tab

+ Mandatory parameters are: endpoint (URI), actor (JSON)
+ Authorization parameter is one of: auth (authorization header, tincan + only), fetch (URI, CMI5 only)
+ Optional parameters are: activityId (URI), registration (UUID) +

+ + Default Statement: {{ (defaultStatement$ | async)?.body }} +
+ Unique Activity: {{ (sampleStatement$ | async)?.body }} +
+ Completed Statement: {{ (completedStatement$ | async)?.body }} +
+ Callback Statement: {{ (callbackStatement$ | async)?.body }} + `, + providers: [], +}) +export class XapiCourseComponent { + defaultStatement$: Observable = this.courseService.postStatement(); + + sampleStatement$: Observable = this.courseService.postStatement({ + verb: experienced, + object: { + id: 'http://example.com/activities/sample-activity', + definition: { + name: { + 'en-US': 'Sample Activity', + }, + }, + }, + }); + + completedStatement$: Observable = + this.courseService.sendCompletedStatement(); + + callbackStatement$: Observable = this.courseService.postStatement( + (defaultStatement) => + ({ + ...defaultStatement, + verb: answered, + } as Statement) + ); + + constructor(public courseService: XapiCourseService) {} +} diff --git a/sonar-project.properties b/sonar-project.properties index fc592f4..44278b5 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -6,8 +6,8 @@ sonar.organization=berrycloud #sonar.projectVersion=1.0 # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. -sonar.sources=projects/ngx-xapi/client/src,projects/ngx-xapi/model/src,projects/ngx-xapi/profiles/cmi5/src,projects/samples/src -sonar.tests=projects/ngx-xapi/client/src,projects/ngx-xapi/model/src,projects/ngx-xapi/profiles/cmi5/src +sonar.sources=projects/ngx-xapi/client/src,projects/ngx-xapi/model/src,projects/ngx-xapi/course/src,projects/ngx-xapi/profiles/cmi5/src,projects/samples/src +sonar.tests=projects/ngx-xapi/client/src,projects/ngx-xapi/model/src,projects/ngx-xapi/course/src,projects/ngx-xapi/profiles/cmi5/src # Encoding of the source code. Default is default system encoding #sonar.sourceEncoding=UTF-8 diff --git a/tsconfig.json b/tsconfig.json index f1ad897..096b955 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,6 +21,9 @@ "@berry-cloud/ngx-xapi/client": [ "dist/ngx-xapi/client" ], + "@berry-cloud/ngx-xapi/course": [ + "dist/ngx-xapi/course" + ], "@berry-cloud/ngx-xapi": [ "dist/ngx-xapi" ]