From 7a1b0aab2431638454da3b4df3e454a453458fef Mon Sep 17 00:00:00 2001 From: Shiran Pasternak Date: Tue, 29 Nov 2022 12:57:16 -0500 Subject: [PATCH 1/3] Poll rate wasn't used correctly by EntityView --- .../core/data/entity-view/entity-view.spec.ts | 56 +++++++++++++------ .../core/data/entity-view/entity-view.ts | 2 +- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/@batch-flask/core/data/entity-view/entity-view.spec.ts b/src/@batch-flask/core/data/entity-view/entity-view.spec.ts index 6a47737f2a..4cdc90a42d 100644 --- a/src/@batch-flask/core/data/entity-view/entity-view.spec.ts +++ b/src/@batch-flask/core/data/entity-view/entity-view.spec.ts @@ -47,7 +47,7 @@ describe("EntityView", () => { view.fetch(); tick(); expect(dataSpy).toHaveBeenCalledTimes(1); - expect(item!.toJS()).toEqual(fake1); + expect(item.toJS()).toEqual(fake1); expect(dataSpy).toHaveBeenCalledWith({ parentId: "parent-1", id: "1" }); })); @@ -57,35 +57,59 @@ describe("EntityView", () => { view.item.subscribe(x => item = x); view.fetch(); expect(item).not.toBeFalsy(); - expect(item!.toJS()).toEqual({ id: "1", parentId: "parent-1", state: "creating", name: "Fake1" }); + expect(item.toJS()).toEqual({ id: "1", parentId: "parent-1", state: "creating", name: "Fake1" }); tick(); // This should be the return from the fetched data - expect(item!.toJS()).toEqual(fake1); + expect(item.toJS()).toEqual(fake1); })); it("Update the data when refreshing", fakeAsync(() => { view.item.subscribe(x => item = x); view.fetch(); tick(); - expect(item!.toJS()).toEqual(fake1); + expect(item.toJS()).toEqual(fake1); view.refresh(); tick(); - expect(item!.toJS()).toEqual(fake2); + expect(item.toJS()).toEqual(fake2); view.refresh(); tick(); - expect(item!.toJS()).toEqual(fake3); + expect(item.toJS()).toEqual(fake3); expect(dataSpy).toHaveBeenCalledTimes(3); })); - it("Update the params", fakeAsync(() => { + it("updates at the poll rate", fakeAsync(() => { + const cache2 = new DataCache(); + const dataSpy2 = jasmine.createSpy("supplyDataSpy") + .and.returnValues(...data.map(x => from(Promise.resolve(x)))); + const getter2 = new BasicEntityGetter(FakeModel, { + cache: () => cache2, + supplyData: dataSpy2, + }); + + const view2 = new EntityView({ + cache: () => cache2, + getter: getter2, + poll: 1000 + }); + view2.params = { parentId: "parent-1", id: "2" }; + + let item2: FakeModel; + view.item.subscribe(x => item = x); - view.params = { parentId: "parent-1", id: "2" }; + view2.item.subscribe(x => item2 = x); view.fetch(); + view2.fetch(); tick(); - expect(item!.toJS()).toEqual(fake1); - expect(dataSpy).toHaveBeenCalledWith({ parentId: "parent-1", id: "2" }); + expect(item.toJS()).toEqual(fake1); + expect(item2.toJS()).toEqual(fake1); + + tick(1000); + expect(item.toJS()).toEqual(fake1); + expect(item2.toJS()).toEqual(fake2); + + view2.dispose(); })); it("Update the params", fakeAsync(() => { @@ -93,21 +117,21 @@ describe("EntityView", () => { view.params = { parentId: "parent-1", id: "2" }; view.fetch(); tick(); - expect(item!.toJS()).toEqual(fake1); - expect(dataSpy).toHaveBeenCalledWith({parentId: "parent-1", id: "2" }); + expect(item.toJS()).toEqual(fake1); + expect(dataSpy).toHaveBeenCalledWith({ parentId: "parent-1", id: "2" }); })); it("Update the cache should update the item", fakeAsync(() => { view.item.subscribe(x => item = x); view.fetch(); tick(); - expect(item!.toJS()).toEqual(fake1); + expect(item.toJS()).toEqual(fake1); expect(dataSpy).toHaveBeenCalledTimes(1); - expect(dataSpy).toHaveBeenCalledWith({parentId: "parent-1", id: "1" }); + expect(dataSpy).toHaveBeenCalledWith({ parentId: "parent-1", id: "1" }); - cache.addItem(new FakeModel({...data[2], parentId: "parent-1"})); + cache.addItem(new FakeModel({ ...data[2], parentId: "parent-1" })); tick(); - expect(item!.toJS()).toEqual(fake3); + expect(item.toJS()).toEqual(fake3); })); describe("When it return a 404 error", () => { diff --git a/src/@batch-flask/core/data/entity-view/entity-view.ts b/src/@batch-flask/core/data/entity-view/entity-view.ts index f51a162cc6..b9cd15ced6 100644 --- a/src/@batch-flask/core/data/entity-view/entity-view.ts +++ b/src/@batch-flask/core/data/entity-view/entity-view.ts @@ -47,7 +47,7 @@ export class EntityView, TParams> extends GenericVie ); if (config.poll) { - this._pollTracker = this.startPoll(5000); + this._pollTracker = this.startPoll(config.poll); } } From 35108c8e0bb914990852dd1f54ef595c676302c5 Mon Sep 17 00:00:00 2001 From: Shiran Pasternak Date: Tue, 11 Oct 2022 18:31:11 -0400 Subject: [PATCH 2/3] Migrates to new Azure Storage API --- karma.conf.js | 1 - package-lock.json | 1984 ++++++----------- package.json | 3 +- scripts/azpipelines/update-latest.ts | 94 +- scripts/azpipelines/upload-to-storage.ts | 69 +- scripts/azpipelines/utils.ts | 80 +- .../application-create-dialog.component.ts | 5 +- .../blob-container-picker.component.ts | 3 +- ...cefile-cloud-file-dialog.component.spec.ts | 23 +- ...esourcefile-cloud-file-dialog.component.ts | 7 +- .../resourcefile-picker.component.ts | 3 +- .../browse/data-container-list.component.ts | 6 +- .../file-group-sas.component.ts | 5 +- .../decorators/container-lease-decorator.ts | 18 +- src/app/models/blob-container.ts | 11 + src/app/models/container-lease.ts | 16 +- .../services/core/data/storage-list-getter.ts | 28 +- .../storage/blob-storage-client-proxy.ts | 444 ++-- .../storage/models/blob-storage-result.ts | 4 - src/app/services/storage/models/index.ts | 3 +- .../storage/models/shared-access-policy.ts | 5 +- .../services/storage/models/storage-blob.ts | 137 ++ .../storage/models/storage-request-options.ts | 2 - .../services/storage/storage-blob.service.ts | 100 +- .../storage/storage-client-proxy-factory.ts | 20 +- .../storage/storage-client.service.ts | 29 +- .../storage/storage-container.service.ts | 10 +- src/client/client.module.ts | 2 + src/client/core/batch-explorer-application.ts | 7 +- src/client/core/index.ts | 1 + src/client/core/storage/index.ts | 1 + .../core/storage/storage-blob-adapter.spec.ts | 224 ++ .../core/storage/storage-blob-adapter.ts | 269 +++ src/common/constants/constants.ts | 16 +- 34 files changed, 1756 insertions(+), 1874 deletions(-) delete mode 100644 src/app/services/storage/models/blob-storage-result.ts create mode 100644 src/app/services/storage/models/storage-blob.ts delete mode 100644 src/app/services/storage/models/storage-request-options.ts create mode 100644 src/client/core/storage/index.ts create mode 100644 src/client/core/storage/storage-blob-adapter.spec.ts create mode 100644 src/client/core/storage/storage-blob-adapter.ts diff --git a/karma.conf.js b/karma.conf.js index 8c4ea346f7..0f959abb3d 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -95,7 +95,6 @@ module.exports = function(config) { { type: "cobertura", subdir: ".", file: "cobertura.xml" }, ] }, - // Can't enable yet has a conflict in dependency with azure-storage junitReporter: { outputDir: "./coverage" } diff --git a/package-lock.json b/package-lock.json index 89ab4df735..e02b14bad1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,9 +22,8 @@ "@angular/platform-server": "^11.0.0", "@angular/router": "^11.0.0", "@azure/msal-node": "^1.14.6", - "@azure/storage-blob": "^10.5.0", + "@azure/storage-blob": "^12.11.0", "applicationinsights": "^1.8.5", - "azure-storage": "^2.10.7", "chart.js": "^2.9.3", "chokidar": "^3.4.3", "commander": "^8.0.0", @@ -449,50 +448,119 @@ } }, "node_modules/@azure/abort-controller": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.0.4.tgz", - "integrity": "sha512-lNUmDRVGpanCsiUN3NWxFTdwmdFI53xwhkTFfHDGTYk46ca7Ind3nanJc+U6Zj9Tv+9nTCWRBscWEW1DyKOpTw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", "dependencies": { - "tslib": "^2.0.0" + "tslib": "^2.2.0" }, "engines": { - "node": ">=8.0.0" + "node": ">=12.0.0" } }, "node_modules/@azure/core-auth": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.2.0.tgz", - "integrity": "sha512-KUl+Nwn/Sm6Lw5d3U90m1jZfNSL087SPcqHLxwn2T6PupNKmcgsEbDjHB25gDvHO4h7pBsTlrdJAY7dz+Qk8GA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz", + "integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==", "dependencies": { "@azure/abort-controller": "^1.0.0", - "tslib": "^2.0.0" + "tslib": "^2.2.0" }, "engines": { - "node": ">=8.0.0" + "node": ">=12.0.0" } }, - "node_modules/@azure/ms-rest-js": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.2.3.tgz", - "integrity": "sha512-sXOhOu/37Tr8428f32Jwuwga975Xw64pYg1UeUwOBMhkNgtn5vUuNRa3fhmem+I6f8EKoi6hOsYDFlaHeZ52jA==", - "dependencies": { - "@azure/core-auth": "^1.1.4", - "@types/node-fetch": "^2.3.7", - "@types/tunnel": "0.0.1", - "abort-controller": "^3.0.0", - "form-data": "^2.5.0", - "node-fetch": "^2.6.0", - "tough-cookie": "^3.0.1", - "tslib": "^1.10.0", - "tunnel": "0.0.6", - "uuid": "^3.3.2", + "node_modules/@azure/core-http": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-2.2.7.tgz", + "integrity": "sha512-TyGMeDm90mkRS8XzSQbSMD+TqnWL1XKGCh0x0QVGMD8COH2yU0q5SaHm/IBEBkzcq0u73NhS/p57T3KVSgUFqQ==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/core-util": "^1.1.0", + "@azure/logger": "^1.0.0", + "@types/node-fetch": "^2.5.0", + "@types/tunnel": "^0.0.3", + "form-data": "^4.0.0", + "node-fetch": "^2.6.7", + "process": "^0.11.10", + "tough-cookie": "^4.0.0", + "tslib": "^2.2.0", + "tunnel": "^0.0.6", + "uuid": "^8.3.0", "xml2js": "^0.4.19" + }, + "engines": { + "node": ">=12.0.0" } }, - "node_modules/@azure/ms-rest-js/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "node_modules/@azure/core-http/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@azure/core-lro": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.4.0.tgz", + "integrity": "sha512-F65+rYkll1dpw3RGm8/SSiSj+/QkMeYDanzS/QKlM1dmuneVyXbO46C88V1MRHluLGdMP6qfD3vDRYALn0z0tQ==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-paging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.3.0.tgz", + "integrity": "sha512-H6Tg9eBm0brHqLy0OSAGzxIh1t4UL8eZVrSUMJ60Ra9cwq2pOskFqVpz2pYoHDsBY1jZ4V/P8LRGb5D5pmC6rg==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.0.0-preview.13", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", + "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", + "dependencies": { + "@opentelemetry/api": "^1.0.1", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.1.1.tgz", + "integrity": "sha512-A4TBYVQCtHOigFb2ETiiKFDocBoI1Zk2Ui1KpI42aJSIDexF7DHQFpnjonltXAIU/ceH+1fsZAWWgvX6/AKzog==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.3.tgz", + "integrity": "sha512-aK4s3Xxjrx3daZr3VylxejK3vG5ExXck5WOHDJ8in/k9AqlfIyFMMT1uG7u8mNjX+QRILTIn0/Xgschfh/dQ9g==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } }, "node_modules/@azure/msal-common": { "version": "9.0.2", @@ -524,20 +592,23 @@ } }, "node_modules/@azure/storage-blob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-10.5.0.tgz", - "integrity": "sha512-67+0EP7STy9BQgzvN1RgmSvXhxRd044eDgepX7zBp7XslBxz8YGo2cSLm9w5o5Qf1FLCRlwuziRMikaPCLMpVw==", + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.11.0.tgz", + "integrity": "sha512-na+FisoARuaOWaHWpmdtk3FeuTWf2VWamdJ9/TJJzj5ZdXPLC3juoDgFs6XVuJIoK30yuBpyFBEDXVRK4pB7Tg==", "dependencies": { - "@azure/ms-rest-js": "^2.0.0", + "@azure/abort-controller": "^1.0.0", + "@azure/core-http": "^2.0.0", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/logger": "^1.0.0", "events": "^3.0.0", - "tslib": "^1.9.3" + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" } }, - "node_modules/@azure/storage-blob/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, "node_modules/@babel/code-frame": { "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", @@ -1460,6 +1531,14 @@ "node": ">=10" } }, + "node_modules/@opentelemetry/api": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.2.0.tgz", + "integrity": "sha512-0nBr+VZNKm9tvNDZFstI3Pq1fCTEDK5OZTnVKNvBNAKgd0yIvmwsP4m61rEv7ZP+tOUjWJhROpxK5MsnlF911g==", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@playwright/test": { "version": "1.24.2", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.24.2.tgz", @@ -1484,11 +1563,14 @@ "node": ">=4" } }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", - "dev": true + "node_modules/@socket.io/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } }, "node_modules/@stylelint/postcss-css-in-js": { "version": "0.37.2", @@ -1533,6 +1615,12 @@ "moment": "^2.10.2" } }, + "node_modules/@types/component-emitter": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", + "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==", + "dev": true + }, "node_modules/@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", @@ -1998,9 +2086,9 @@ "dev": true }, "node_modules/@types/tunnel": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.1.tgz", - "integrity": "sha512-AOqu6bQu5MSWwYvehMXLukFHnupHrpZ8nvgae5Ggie9UwzDR1CCwoXgSSWNZJuyOlCdfdsWMA5F2LlmvyoTv8A==", + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", + "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==", "dependencies": { "@types/node": "*" } @@ -2422,25 +2510,14 @@ "integrity": "sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==", "dev": true }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", "dev": true, "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "mime-types": "~2.1.24", + "negotiator": "0.6.2" }, "engines": { "node": ">= 0.6" @@ -2495,6 +2572,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2735,20 +2813,6 @@ "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", "dev": true }, - "node_modules/app-builder-lib/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/app-builder-lib/node_modules/fs-extra": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", @@ -3055,14 +3119,6 @@ "node": ">= 6" } }, - "node_modules/asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, "node_modules/asn1.js": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", @@ -3095,6 +3151,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "optional": true, "engines": { "node": ">=0.8" } @@ -3242,61 +3300,12 @@ "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", "dev": true }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" - }, "node_modules/axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", "dev": true }, - "node_modules/azure-storage": { - "version": "2.10.7", - "resolved": "https://registry.npmjs.org/azure-storage/-/azure-storage-2.10.7.tgz", - "integrity": "sha512-4oeFGtn3Ziw/fGs/zkoIpKKtygnCVIcZwzJ7UQzKTxhkGQqVCByOFbYqMGYR3L+wOsunX9lNfD0jc51SQuKSSA==", - "deprecated": "Please note: newer packages @azure/storage-blob, @azure/storage-queue and @azure/storage-file are available as of November 2019 and @azure/data-tables is available as of June 2021. While the legacy azure-storage package will continue to receive critical bug fixes, we strongly encourage you to upgrade. Migration guide can be found: https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/storage/MigrationGuide.md", - "dependencies": { - "browserify-mime": "^1.2.9", - "extend": "^3.0.2", - "json-edm-parser": "~0.1.2", - "json-schema": "~0.4.0", - "md5.js": "^1.3.4", - "readable-stream": "^2.0.0", - "request": "^2.86.0", - "underscore": "^1.12.1", - "uuid": "^3.0.0", - "validator": "^13.7.0", - "xml2js": "~0.2.8", - "xmlbuilder": "^9.0.7" - }, - "engines": { - "node": ">= 0.8.26" - } - }, - "node_modules/azure-storage/node_modules/sax": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz", - "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=" - }, - "node_modules/azure-storage/node_modules/xml2js": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.2.8.tgz", - "integrity": "sha1-m4FpCTFjH/CdGVdUn69U9PmAs8I=", - "dependencies": { - "sax": "0.5.x" - } - }, "node_modules/babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -3604,14 +3613,6 @@ "resolved": "https://registry.npmjs.org/batch-processor/-/batch-processor-1.0.0.tgz", "integrity": "sha1-dclcMrdI4IUNEMKxaPa9vpiRrOg=" }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -3697,27 +3698,24 @@ "dev": true }, "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", "dev": true, "dependencies": { - "bytes": "3.1.2", + "bytes": "3.1.0", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", + "depd": "~1.1.2", + "http-errors": "1.7.2", "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">= 0.8" } }, "node_modules/body-parser/node_modules/debug": { @@ -3729,33 +3727,12 @@ "ms": "2.0.0" } }, - "node_modules/body-parser/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, - "node_modules/body-parser/node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/bonjour": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", @@ -3956,11 +3933,6 @@ "safe-buffer": "^5.1.2" } }, - "node_modules/browserify-mime": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/browserify-mime/-/browserify-mime-1.2.9.tgz", - "integrity": "sha1-rrGvKN5sDXpqLOQK22j/GEIq8x8=" - }, "node_modules/browserify-rsa": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", @@ -4432,9 +4404,9 @@ "dev": true }, "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", "dev": true, "engines": { "node": ">= 0.8" @@ -4602,14 +4574,20 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001309", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001309.tgz", - "integrity": "sha512-Pl8vfigmBXXq+/yUz1jUwULeq9xhMJznzdc/xwl4WclDAuebcTHVefpz8lE/bMI+UN7TOkSSe7B7RnZd6+dzjA==", + "version": "1.0.30001442", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001442.tgz", + "integrity": "sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow==", "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] }, "node_modules/canonical-path": { "version": "1.0.0", @@ -4617,11 +4595,6 @@ "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", "dev": true }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -5505,35 +5478,16 @@ } }, "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", "dependencies": { - "safe-buffer": "5.2.1" + "safe-buffer": "5.1.2" }, "engines": { "node": ">= 0.6" } }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", @@ -6179,17 +6133,6 @@ "d3-transition": "1" } }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/date-fns": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.19.0.tgz", @@ -6251,9 +6194,9 @@ } }, "node_modules/decode-uri-component": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.1.tgz", - "integrity": "sha512-XZHyaFJ6QMWhYmlz+UcmtaLeecNiXwkTGzCqG5WByt+1P1HnU6Siwf0TeP3OsZmlnGqQRSEMIxue0LLCaGY3dw==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "engines": { "node": ">=0.10" } @@ -6623,14 +6566,10 @@ } }, "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true }, "node_modules/detect-file": { "version": "1.0.0", @@ -7080,15 +7019,6 @@ "stream-shift": "^1.0.0" } }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -7662,9 +7592,9 @@ } }, "node_modules/engine.io": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz", - "integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.2.tgz", + "integrity": "sha512-v/7eGHxPvO2AWsksyx2PUsQvBafuvqs0jJJQ0FdmJG1b9qIvgSbqDRGwNhfk2XHaTTbTXiC4quRE8Q9nRjsrQQ==", "dev": true, "dependencies": { "@types/cookie": "^0.4.1", @@ -7675,7 +7605,7 @@ "cookie": "~0.4.1", "cors": "~2.8.5", "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", + "engine.io-parser": "~5.0.0", "ws": "~8.2.3" }, "engines": { @@ -7683,10 +7613,13 @@ } }, "node_modules/engine.io-parser": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", - "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz", + "integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==", "dev": true, + "dependencies": { + "@socket.io/base64-arraybuffer": "~1.0.2" + }, "engines": { "node": ">=10.0.0" } @@ -8711,20 +8644,12 @@ "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", "dev": true, "engines": { "node": ">= 0.6" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" - } - }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -8955,39 +8880,38 @@ } }, "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", "dev": true, "dependencies": { - "accepts": "~1.3.8", + "accepts": "~1.3.7", "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.4.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "2.0.0", + "depd": "~1.1.2", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "~1.1.2", "fresh": "0.5.2", - "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "2.4.1", + "on-finished": "~2.3.0", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -9003,9 +8927,9 @@ "dev": true }, "node_modules/express/node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", "dev": true, "engines": { "node": ">= 0.6" @@ -9020,80 +8944,12 @@ "ms": "2.0.0" } }, - "node_modules/express/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/express/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, - "node_modules/express/node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/express/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/ext-list": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", @@ -9120,7 +8976,8 @@ "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true }, "node_modules/extend-shallow": { "version": "3.0.2", @@ -9218,14 +9075,17 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true, "engines": [ "node >=0.6.0" - ] + ], + "optional": true }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, "node_modules/fast-diff": { "version": "1.2.0", @@ -9266,7 +9126,8 @@ "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -9795,14 +9656,6 @@ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "engines": { - "node": "*" - } - }, "node_modules/fork-ts-checker-webpack-plugin": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.1.tgz", @@ -9960,22 +9813,22 @@ } }, "node_modules/form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dependencies": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" }, "engines": { - "node": ">= 0.12" + "node": ">= 6" } }, "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", "dev": true, "engines": { "node": ">= 0.6" @@ -9996,7 +9849,7 @@ "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", "dev": true, "engines": { "node": ">= 0.6" @@ -10205,14 +10058,6 @@ "node": ">=0.10.0" } }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -10509,27 +10354,6 @@ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -10683,6 +10507,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, "dependencies": { "inherits": "^2.0.4", "readable-stream": "^3.6.0", @@ -10696,6 +10521,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -10709,6 +10535,7 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, "funding": [ { "type": "github", @@ -10728,6 +10555,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -10994,38 +10822,26 @@ "dev": true }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", "dev": true, "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" }, "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-errors/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "engines": { - "node": ">= 0.8" + "node": ">= 0.6" } }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } + "node_modules/http-errors/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true }, "node_modules/http-parser-js": { "version": "0.5.3", @@ -11088,20 +10904,6 @@ "node": ">=4.0.0" } }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, "node_modules/https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -11408,6 +11210,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true, "engines": { "node": ">=4" } @@ -11939,7 +11742,8 @@ "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true }, "node_modules/is-windows": { "version": "1.0.2", @@ -11988,11 +11792,6 @@ "node": ">=0.10.0" } }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, "node_modules/istanbul-instrumenter-loader": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.1.tgz", @@ -12325,11 +12124,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, "node_modules/jschardet": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-2.3.0.tgz", @@ -12367,14 +12161,6 @@ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" }, - "node_modules/json-edm-parser": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/json-edm-parser/-/json-edm-parser-0.1.2.tgz", - "integrity": "sha1-HmCw/vG8CvZ7wNFG393lSGzWFbQ=", - "dependencies": { - "jsonparse": "~1.2.0" - } - }, "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -12386,15 +12172,11 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -12405,7 +12187,9 @@ "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true, + "optional": true }, "node_modules/json3": { "version": "3.3.3", @@ -12437,14 +12221,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/jsonparse": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.2.0.tgz", - "integrity": "sha1-XAxWhRBxYOcv50ib3eoLRMK8Z70=", - "engines": [ - "node >= 0.2.0" - ] - }, "node_modules/jsonwebtoken": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", @@ -12474,20 +12250,6 @@ "node": ">=10" } }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", @@ -13408,6 +13170,7 @@ "version": "1.3.4", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "dev": true, "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1" @@ -13689,19 +13452,19 @@ } }, "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", + "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "version": "2.1.29", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", + "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", "dependencies": { - "mime-db": "1.52.0" + "mime-db": "1.46.0" }, "engines": { "node": ">= 0.6" @@ -14050,9 +13813,9 @@ "dev": true }, "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", "dev": true, "engines": { "node": ">= 0.6" @@ -14559,14 +14322,6 @@ "yargs-parser": "^13.1.2" } }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "engines": { - "node": "*" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -15382,11 +15137,6 @@ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -15853,7 +15603,6 @@ "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true, "engines": { "node": ">= 0.6.0" } @@ -15884,12 +15633,12 @@ "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=" }, "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", "dev": true, "dependencies": { - "forwarded": "0.2.0", + "forwarded": "~0.1.2", "ipaddr.js": "1.9.1" }, "engines": { @@ -15920,9 +15669,9 @@ "dev": true }, "node_modules/psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" }, "node_modules/public-encrypt": { "version": "4.0.3", @@ -16004,18 +15753,12 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, "engines": { "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, "node_modules/query-string": { @@ -16053,8 +15796,7 @@ "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" }, "node_modules/queue-microtask": { "version": "1.2.2", @@ -16114,13 +15856,13 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", "dev": true, "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", + "bytes": "3.1.0", + "http-errors": "1.7.2", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -16790,70 +16532,6 @@ "node": ">=0.10.0" } }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request/node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/request/node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/request/node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -16890,8 +16568,7 @@ "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" }, "node_modules/reserved-words": { "version": "0.1.2", @@ -17346,24 +17023,24 @@ } }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", "dev": true, "dependencies": { "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", + "depd": "~1.1.2", + "destroy": "~1.0.4", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "2.0.0", + "http-errors": "~1.7.2", "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", + "ms": "2.1.1", + "on-finished": "~2.3.0", "range-parser": "~1.2.1", - "statuses": "2.0.1" + "statuses": "~1.5.0" }, "engines": { "node": ">= 0.8.0" @@ -17381,18 +17058,9 @@ "node_modules/send/node_modules/debug/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, - "node_modules/send/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/send/node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -17406,32 +17074,11 @@ } }, "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, - "node_modules/send/node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/serialize-error": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", @@ -17531,15 +17178,15 @@ "dev": true }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", "dev": true, "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.17.1" }, "engines": { "node": ">= 0.8.0" @@ -17593,9 +17240,9 @@ "dev": true }, "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", "dev": true }, "node_modules/sha.js": { @@ -17649,20 +17296,6 @@ "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -18016,35 +17649,36 @@ } }, "node_modules/socket.io": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.3.tgz", - "integrity": "sha512-zdpnnKU+H6mOp7nYRXH4GNv1ux6HL6+lHL8g7Ds7Lj8CkdK1jJK/dlwsKDculbyOHifcJ0Pr/yeXnZQ5GeFrcg==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.1.tgz", + "integrity": "sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg==", "dev": true, "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "debug": "~4.3.2", - "engine.io": "~6.2.0", - "socket.io-adapter": "~2.4.0", - "socket.io-parser": "~4.2.0" + "engine.io": "~6.1.0", + "socket.io-adapter": "~2.3.3", + "socket.io-parser": "~4.0.4" }, "engines": { "node": ">=10.0.0" } }, "node_modules/socket.io-adapter": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz", - "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz", + "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==", "dev": true }, "node_modules/socket.io-parser": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", - "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.5.tgz", + "integrity": "sha512-sNjbT9dX63nqUFIOv95tTVm6elyIU4RvB1m8dOeZt+IgWwcWklFDOdmGcfo3zSiRsnR/3pJkjY5lfoGqEe4Eig==", "dev": true, "dependencies": { - "@socket.io/component-emitter": "~3.1.0", + "@types/component-emitter": "^1.2.10", + "component-emitter": "~1.3.0", "debug": "~4.3.1" }, "engines": { @@ -18321,30 +17955,6 @@ "dev": true, "optional": true }, - "node_modules/sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ssri": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", @@ -20037,9 +19647,9 @@ } }, "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", "dev": true, "engines": { "node": ">=0.6" @@ -20052,18 +19662,27 @@ "dev": true }, "node_modules/tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", + "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", "dependencies": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" }, "engines": { "node": ">=6" } }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -20259,11 +19878,6 @@ "node": "*" } }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -20393,11 +20007,6 @@ "through": "^2.3.8" } }, - "node_modules/underscore": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", - "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==" - }, "node_modules/unified": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.1.tgz", @@ -20732,6 +20341,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -20788,7 +20398,6 @@ "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" @@ -20884,6 +20493,7 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, "bin": { "uuid": "bin/uuid" } @@ -20904,14 +20514,6 @@ "spdx-expression-parse": "^3.0.0" } }, - "node_modules/validator": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", - "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", - "engines": { - "node": ">= 0.10" - } - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -20925,9 +20527,11 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, "engines": [ "node >=0.6.0" ], + "optional": true, "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -22410,6 +22014,7 @@ "version": "9.0.7", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", + "dev": true, "engines": { "node": ">=4.0" } @@ -22727,47 +22332,95 @@ } }, "@azure/abort-controller": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.0.4.tgz", - "integrity": "sha512-lNUmDRVGpanCsiUN3NWxFTdwmdFI53xwhkTFfHDGTYk46ca7Ind3nanJc+U6Zj9Tv+9nTCWRBscWEW1DyKOpTw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", "requires": { - "tslib": "^2.0.0" + "tslib": "^2.2.0" } }, "@azure/core-auth": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.2.0.tgz", - "integrity": "sha512-KUl+Nwn/Sm6Lw5d3U90m1jZfNSL087SPcqHLxwn2T6PupNKmcgsEbDjHB25gDvHO4h7pBsTlrdJAY7dz+Qk8GA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz", + "integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==", "requires": { "@azure/abort-controller": "^1.0.0", - "tslib": "^2.0.0" + "tslib": "^2.2.0" } }, - "@azure/ms-rest-js": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.2.3.tgz", - "integrity": "sha512-sXOhOu/37Tr8428f32Jwuwga975Xw64pYg1UeUwOBMhkNgtn5vUuNRa3fhmem+I6f8EKoi6hOsYDFlaHeZ52jA==", - "requires": { - "@azure/core-auth": "^1.1.4", - "@types/node-fetch": "^2.3.7", - "@types/tunnel": "0.0.1", - "abort-controller": "^3.0.0", - "form-data": "^2.5.0", - "node-fetch": "^2.6.0", - "tough-cookie": "^3.0.1", - "tslib": "^1.10.0", - "tunnel": "0.0.6", - "uuid": "^3.3.2", + "@azure/core-http": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-2.2.7.tgz", + "integrity": "sha512-TyGMeDm90mkRS8XzSQbSMD+TqnWL1XKGCh0x0QVGMD8COH2yU0q5SaHm/IBEBkzcq0u73NhS/p57T3KVSgUFqQ==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/core-util": "^1.1.0", + "@azure/logger": "^1.0.0", + "@types/node-fetch": "^2.5.0", + "@types/tunnel": "^0.0.3", + "form-data": "^4.0.0", + "node-fetch": "^2.6.7", + "process": "^0.11.10", + "tough-cookie": "^4.0.0", + "tslib": "^2.2.0", + "tunnel": "^0.0.6", + "uuid": "^8.3.0", "xml2js": "^0.4.19" }, "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" } } }, + "@azure/core-lro": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.4.0.tgz", + "integrity": "sha512-F65+rYkll1dpw3RGm8/SSiSj+/QkMeYDanzS/QKlM1dmuneVyXbO46C88V1MRHluLGdMP6qfD3vDRYALn0z0tQ==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + } + }, + "@azure/core-paging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.3.0.tgz", + "integrity": "sha512-H6Tg9eBm0brHqLy0OSAGzxIh1t4UL8eZVrSUMJ60Ra9cwq2pOskFqVpz2pYoHDsBY1jZ4V/P8LRGb5D5pmC6rg==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@azure/core-tracing": { + "version": "1.0.0-preview.13", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", + "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", + "requires": { + "@opentelemetry/api": "^1.0.1", + "tslib": "^2.2.0" + } + }, + "@azure/core-util": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.1.1.tgz", + "integrity": "sha512-A4TBYVQCtHOigFb2ETiiKFDocBoI1Zk2Ui1KpI42aJSIDexF7DHQFpnjonltXAIU/ceH+1fsZAWWgvX6/AKzog==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + } + }, + "@azure/logger": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.3.tgz", + "integrity": "sha512-aK4s3Xxjrx3daZr3VylxejK3vG5ExXck5WOHDJ8in/k9AqlfIyFMMT1uG7u8mNjX+QRILTIn0/Xgschfh/dQ9g==", + "requires": { + "tslib": "^2.2.0" + } + }, "@azure/msal-common": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-9.0.2.tgz", @@ -22791,20 +22444,18 @@ } }, "@azure/storage-blob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-10.5.0.tgz", - "integrity": "sha512-67+0EP7STy9BQgzvN1RgmSvXhxRd044eDgepX7zBp7XslBxz8YGo2cSLm9w5o5Qf1FLCRlwuziRMikaPCLMpVw==", + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.11.0.tgz", + "integrity": "sha512-na+FisoARuaOWaHWpmdtk3FeuTWf2VWamdJ9/TJJzj5ZdXPLC3juoDgFs6XVuJIoK30yuBpyFBEDXVRK4pB7Tg==", "requires": { - "@azure/ms-rest-js": "^2.0.0", + "@azure/abort-controller": "^1.0.0", + "@azure/core-http": "^2.0.0", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/logger": "^1.0.0", "events": "^3.0.0", - "tslib": "^1.9.3" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } + "tslib": "^2.2.0" } }, "@babel/code-frame": { @@ -23518,6 +23169,11 @@ } } }, + "@opentelemetry/api": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.2.0.tgz", + "integrity": "sha512-0nBr+VZNKm9tvNDZFstI3Pq1fCTEDK5OZTnVKNvBNAKgd0yIvmwsP4m61rEv7ZP+tOUjWJhROpxK5MsnlF911g==" + }, "@playwright/test": { "version": "1.24.2", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.24.2.tgz", @@ -23533,10 +23189,10 @@ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==" }, - "@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", + "@socket.io/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==", "dev": true }, "@stylelint/postcss-css-in-js": { @@ -23572,6 +23228,12 @@ "moment": "^2.10.2" } }, + "@types/component-emitter": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", + "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==", + "dev": true + }, "@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", @@ -24035,9 +23697,9 @@ "dev": true }, "@types/tunnel": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.1.tgz", - "integrity": "sha512-AOqu6bQu5MSWwYvehMXLukFHnupHrpZ8nvgae5Ggie9UwzDR1CCwoXgSSWNZJuyOlCdfdsWMA5F2LlmvyoTv8A==", + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", + "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==", "requires": { "@types/node": "*" } @@ -24377,22 +24039,14 @@ "integrity": "sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==", "dev": true }, - "abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "requires": { - "event-target-shim": "^5.0.0" - } - }, "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", "dev": true, "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "mime-types": "~2.1.24", + "negotiator": "0.6.2" } }, "acorn": { @@ -24430,6 +24084,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -24624,17 +24279,6 @@ "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", "dev": true }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, "fs-extra": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", @@ -24873,14 +24517,6 @@ } } }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, "asn1.js": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", @@ -24931,7 +24567,9 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "optional": true }, "assign-symbols": { "version": "1.0.0", @@ -25031,56 +24669,12 @@ } } }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" - }, "axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", "dev": true }, - "azure-storage": { - "version": "2.10.7", - "resolved": "https://registry.npmjs.org/azure-storage/-/azure-storage-2.10.7.tgz", - "integrity": "sha512-4oeFGtn3Ziw/fGs/zkoIpKKtygnCVIcZwzJ7UQzKTxhkGQqVCByOFbYqMGYR3L+wOsunX9lNfD0jc51SQuKSSA==", - "requires": { - "browserify-mime": "^1.2.9", - "extend": "^3.0.2", - "json-edm-parser": "~0.1.2", - "json-schema": "~0.4.0", - "md5.js": "^1.3.4", - "readable-stream": "^2.0.0", - "request": "^2.86.0", - "underscore": "^1.12.1", - "uuid": "^3.0.0", - "validator": "^13.7.0", - "xml2js": "~0.2.8", - "xmlbuilder": "^9.0.7" - }, - "dependencies": { - "sax": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz", - "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=" - }, - "xml2js": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.2.8.tgz", - "integrity": "sha1-m4FpCTFjH/CdGVdUn69U9PmAs8I=", - "requires": { - "sax": "0.5.x" - } - } - } - }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -25341,14 +24935,6 @@ "resolved": "https://registry.npmjs.org/batch-processor/-/batch-processor-1.0.0.tgz", "integrity": "sha1-dclcMrdI4IUNEMKxaPa9vpiRrOg=" }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "^0.14.3" - } - }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -25430,23 +25016,21 @@ "dev": true }, "body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", "dev": true, "requires": { - "bytes": "3.1.2", + "bytes": "3.1.0", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", + "depd": "~1.1.2", + "http-errors": "1.7.2", "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" }, "dependencies": { "debug": { @@ -25458,26 +25042,11 @@ "ms": "2.0.0" } }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } } } }, @@ -25641,11 +25210,6 @@ "safe-buffer": "^5.1.2" } }, - "browserify-mime": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/browserify-mime/-/browserify-mime-1.2.9.tgz", - "integrity": "sha1-rrGvKN5sDXpqLOQK22j/GEIq8x8=" - }, "browserify-rsa": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", @@ -26003,9 +25567,9 @@ "dev": true }, "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", "dev": true }, "cacache": { @@ -26138,9 +25702,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001309", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001309.tgz", - "integrity": "sha512-Pl8vfigmBXXq+/yUz1jUwULeq9xhMJznzdc/xwl4WclDAuebcTHVefpz8lE/bMI+UN7TOkSSe7B7RnZd6+dzjA==", + "version": "1.0.30001442", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001442.tgz", + "integrity": "sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow==", "dev": true }, "canonical-path": { @@ -26149,11 +25713,6 @@ "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", "dev": true }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -26896,18 +26455,11 @@ "dev": true }, "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", "requires": { - "safe-buffer": "5.2.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - } + "safe-buffer": "5.1.2" } }, "content-type": { @@ -27481,14 +27033,6 @@ "d3-transition": "1" } }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - } - }, "date-fns": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.19.0.tgz", @@ -27526,9 +27070,9 @@ } }, "decode-uri-component": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.1.tgz", - "integrity": "sha512-XZHyaFJ6QMWhYmlz+UcmtaLeecNiXwkTGzCqG5WByt+1P1HnU6Siwf0TeP3OsZmlnGqQRSEMIxue0LLCaGY3dw==" + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" }, "decompress": { "version": "4.2.1", @@ -27821,9 +27365,9 @@ } }, "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", "dev": true }, "detect-file": { @@ -28202,15 +27746,6 @@ "stream-shift": "^1.0.0" } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -28668,9 +28203,9 @@ } }, "engine.io": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz", - "integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.2.tgz", + "integrity": "sha512-v/7eGHxPvO2AWsksyx2PUsQvBafuvqs0jJJQ0FdmJG1b9qIvgSbqDRGwNhfk2XHaTTbTXiC4quRE8Q9nRjsrQQ==", "dev": true, "requires": { "@types/cookie": "^0.4.1", @@ -28681,15 +28216,18 @@ "cookie": "~0.4.1", "cors": "~2.8.5", "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", + "engine.io-parser": "~5.0.0", "ws": "~8.2.3" } }, "engine.io-parser": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", - "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", - "dev": true + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz", + "integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==", + "dev": true, + "requires": { + "@socket.io/base64-arraybuffer": "~1.0.2" + } }, "enhanced-resolve": { "version": "4.3.0", @@ -29470,14 +29008,9 @@ "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", "dev": true }, - "event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" - }, "eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -29666,39 +29199,38 @@ } }, "express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", "dev": true, "requires": { - "accepts": "~1.3.8", + "accepts": "~1.3.7", "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.4.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "2.0.0", + "depd": "~1.1.2", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "~1.1.2", "fresh": "0.5.2", - "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "2.4.1", + "on-finished": "~2.3.0", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -29711,9 +29243,9 @@ "dev": true }, "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", "dev": true }, "debug": { @@ -29725,53 +29257,11 @@ "ms": "2.0.0" } }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - }, - "finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - } - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true } } }, @@ -29795,7 +29285,8 @@ "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true }, "extend-shallow": { "version": "3.0.2", @@ -29878,12 +29369,15 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true, + "optional": true }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, "fast-diff": { "version": "1.2.0", @@ -29920,7 +29414,8 @@ "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "fast-levenshtein": { "version": "2.0.6", @@ -30356,11 +29851,6 @@ } } }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, "fork-ts-checker-webpack-plugin": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.1.tgz", @@ -30474,19 +29964,19 @@ } }, "form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "requires": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", "dev": true }, "fragment-cache": { @@ -30501,7 +29991,7 @@ "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", "dev": true }, "from2": { @@ -30672,14 +30162,6 @@ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - } - }, "github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -30905,20 +30387,6 @@ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, "hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -31034,6 +30502,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, "requires": { "inherits": "^2.0.4", "readable-stream": "^3.6.0", @@ -31044,6 +30513,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -31053,12 +30523,14 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, "requires": { "safe-buffer": "~5.2.0" } @@ -31279,28 +30751,22 @@ "dev": true }, "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", "dev": true, "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" }, "dependencies": { - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true } } @@ -31356,16 +30822,6 @@ "micromatch": "^3.1.10" } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -31597,7 +31053,8 @@ "ip-regex": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true }, "ipaddr.js": { "version": "1.9.1", @@ -31944,7 +31401,8 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true }, "is-windows": { "version": "1.0.2", @@ -31981,11 +31439,6 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, "istanbul-instrumenter-loader": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.1.tgz", @@ -32257,11 +31710,6 @@ "esprima": "^4.0.0" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, "jschardet": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-2.3.0.tgz", @@ -32284,14 +31732,6 @@ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" }, - "json-edm-parser": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/json-edm-parser/-/json-edm-parser-0.1.2.tgz", - "integrity": "sha1-HmCw/vG8CvZ7wNFG393lSGzWFbQ=", - "requires": { - "jsonparse": "~1.2.0" - } - }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -32303,15 +31743,11 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -32322,7 +31758,9 @@ "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true, + "optional": true }, "json3": { "version": "3.3.3", @@ -32348,11 +31786,6 @@ "graceful-fs": "^4.1.6" } }, - "jsonparse": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.2.0.tgz", - "integrity": "sha1-XAxWhRBxYOcv50ib3eoLRMK8Z70=" - }, "jsonwebtoken": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", @@ -32374,17 +31807,6 @@ } } }, - "jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - } - }, "jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", @@ -33128,6 +32550,7 @@ "version": "1.3.4", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "dev": true, "requires": { "hash-base": "^3.0.0", "inherits": "^2.0.1" @@ -33350,16 +32773,16 @@ "dev": true }, "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", + "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==" }, "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "version": "2.1.29", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", + "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", "requires": { - "mime-db": "1.52.0" + "mime-db": "1.46.0" } }, "mimic-response": { @@ -33645,9 +33068,9 @@ "dev": true }, "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", "dev": true }, "neo-async": { @@ -34071,11 +33494,6 @@ } } }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -34715,11 +34133,6 @@ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -35075,8 +34488,7 @@ "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" }, "process-nextick-args": { "version": "1.0.7", @@ -35101,12 +34513,12 @@ "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=" }, "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", "dev": true, "requires": { - "forwarded": "0.2.0", + "forwarded": "~0.1.2", "ipaddr.js": "1.9.1" } }, @@ -35134,9 +34546,9 @@ "dev": true }, "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" }, "public-encrypt": { "version": "4.0.3", @@ -35213,13 +34625,10 @@ "dev": true }, "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dev": true, - "requires": { - "side-channel": "^1.0.4" - } + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true }, "query-string": { "version": "5.1.1", @@ -35246,8 +34655,7 @@ "querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" }, "queue-microtask": { "version": "1.2.2", @@ -35287,13 +34695,13 @@ "dev": true }, "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", "dev": true, "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", + "bytes": "3.1.0", + "http-errors": "1.7.2", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } @@ -35809,59 +35217,6 @@ "is-finite": "^1.0.0" } }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - } - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -35889,8 +35244,7 @@ "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" }, "reserved-words": { "version": "0.1.2", @@ -36236,24 +35590,24 @@ } }, "send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", "dev": true, "requires": { "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", + "depd": "~1.1.2", + "destroy": "~1.0.4", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "2.0.0", + "http-errors": "~1.7.2", "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", + "ms": "2.1.1", + "on-finished": "~2.3.0", "range-parser": "~1.2.1", - "statuses": "2.0.1" + "statuses": "~1.5.0" }, "dependencies": { "debug": { @@ -36268,17 +35622,11 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true } } }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -36286,24 +35634,9 @@ "dev": true }, "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true } } @@ -36393,15 +35726,15 @@ } }, "serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", "dev": true, "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.17.1" } }, "set-blocking": { @@ -36445,9 +35778,9 @@ "dev": true }, "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", "dev": true }, "sha.js": { @@ -36489,17 +35822,6 @@ "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -36779,32 +36101,33 @@ } }, "socket.io": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.3.tgz", - "integrity": "sha512-zdpnnKU+H6mOp7nYRXH4GNv1ux6HL6+lHL8g7Ds7Lj8CkdK1jJK/dlwsKDculbyOHifcJ0Pr/yeXnZQ5GeFrcg==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.1.tgz", + "integrity": "sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg==", "dev": true, "requires": { "accepts": "~1.3.4", "base64id": "~2.0.0", "debug": "~4.3.2", - "engine.io": "~6.2.0", - "socket.io-adapter": "~2.4.0", - "socket.io-parser": "~4.2.0" + "engine.io": "~6.1.0", + "socket.io-adapter": "~2.3.3", + "socket.io-parser": "~4.0.4" } }, "socket.io-adapter": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz", - "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz", + "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==", "dev": true }, "socket.io-parser": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", - "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.5.tgz", + "integrity": "sha512-sNjbT9dX63nqUFIOv95tTVm6elyIU4RvB1m8dOeZt+IgWwcWklFDOdmGcfo3zSiRsnR/3pJkjY5lfoGqEe4Eig==", "dev": true, "requires": { - "@socket.io/component-emitter": "~3.1.0", + "@types/component-emitter": "^1.2.10", + "component-emitter": "~1.3.0", "debug": "~4.3.1" } }, @@ -37046,22 +36369,6 @@ "dev": true, "optional": true }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, "ssri": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", @@ -38438,9 +37745,9 @@ } }, "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", "dev": true }, "toposort": { @@ -38450,13 +37757,21 @@ "dev": true }, "tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", + "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", "requires": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "dependencies": { + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==" + } } }, "tr46": { @@ -38608,11 +37923,6 @@ "safe-buffer": "^5.0.1" } }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -38709,11 +38019,6 @@ "through": "^2.3.8" } }, - "underscore": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", - "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==" - }, "unified": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.1.tgz", @@ -38969,6 +38274,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "requires": { "punycode": "^2.1.0" } @@ -39025,7 +38331,6 @@ "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, "requires": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" @@ -39103,7 +38408,8 @@ "uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true }, "v8-compile-cache": { "version": "2.3.0", @@ -39121,11 +38427,6 @@ "spdx-expression-parse": "^3.0.0" } }, - "validator": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", - "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==" - }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -39136,6 +38437,8 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "optional": true, "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -40337,7 +39640,8 @@ "xmlbuilder": { "version": "9.0.7", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", + "dev": true }, "xtend": { "version": "4.0.2", diff --git a/package.json b/package.json index a22e9a531a..78e80ce522 100644 --- a/package.json +++ b/package.json @@ -174,9 +174,8 @@ "@angular/platform-server": "^11.0.0", "@angular/router": "^11.0.0", "@azure/msal-node": "^1.14.6", - "@azure/storage-blob": "^10.5.0", + "@azure/storage-blob": "^12.11.0", "applicationinsights": "^1.8.5", - "azure-storage": "^2.10.7", "chart.js": "^2.9.3", "chokidar": "^3.4.3", "commander": "^8.0.0", diff --git a/scripts/azpipelines/update-latest.ts b/scripts/azpipelines/update-latest.ts index ddac194475..2c64265bc8 100644 --- a/scripts/azpipelines/update-latest.ts +++ b/scripts/azpipelines/update-latest.ts @@ -1,14 +1,19 @@ // eslint-disable no-console -import * as azureStorage from "azure-storage"; import makeDir from "make-dir"; import * as path from "path"; import * as fs from "fs"; -import { getManifest, getContainerName } from "./utils"; +import { getManifest, getContainerName, BlobStorageClient } from "./utils"; import { promisify } from "util"; const copyFile = promisify(fs.copyFile); -const stagingDir = path.join(process.env.AGENT_TEMPDIRECTORY, "batchexplorer-github"); +if (!process.env.AGENT_TEMPDIRECTORY) { + throw new Error( + "Required AGENT_TEMPDIRECTORY environment variable is empty" + ); +} +const stagingDir = path.join(process.env.AGENT_TEMPDIRECTORY, + "batchexplorer-github"); console.log("Env", process.env); const storageAccountName = process.env.AZURE_STORAGE_ACCOUNT; const storageAccountKey = process.argv[2]; @@ -16,60 +21,37 @@ const storageAccountKey = process.argv[2]; console.log("Artifact staging directory is", stagingDir); console.log(`##vso[task.setvariable variable=BE_GITHUB_ARTIFACTS_DIR]${stagingDir}`) +if (!storageAccountName) { + console.error(`No storage account name found in AZURE_STORAGE_ACCOUNT`); + process.exit(-1) +} + if (!storageAccountKey) { console.error("No storage account key passed"); process.exit(-1); } console.log("Uploading to storage account:", storageAccountName); -const blobService = azureStorage.createBlobService(storageAccountName, storageAccountKey); - -function sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); -} -async function getProperties(container, blob): Promise { - return new Promise((resolve, reject) => { - blobService.getBlobProperties(container, blob, (error, result) => { - if (error) { return reject(error); } - resolve(result); - }); - }); -} +const storageClient = new BlobStorageClient(storageAccountName, + storageAccountKey); -async function waitCopy(container, blob) { - while (true) { - const properties = await getProperties(container, blob); - const copyStatus = properties.copy && properties.copy.status; - switch (copyStatus) { - case "success": - return true; - case "aborted": - throw new Error("Copy was aborted"); - case "failed": - throw new Error("Copy has failed"); - case "pending": - console.log(`Copy "${blob}"is pending`); +async function copyBlob(source, container, blob) { + const poller = await storageClient.beginCopyBlob(source, container, blob); + try { + return await poller.pollUntilDone(); + } catch (error) { + switch (error.name) { + case "PollerCancelledError": + throw new Error(`Copy was cancelled: ${error.message}`); + case "PollerStoppedError": + throw new Error(`Copy was stopped: ${error.message}`); + default: + throw new Error(`Copy failed: ${error.message}`); } - sleep(5000); } } -async function startCopyBlob(source, container, blob) { - return new Promise((resolve, reject) => { - blobService.startCopyBlob(source, container, blob, (error, result) => { - if (error) { return reject(error); } - - resolve(result); - }); - }); -} - -async function copyBlob(source, container, blob) { - await startCopyBlob(source, container, blob); - return waitCopy(container, blob); -} - function getLatestFile(os) { switch (os) { case "darwin": @@ -83,9 +65,16 @@ function getLatestFile(os) { async function copyFilesToArtifactStaging(os) { const manifest = getManifest(os); - console.log(`Copy ${manifest.files.length} files for os: ${os}`); - for (const file of manifest.files) { - await copyFile(path.join(os, file.path), path.join(stagingDir, file.path)) + if (manifest.files) { + console.log(`Copy ${manifest.files.length} files for os: ${os}`); + for (const file of manifest.files) { + if (file.path) { + await copyFile( + path.join(os, file.path), + path.join(stagingDir, file.path) + ); + } + } } } @@ -101,10 +90,15 @@ async function updateLatest(os) { const manifest = getManifest(os); console.log(`##vso[task.setvariable variable=BE_RELEASE_VERSION]${manifest.version}`) console.log(`Updating latest for os: ${os}`); + if (!manifest.buildType) { + throw new Error( + "Manifest does not contain required value for buildType" + ); + } const container = getContainerName(manifest.buildType); const latestFile = getLatestFile(os); - const orgiginalBlob = `${manifest.version}/${latestFile}`; - const sourceUrl = blobService.getUrl(container, orgiginalBlob); + const originalBlob = `${manifest.version}/${latestFile}`; + const sourceUrl = storageClient.getUrl(container, originalBlob); console.log("Copying", sourceUrl, container, latestFile); return copyBlob(sourceUrl, container, latestFile); } diff --git a/scripts/azpipelines/upload-to-storage.ts b/scripts/azpipelines/upload-to-storage.ts index c540236d75..6f0ab5a3d3 100644 --- a/scripts/azpipelines/upload-to-storage.ts +++ b/scripts/azpipelines/upload-to-storage.ts @@ -1,8 +1,7 @@ // eslint-disable no-console import * as fs from "fs"; import * as path from "path"; -import * as AzureStorage from "azure-storage"; -import { getManifest, getContainerName } from "./utils"; +import { getManifest, getContainerName, BlobStorageClient } from "./utils"; import * as crypto from "crypto"; const storageAccountName = process.env.AZURE_STORAGE_ACCOUNT; @@ -11,50 +10,32 @@ const attemptNumber = Number(process.env.Release_AttemptNumber); console.log(`This is the ${attemptNumber} try to release`); +if (!storageAccountName) { + console.error(`No storage account name found in AZURE_STORAGE_ACCOUNT`); + process.exit(-1); +} + if (!storageAccountKey) { console.error("No storage account key passed"); process.exit(-1); } console.log("Uploading to storage account:", storageAccountName); -const blobService = AzureStorage.createBlobService(storageAccountName, storageAccountKey); function computeFileMd5(filename) { const data = fs.readFileSync(filename); return crypto.createHash("md5").update(data).digest("base64"); } -async function getBlob(container, blobName): Promise { - return new Promise((resolve, reject) => { - blobService.getBlobProperties(container, blobName, (error, result) => { - if (error) { - return reject(error); - } - - resolve(result); - }); - }); -} +const storageClient = new BlobStorageClient(storageAccountName, + storageAccountKey); async function createBlobFromLocalFile(container, filename, blobName, override = false) { - const options: AzureStorage.BlobService.CreateBlockBlobRequestOptions = {}; - if (!override) { - options.accessConditions = AzureStorage.AccessCondition.generateIfNotExistsCondition(); - } - - return new Promise((resolve, reject) => { - blobService.createBlockBlobFromLocalFile(container, blobName, filename, options, - (error, result, response) => { - - if (error) { - reject(error); - return; - } + const response = await storageClient.createBlob(container, blobName, + filename, override); - console.log("Uploaded", result, response); - resolve(result); - }); - }); + console.log("Uploaded", response); + return response; } async function uploadToBlob(container, filename, blobName, override = false) { @@ -62,10 +43,10 @@ async function uploadToBlob(container, filename, blobName, override = false) { try { return await createBlobFromLocalFile(container, filename, blobName, override); } catch (error) { - if (error.code === "BlobAlreadyExists") { - const blob = await getBlob(container, blobName); + if (error.details.errorCode === "BlobAlreadyExists") { + const blob = await storageClient.getBlob(container, blobName); const md5 = computeFileMd5(filename); - const blobMd5 = blob.contentSettings && blob.contentSettings.contentMD5; + const blobMd5 = blob.blobContentMD5?.toString(); if (md5 === blobMd5) { console.log(`Already uploaded ${filename} skipping(Md5 hash matched)`); } else { @@ -79,10 +60,22 @@ async function uploadToBlob(container, filename, blobName, override = false) { async function uploadFiles(os) { const manifest = getManifest(os); - console.log(`Uploading ${manifest.files.length} files for os: ${os}`); - const container = getContainerName(manifest.buildType); - for (const file of manifest.files) { - await uploadToBlob(container, path.join(os, file.path), file.remotePath); + if (manifest.files) { + console.log(`Uploading ${manifest.files?.length} files for os: ${os}`); + if (!manifest.buildType) { + throw new Error( + "Manifest does not contain required value for buildType" + ); + } + const container = getContainerName(manifest.buildType); + for (const file of manifest.files) { + if (file.path) { + await uploadToBlob(container, path.join(os, file.path), + file.remotePath); + } + } + } else { + console.error(`Cannot get manifest for ${os}`); } } diff --git a/scripts/azpipelines/utils.ts b/scripts/azpipelines/utils.ts index 3bc48dd822..d1d0a3b0d6 100644 --- a/scripts/azpipelines/utils.ts +++ b/scripts/azpipelines/utils.ts @@ -1,3 +1,4 @@ +import { BlobBeginCopyFromURLResponse, BlobDownloadResponseParsed, BlobGetPropertiesResponse, BlobUploadCommonResponse, BlockBlobClient, PollerLike, PollOperationState, StorageSharedKeyCredential } from "@azure/storage-blob"; import * as fs from "fs"; import * as path from "path"; @@ -13,7 +14,11 @@ interface Manifest { } export function getManifest(os: string): Manifest { - return JSON.parse(fs.readFileSync(path.join(os, "manifest.json")).toString()); + const filePath = path.join(os, "manifest.json"); + if (!fs.existsSync(filePath)) { + throw new Error(`Manifest path ${filePath} does not exist`); + } + return JSON.parse(fs.readFileSync(filePath).toString()) || {}; } export function getContainerName(buildType: string): string { @@ -26,3 +31,76 @@ export function getContainerName(buildType: string): string { return "test"; } } + + +export function storageURL( + account: string, + container?: string, + blob?: string +): string { + let url = `https://${account}.blob.core.windows.net`; + if (container) { + url = `${url}/${container}`; + if (blob) { + url = `${url}/${blob}`; + } + } + return url; +} + +export type CopyPoller = PollerLike< + PollOperationState, + BlobBeginCopyFromURLResponse +>; +export class BlobStorageClient { + private credential: StorageSharedKeyCredential; + constructor(private accountName: string, accountKey: string) { + this.credential = new StorageSharedKeyCredential(accountName, + accountKey); + } + + private getBlobClient(container: string, blob: string) { + return new BlockBlobClient( + storageURL(this.accountName, container, blob), + this.credential + ) + } + + public getUrl(container: string, blob: string): string { + return storageURL(this.accountName, container, blob); + } + + public async createBlob( + container: string, + blob: string, + filename: string, + override = true + ): + Promise { + const blobClient = this.getBlobClient(container, blob); + const conditions = override ? {} : { ifNoneMatch: "*" }; + const response = await blobClient.uploadFile(filename, { conditions }); + return response; + } + + public async getBlob(container: string, blob: string): + Promise { + return this.getBlobClient(container, blob).download(0); + } + + public async getBlobProperties(container: string, blob: string): + Promise { + return this.getBlobClient(container, blob).getProperties(); + } + + public async beginCopyBlob(source: string, container: string, blob: string): + Promise { + const client = this.getBlobClient(container, blob); + return client.beginCopyFromURL(source, { + intervalInMs: 5000, + onProgress(state) { + console.log(`Copy "${blob}" is pending ${state.copyProgress}`); + }, + }) + } +} diff --git a/src/app/components/application/action/create/application-create-dialog.component.ts b/src/app/components/application/action/create/application-create-dialog.component.ts index cfba66eb86..1b6660aa6b 100644 --- a/src/app/components/application/action/create/application-create-dialog.component.ts +++ b/src/app/components/application/action/create/application-create-dialog.component.ts @@ -8,7 +8,7 @@ import { BatchApplication, BatchApplicationPackage } from "app/models"; import { applicationToCreateFormModel } from "app/models/forms"; import { BatchApplicationPackageService, BatchApplicationService } from "app/services"; import { StorageBlobService } from "app/services/storage"; -import * as storage from "azure-storage"; +import { UploadFileResult } from "app/services/storage/models/storage-blob"; import { Constants } from "common"; import { Observable, of, throwError } from "rxjs"; import { catchError, share, switchMap, tap } from "rxjs/operators"; @@ -103,7 +103,8 @@ export class ApplicationCreateDialogComponent { ); } - private _uploadAppPackage(file: File, sasUrl: string): Observable { + private _uploadAppPackage(file: File, sasUrl: string): + Observable { if (!this.hasValidFile()) { return throwError("Valid file not selected"); } diff --git a/src/app/components/common/blob-container-picker/blob-container-picker.component.ts b/src/app/components/common/blob-container-picker/blob-container-picker.component.ts index 144e39ac02..032fd3d35d 100644 --- a/src/app/components/common/blob-container-picker/blob-container-picker.component.ts +++ b/src/app/components/common/blob-container-picker/blob-container-picker.component.ts @@ -10,6 +10,7 @@ import { BlobContainer } from "app/models"; import { AutoStorageService, ListContainerParams, StorageContainerService, } from "app/services/storage"; +import { PermissionType } from "app/services/storage/models"; import { List } from "immutable"; import { DateTime } from "luxon"; import { BehaviorSubject, Observable, Subject, combineLatest } from "rxjs"; @@ -47,7 +48,7 @@ export class BlobContainerPickerComponent implements ControlValueAccessor, OnCha /** * Permission for the sas url if having output as sas url */ - @Input() public sasPermissions: string = "rl"; + @Input() public sasPermissions: PermissionType[] = ["r"]; public containers: List; public container = new FormControl(); diff --git a/src/app/components/common/resourcefile-picker/resourcefile-cloud-file-dialog/resourcefile-cloud-file-dialog.component.spec.ts b/src/app/components/common/resourcefile-picker/resourcefile-cloud-file-dialog/resourcefile-cloud-file-dialog.component.spec.ts index 665fee1b46..5953c86527 100644 --- a/src/app/components/common/resourcefile-picker/resourcefile-cloud-file-dialog/resourcefile-cloud-file-dialog.component.spec.ts +++ b/src/app/components/common/resourcefile-picker/resourcefile-cloud-file-dialog/resourcefile-cloud-file-dialog.component.spec.ts @@ -11,7 +11,6 @@ import { File, FileExplorerConfig, FormModule } from "@batch-flask/ui"; import { ArmBatchAccount, ArmSubscription, StorageAccount } from "app/models"; import { BatchAccountService, StorageAccountService } from "app/services"; import { AutoStorageService, StorageBlobService, StorageContainerService } from "app/services/storage"; -import { BlobUtilities } from "azure-storage"; import { List } from "immutable"; import { of } from "rxjs"; import { ResourceFileCloudFileDialogComponent } from "./resourcefile-cloud-file-dialog.component"; @@ -219,7 +218,7 @@ describe("ResourceFileCloudFileDialogComponent", () => { expect(blobServiceSpy.list).toHaveBeenCalledOnce(); expect(blobServiceSpy.list).toHaveBeenCalledWith("auto-storage-id", "foobar", { folder: "path/to/folder", - limit: 1, + maxPages: 1, }, true); expect(containerServiceSpy.generateSharedAccessUrl).not.toHaveBeenCalledOnce(); @@ -255,15 +254,15 @@ describe("ResourceFileCloudFileDialogComponent", () => { expect(blobServiceSpy.list).toHaveBeenCalledOnce(); expect(blobServiceSpy.list).toHaveBeenCalledWith("auto-storage-id", "foobar", { folder: "path/to/file.sh", - limit: 1, + maxPages: 1, }, true); expect(blobServiceSpy.generateSharedAccessBlobUrl).toHaveBeenCalledOnce(); expect(blobServiceSpy.generateSharedAccessBlobUrl).toHaveBeenCalledWith( "auto-storage-id", "foobar", "path/to/file.sh", { - AccessPolicy: jasmine.objectContaining({ - Permissions: BlobUtilities.SharedAccessPermissions.READ, - }), - }); + AccessPolicy: jasmine.objectContaining({ + Permissions: ["r"], + }), + }); }); it("updated the current selection", () => { @@ -304,16 +303,16 @@ describe("ResourceFileCloudFileDialogComponent", () => { expect(blobServiceSpy.list).toHaveBeenCalledOnce(); expect(blobServiceSpy.list).toHaveBeenCalledWith("new-storage-acc-1", "foobar", { folder: "path/to/folder", - limit: 1, + maxPages: 1, }, true); expect(containerServiceSpy.generateSharedAccessUrl).toHaveBeenCalled(); expect(containerServiceSpy.generateSharedAccessUrl).toHaveBeenCalledWith( "new-storage-acc-1", "foobar", { - AccessPolicy: jasmine.objectContaining({ - Permissions: BlobUtilities.SharedAccessPermissions.READ, - }), - }); + AccessPolicy: jasmine.objectContaining({ + Permissions: ["r"], + }), + }); }); it("updated the current selection", () => { diff --git a/src/app/components/common/resourcefile-picker/resourcefile-cloud-file-dialog/resourcefile-cloud-file-dialog.component.ts b/src/app/components/common/resourcefile-picker/resourcefile-cloud-file-dialog/resourcefile-cloud-file-dialog.component.ts index 8445ef989c..7bfb80a8cd 100644 --- a/src/app/components/common/resourcefile-picker/resourcefile-cloud-file-dialog/resourcefile-cloud-file-dialog.component.ts +++ b/src/app/components/common/resourcefile-picker/resourcefile-cloud-file-dialog/resourcefile-cloud-file-dialog.component.ts @@ -8,7 +8,6 @@ import { BatchAccountService, StorageAccountService } from "app/services"; import { AutoStorageService, StorageBlobService, StorageContainerService } from "app/services/storage"; import { SharedAccessPolicy } from "app/services/storage/models"; import { ArmResourceUtils, StorageUtils } from "app/utils"; -import { BlobUtilities } from "azure-storage"; import { DateTime } from "luxon"; import { BehaviorSubject, Observable, Subject, of } from "rxjs"; import { @@ -178,7 +177,7 @@ export class ResourceFileCloudFileDialogComponent implements OnInit, OnDestroy { return this.blobService.list(storageAccountId, containerName, { folder: path, - limit: 1, + maxPages: 1, }, true, ).pipe( @@ -203,7 +202,7 @@ export class ResourceFileCloudFileDialogComponent implements OnInit, OnDestroy { const sas: SharedAccessPolicy = { AccessPolicy: { - Permissions: BlobUtilities.SharedAccessPermissions.READ, + Permissions: ["r"], Start: new Date(), Expiry: DateTime.local().plus({ weeks: 1 }).toJSDate(), }, @@ -226,7 +225,7 @@ export class ResourceFileCloudFileDialogComponent implements OnInit, OnDestroy { const sas: SharedAccessPolicy = { AccessPolicy: { - Permissions: BlobUtilities.SharedAccessPermissions.READ, + Permissions: ["r"], Start: new Date(), Expiry: DateTime.local().plus({ weeks: 1 }).toJSDate(), }, diff --git a/src/app/components/common/resourcefile-picker/resourcefile-picker.component.ts b/src/app/components/common/resourcefile-picker/resourcefile-picker.component.ts index 5fb378490d..336959d23b 100644 --- a/src/app/components/common/resourcefile-picker/resourcefile-picker.component.ts +++ b/src/app/components/common/resourcefile-picker/resourcefile-picker.component.ts @@ -11,7 +11,6 @@ import { CloudPathUtils, DragUtils, SecureUtils, UrlUtils } from "@batch-flask/u import { ResourceFileAttributes } from "app/models"; import { AutoStorageService, StorageBlobService, StorageContainerService } from "app/services/storage"; import { SharedAccessPolicy } from "app/services/storage/models"; -import { BlobUtilities } from "azure-storage"; import { BEUserConfiguration } from "common"; import { DateTime } from "luxon"; import * as path from "path"; @@ -202,7 +201,7 @@ export class ResourcefilePickerComponent implements ControlValueAccessor, OnDest this.changeDetector.detectChanges(); const sas: SharedAccessPolicy = { AccessPolicy: { - Permissions: BlobUtilities.SharedAccessPermissions.READ, + Permissions: ["r"], Start: new Date(), Expiry: DateTime.local().plus({ weeks: 1 }).toJSDate(), }, diff --git a/src/app/components/data/browse/data-container-list.component.ts b/src/app/components/data/browse/data-container-list.component.ts index 3dc6d6b0a7..d7794028f9 100644 --- a/src/app/components/data/browse/data-container-list.component.ts +++ b/src/app/components/data/browse/data-container-list.component.ts @@ -5,7 +5,7 @@ import { import { ActivatedRoute } from "@angular/router"; import { Filter, ListSelection, ListView, autobind } from "@batch-flask/core"; import { ListBaseComponent, LoadingStatus, QuickListItemStatus } from "@batch-flask/ui"; -import { BlobContainer, LeaseStatus } from "app/models"; +import { BlobContainer } from "app/models"; import { ListContainerParams, StorageContainerService } from "app/services/storage"; import { ComponentUtils } from "app/utils"; import { Constants } from "common"; @@ -114,8 +114,8 @@ export class DataContainerListComponent extends ListBaseComponent implements OnI } public containerStatus(container: BlobContainer): QuickListItemStatus { - switch (container.lease && container.lease.status) { - case LeaseStatus.locked: + switch (container.lease?.status) { + case "locked": return QuickListItemStatus.warning; default: return QuickListItemStatus.normal; diff --git a/src/app/components/data/shared/file-group-sas/file-group-sas.component.ts b/src/app/components/data/shared/file-group-sas/file-group-sas.component.ts index 5caf2a14fd..92f74b12d3 100644 --- a/src/app/components/data/shared/file-group-sas/file-group-sas.component.ts +++ b/src/app/components/data/shared/file-group-sas/file-group-sas.component.ts @@ -5,6 +5,7 @@ import { import { ListView, autobind } from "@batch-flask/core"; import { BlobContainer } from "app/models"; import { AutoStorageService, ListContainerParams, StorageContainerService } from "app/services/storage"; +import { SharedAccessPolicy } from "app/services/storage/models"; import { Constants } from "common"; import { List } from "immutable"; import { DateTime } from "luxon"; @@ -113,9 +114,9 @@ export class FileGroupSasComponent implements ControlValueAccessor, OnChanges, O * Blob Container read/write/list access policy that is valid for 7 days, The maximum * lifetime of a task in Batch. */ - const accessPolicy = { + const accessPolicy: SharedAccessPolicy = { AccessPolicy: { - Permissions: this.allowWrite ? "rwl" : "rl", + Permissions: (this.allowWrite ? ["r", "w", "l"] : ["r", "l"]), ResourceTypes: "CONTAINER", Services: "BLOB", Start: DateTime.utc().minus({ minutes: 15 }).toJSDate(), diff --git a/src/app/decorators/container-lease-decorator.ts b/src/app/decorators/container-lease-decorator.ts index 0c540675f3..65a8260051 100644 --- a/src/app/decorators/container-lease-decorator.ts +++ b/src/app/decorators/container-lease-decorator.ts @@ -16,12 +16,10 @@ export class ContainerLeaseDecorator extends DecoratorBase { private _leaseStatusField(status: LeaseStatus): string { switch (status) { - case LeaseStatus.locked: + case "locked": return "Locked"; - case LeaseStatus.unlocked: + case "unlocked": return "Unlocked"; - case LeaseStatus.unspecified: - return "Unspecified"; default: return this.stringField(status); @@ -30,18 +28,16 @@ export class ContainerLeaseDecorator extends DecoratorBase { private _leaseStateField(state: LeaseState): string { switch (state) { - case LeaseState.available: + case "available": return "Available"; - case LeaseState.breaking: + case "breaking": return "Breaking"; - case LeaseState.broken: + case "broken": return "Broken"; - case LeaseState.expired: + case "expired": return "Expired"; - case LeaseState.leased: + case "leased": return "Leased"; - case LeaseState.unspecified: - return "Unspecified"; default: return this.stringField(state); diff --git a/src/app/models/blob-container.ts b/src/app/models/blob-container.ts index bbece5bb26..341270c73d 100644 --- a/src/app/models/blob-container.ts +++ b/src/app/models/blob-container.ts @@ -1,4 +1,5 @@ import { Model, NavigableRecord, Prop, Record } from "@batch-flask/core"; +import { StorageContainerProperties } from "app/services/storage"; import { Constants } from "common"; import { ContainerLease, ContainerLeaseAttributes } from "./container-lease"; @@ -28,6 +29,16 @@ export class BlobContainer extends Record implements Na @Prop() public lease: ContainerLease; @Prop() public storageAccountId: string; + constructor(container: StorageContainerProperties) { + super(container); + this.lease = new ContainerLease({ + state: container.leaseState, + status: container.leaseStatus, + duration: container.leaseDuration + }); + this.publicAccessLevel = container.publicAccess; + } + public get routerLink(): string[] { if (this.isFileGroup) { return ["/data/file-groups/containers", this.id]; diff --git a/src/app/models/container-lease.ts b/src/app/models/container-lease.ts index 98b09e5f6f..ca81b90b73 100644 --- a/src/app/models/container-lease.ts +++ b/src/app/models/container-lease.ts @@ -1,3 +1,4 @@ +import { LeaseStateType, LeaseStatusType } from "@azure/storage-blob"; import { Model, Prop, Record } from "@batch-flask/core"; export interface ContainerLeaseAttributes { @@ -16,17 +17,6 @@ export class ContainerLease extends Record { @Prop() public duration: string; } -export enum LeaseStatus { - locked = "locked", - unlocked = "unlocked", - unspecified = "unspecified", -} +export type LeaseStatus = LeaseStatusType; -export enum LeaseState { - available = "available", - breaking = "breaking", - broken = "broken", - expired = "expired", - leased = "leased", - unspecified = "unspecified", -} +export type LeaseState = LeaseStateType; diff --git a/src/app/services/core/data/storage-list-getter.ts b/src/app/services/core/data/storage-list-getter.ts index 3fdcc4ca4f..268797cd4a 100644 --- a/src/app/services/core/data/storage-list-getter.ts +++ b/src/app/services/core/data/storage-list-getter.ts @@ -1,28 +1,24 @@ import { Type } from "@angular/core"; import { ContinuationToken, ListGetter, ListGetterConfig, Record, ServerError } from "@batch-flask/core"; +import { StorageBlobResult } from "app/services/storage/models"; import { StorageClientService } from "app/services/storage/storage-client.service"; import { Observable, from, throwError } from "rxjs"; -import { catchError, flatMap, map, share } from "rxjs/operators"; +import { catchError, map, share, switchMap } from "rxjs/operators"; export interface StorageBaseParams { storageAccountId: string; } -export interface StorageListResponse { - data: any[]; - continuationToken?: string; -} - export interface StorageListConfig, TParams extends StorageBaseParams> extends ListGetterConfig { - getData: (client: any, params: TParams, options: any, token: any) => Promise; + getData: (client: any, params: TParams, options: any, token: any) => Promise>; } export class StorageListGetter, TParams extends StorageBaseParams> extends ListGetter { - private _getData: (client: any, params: TParams, options: any, token: any) => Promise; + private _getData: (client: any, params: TParams, options: any, token: any) => Promise>; constructor( type: Type, @@ -36,26 +32,18 @@ export class StorageListGetter, TParams extends Stor protected list(params: TParams, options: any): Observable { return this._clientProxy(params, options, null).pipe( - flatMap((client) => { - return from(client); - }), + switchMap(client => from(client)), map(response => this._processStorageResponse(response)), - catchError((error) => { - return throwError(ServerError.fromStorage(error)); - }), + catchError(error => throwError(ServerError.fromStorage(error))), share(), ); } protected listNext(token: ContinuationToken): Observable { return this._clientProxy(token.params, token.options, token.nextLink).pipe( - flatMap((client) => { - return from(client); - }), + switchMap(client => from(client)), map(response => this._processStorageResponse(response)), - catchError((error) => { - return throwError(ServerError.fromStorage(error)); - }), + catchError(error => throwError(ServerError.fromStorage(error))), share(), ); } diff --git a/src/app/services/storage/blob-storage-client-proxy.ts b/src/app/services/storage/blob-storage-client-proxy.ts index 48017d444b..3ae96a6013 100644 --- a/src/app/services/storage/blob-storage-client-proxy.ts +++ b/src/app/services/storage/blob-storage-client-proxy.ts @@ -1,20 +1,11 @@ -import { EncodingUtils } from "@batch-flask/utils"; -import { BlobService } from "azure-storage"; -import { BlobStorageResult, SharedAccessPolicy, StorageRequestOptions } from "./models"; +import { BlobUploadCommonResponse } from "@azure/storage-blob"; +import { ElectronRemote } from "@batch-flask/electron"; +import { IpcEvent } from "common/constants"; +import { SharedAccessPolicy } from "./models"; +import * as blob from "./models/storage-blob"; +import { BlobContentResult } from "./storage-blob.service"; -export interface ListBlobOptions { - /** - * Filter for the path.(Relative to the prefix if given) - */ - folder?: string; - - /** - * If it should list all files or 1 directory deep. - */ - recursive?: boolean; - - limit?: number; -} +const storageIpc = IpcEvent.storageBlob; export interface ListBlobResponse { body: { @@ -28,370 +19,239 @@ export interface ListBlobResponse { } export class BlobStorageClientProxy { - public client: BlobService; - constructor(blobService: BlobService) { - this.client = blobService; + private storageInfo: { url: string; account: string, key: string }; + + constructor( + private remote: ElectronRemote, + account: string, + key: string, + blobEndpoint: string + ) { + const url = `https://${account}.${blobEndpoint}`; + this.storageInfo = { url, account, key }; } /** - * Lists blobs from the container that match the prefix. In our case the prefix will be the - * taskId and the OutputKind of the task output. - * http://azure.github.io/azure-storage-node/BlobService.html#listBlobsSegmentedWithPrefix__anchor + * Lists blobs from the container that match the prefix. In our case the + * prefix will be the taskId and the OutputKind of the task output. * * @param {string} container - Name of the storage container * @param {string} blobPrefix - The prefix of the blob name. In our case it is the taskId prefix: * "${taskId}/$TaskOutput|$TaskLog/${namePrefixFilter}" - * @param {string} filter - Optional text for filtering further than the blob prefix * @param {string} continuationToken - Token that was returned from the last call, if any - * @param {StorageRequestOptions} options - Optional request parameters + * @param {blob.RequestOptions} options - Optional request parameters */ - public listBlobs( - container: string, - options: ListBlobOptions = {}, - continuationToken?: any): Promise { - - // we want to keep the filter and prefix separate for mapping files in the response. - const prefix = options.folder; - const storageOptions: StorageRequestOptions = { - delimiter: options.recursive ? null : "/", - maxResults: options.limit, - }; - return new Promise((resolve, reject) => { - this.client.listBlobsSegmentedWithPrefix(container, prefix, continuationToken, storageOptions, - (error, result, response: any) => { - if (error) { return reject(error); } - - const folders = this._getFolderNames(response).map((name) => { - return { - name: name, - url: `${container}/${name}`, - isDirectory: true, - }; - }); - - resolve({ - data: folders.concat(result.entries.map((blob) => { - return { - name: blob.name, - url: `${container}/${blob.name}`, - isDirectory: false, - properties: { - contentLength: parseInt(blob.contentLength, 10), - contentType: blob.contentSettings.contentType, - creationTime: null, - lastModified: blob.lastModified, - }, - }; - })), - continuationToken: result.continuationToken, - }); - }); - }); + public async listBlobs( + containerName: string, + options: blob.ListBlobOptions, + continuationToken?: string + ): Promise { + return this.remote.send(storageIpc.listBlobs, + { ...this.storageInfo, containerName, options, continuationToken }); } /** * Returns all user-defined metadata, standard HTTP properties, and system * properties for the blob. - * http://azure.github.io/azure-storage-node/BlobService.html#getBlobProperties__anchor * - * @param {string} container - Name of the storage container + * @param {string} containerName - Name of the storage container * @param {string} blobName - Name of the blob file: "myblob.txt" * @param {string} blobPrefix - Optional prefix to the blob from the container root: "1001/$TaskOutput/" - * @param {StorageRequestOptions} options - Optional request parameters + * @param {blob.RequestOptions} options - Optional request parameters */ - public getBlobProperties( - container: string, + public async getBlobProperties( + containerName: string, blobName: string, - blobPrefix?: string, - options?: StorageRequestOptions): Promise { - - const blobPath = `${blobPrefix || ""}${blobName}`; - return new Promise((resolve, reject) => { - this.client.getBlobProperties(container, blobPath, options, (error, result, response) => { - if (error) { - reject(error); - } else { - resolve({ - data: { - name: blobName, - url: `${container}/${blobPath}`, - isDirectory: false, - properties: { - contentLength: parseInt(result.contentLength, 10), - contentType: result.contentSettings.contentType, - creationTime: null, - lastModified: result.lastModified, - }, - }, - }); - } - }); - }); + blobPrefix = "", + options?: blob.RequestOptions + ): Promise { + + return this.remote.send(storageIpc.getBlobProperties, + { + ...this.storageInfo, + containerName, + blobName, + blobPrefix, + options + } + ); } /** * Downloads a blob into a text string. - * http://azure.github.io/azure-storage-node/BlobService.html#getBlobToText__anchor + * * @param {string} container - Name of the storage container * @param {string} blob - Fully prefixed blob path: "1001/$TaskOutput/myblob.txt" - * @param {StorageRequestOptions} options - Optional request parameters + * @param {blob.RequestOptions} options - Optional request parameters */ - public async getBlobContent(container: string, blob: string, options?: StorageRequestOptions) { - const buffer = await this._getBlobAsBuffer(container, blob, options); - const {encoding} = await EncodingUtils.detectEncodingFromBuffer({ buffer, bytesRead: buffer.length }); - let content; - if (encoding) { - content = new TextDecoder(encoding).decode(buffer); - } else { - content = buffer.toString(); - } - - return { content }; + public async getBlobContent( + containerName: string, + blobName: string, + options?: blob.GetBlobContentOptions + ): Promise { + return this.remote.send(storageIpc.getBlobContent, + { ...this.storageInfo, containerName, blobName, options }); } /** * Downloads a blob into a file. - * http://azure.github.io/azure-storage-node/BlobService.html#getBlobToLocalFile__anchor - * Note: this returns a SpeedSummary object that can list the percent complete. Can't see any - * implementation of this in the docs. Might be useful at some point. + * * @param {string} container - Name of the storage container * @param {string} blob - Fully prefixed blob path: "1001/$TaskOutput/myblob.txt" * @param {string} localFileName - The local path to the file to be downloaded. - * @param {StorageRequestOptions} options - Optional request parameters + * @param {blob.RequestOptions} options - Optional request parameters */ - public getBlobToLocalFile(container: string, blob: string, localFileName: string, options?: StorageRequestOptions) { - return new Promise((resolve, reject) => { - this.client.getBlobToLocalFile(container, blob, localFileName, options, (error, result, response) => { - if (error) { - reject(error); - } else { - // resolve with no content - resolve({}); - } - }); - }); + public async getBlobToLocalFile( + containerName: string, + blobName: string, + localFileName: string, + options?: blob.RequestOptions + ): Promise { + await this.remote.send(storageIpc.downloadBlob, + { + ...this.storageInfo, + containerName, + blobName, + localFileName, + options + } + ); + return; } /** - * Marks the specified blob or snapshot for deletion if it exists. The blob is later deleted during - * garbage collection. If a blob has snapshots, you must delete them when deleting the blob by setting - * the deleteSnapshots option. - * http://azure.github.io/azure-storage-node/BlobService.html#deleteBlobIfExists__anchor + * Marks the specified blob or snapshot for deletion if it exists. The blob + * is later deleted during garbage collection. If a blob has snapshots, you + * must delete them when deleting the blob by setting the deleteSnapshots + * option. * * @param {string} container - ID of the storage container * @param {string} blob - Fully prefixed blob path: "1001/$TaskOutput/myblob.txt" - * @param {StorageRequestOptions} options - Optional request parameters + * @param {blob.RequestOptions} options - Optional request parameters */ - public deleteBlobIfExists(container: string, blob: string, options?: StorageRequestOptions) - : Promise { - - return new Promise((resolve, reject) => { - this.client.deleteBlobIfExists(container, blob, options, (error, response) => { - if (error) { - reject(error); - } else { - resolve(response); - } - }); - }); + public async deleteBlobIfExists( + containerName: string, + blobName: string, + options?: blob.RequestOptions + ): Promise { + return this.remote.send(storageIpc.deleteBlob, + { ...this.storageInfo, containerName, blobName, options }); } /** - * Lists a segment containing a collection of container items whose names begin with the specified - * prefix under the specified account. By default the prefix will generally be "grp-" as this is the - * NCJ prefix for file group containers, but can aso be anything we like in order to get any - * arbritrary container. - * http://azure.github.io/azure-storage-node/BlobService.html#listContainersSegmentedWithPrefix__anchor + * Lists a segment containing a collection of container items whose names + * begin with the specified prefix under the specified account. By default + * the prefix will generally be "grp-" as this is the NCJ prefix for file + * group containers, but can aso be anything we like in order to get any + * arbitrary container. * * @param {string} prefix - Container name prefix including filter, or null. - * @param {string} filter - Filter in addition to name prefix. * @param {string} continuationToken - Token that was returned from the last call, if any - * @param {StorageRequestOptions} options - Optional request parameters + * @param {blob.RequestOptions} options - Optional request parameters */ - public listContainersWithPrefix( - startswith: string, - continuationToken?: any, - options?: StorageRequestOptions): Promise { - - return new Promise((resolve, reject) => { - this.client.listContainersSegmentedWithPrefix(startswith, continuationToken, options, - (error, result, response) => { - if (error) { - reject(error); - } else { - resolve({ - data: result.entries.map((container) => { - return { - ...container, - id: container.name, - }; - }), - continuationToken: result.continuationToken, - }); - } - }); - }); + public async listContainersWithPrefix( + prefix: string, + continuationToken?: string, + options?: blob.RequestOptions + ): Promise { + return this.remote.send(storageIpc.listContainers, + { ...this.storageInfo, continuationToken, prefix, options } + ); } /** - * Returns all user-defined metadata and system properties for the specified container. - * The data returned does not include the container's list of blobs. - * http://azure.github.io/azure-storage-node/BlobService.html#getContainerProperties__anchor + * Returns all user-defined metadata and system properties for the + * specified container. The data returned does not include the container's + * list of blobs. * * @param {string} container - Name of the storage container - * @param {string} prefix - Container name prefix to be remove from the display name. - * @param {StorageRequestOptions} options - Optional request parameters + * @param {blob.RequestOptions} options - Optional request parameters */ - public getContainerProperties(container: string, prefix: string, options?: StorageRequestOptions) - : Promise { - - return new Promise((resolve, reject) => { - this.client.getContainerProperties(container, options, (error, result, response) => { - if (error) { - reject(error); - } else { - resolve({ - data: { - ...result, - id: result.name, - }, - }); - } - }); - }); + public async getContainerProperties( + containerName: string, + options?: blob.RequestOptions + ): Promise { + return this.remote.send(storageIpc.getContainerProperties, + { ...this.storageInfo, containerName, options }); } /** - * Marks the specified container for deletion. The container and any blobs contained within - * it are later deleted during garbage collection. - * http://azure.github.io/azure-storage-node/BlobService.html#deleteContainer__anchor + * Marks the specified container for deletion. The container and any blobs + * contained within it are later deleted during garbage collection. * * @param {string} container - Name of the storage container - * @param {StorageRequestOptions} options - Optional request parameters + * @param {blob.RequestOptions} options - Optional request parameters */ - public deleteContainer(container: string, options?: StorageRequestOptions) - : Promise { - - return new Promise((resolve, reject) => { - this.client.deleteContainer(container, options, (error, response) => { - if (error) { - reject(error); - } else { - resolve(); - } - }); - }); + public async deleteContainer( + containerName: string, + options?: blob.RequestOptions + ): Promise { + return this.remote.send(storageIpc.deleteContainer, + { ...this.storageInfo, containerName, options }); } /** * Creates a new container under the specified account. * If a container with the same name already exists, the operation fails. - * http://azure.github.io/azure-storage-node/BlobService.html#createContainer__anchor * * @param {string} container - Name of the storage container */ - public createContainer(containerName: string) - : Promise { - - return new Promise((resolve, reject) => { - this.client.createContainer(containerName, (error, response) => { - if (error) { - reject(error); - } else { - resolve(); - } - }); - }); + public async createContainer(containerName: string): Promise { + return this.remote.send(storageIpc.createContainer, + { ...this.storageInfo, containerName }); } /** - * Creates a new container under the specified account if it doesn't exsits. + * Creates a new container under the specified account if it doesn't exists. * * @param {string} container - Name of the storage container + * @returns {boolean} whether a new container was created */ - public createContainerIfNotExists(containerName: string) - : Promise { - - return new Promise((resolve, reject) => { - this.client.createContainerIfNotExists(containerName, (error, response) => { - if (error) { - reject(error); - } else { - resolve(); - } - }); - }); + public async createContainerIfNotExists( + containerName: string, + options?: blob.RequestOptions + ): Promise { + return this.remote.send(storageIpc.createContainer, + { + ...this.storageInfo, + containerName, + options, + unlessExists: true + } + ); } - /** - * Retrieves a shared access signature token. - * http://azure.github.io/azure-storage-node/BlobService.html#generateSharedAccessSignature__anchor - * - * @param {string} container - Name of the storage container - * @param {string} sharedAccessPolicy - The shared access policy - */ - public generateSharedAccessSignature( - container: string, blob: string, sharedAccessPolicy: SharedAccessPolicy): string { - return this.client.generateSharedAccessSignature(container, blob, sharedAccessPolicy, null); + public async generateSasUrl( + containerName: string, + blobName?: string, + accessPolicy?: SharedAccessPolicy + ): Promise { + return this.remote.send(storageIpc.generateSasUrl, + { ...this.storageInfo, containerName, blobName, accessPolicy }); } /** * Retrieves a blob or container URL. - * http://azure.github.io/azure-storage-node/BlobService.html#getUrl__anchor * * @param {string} container - Name of the storage container * @param {string} blob - Optional blob name. * @param {string} sasToken - The Shared Access Signature token. */ public getUrl(container: string, blob?: string, sasToken?: string): string { - return this.client.getUrl(container, blob, sasToken); - } - - public async uploadFile(container: string, file: string, remotePath: string): Promise { - return new Promise((resolve, reject) => { - this.client.createBlockBlobFromLocalFile(container, remotePath, file, - (error: any, result: BlobService.BlobResult) => { - if (error) { return reject(error); } - resolve(result); - }); - }); + return [ + this.storageInfo.url, + container, + blob ? `/${blob}${sasToken}` : sasToken + ].join("/"); } - /** - * Return the list of folder names return by listing blobs with a delimiter - * @param response - */ - private _getFolderNames(response: ListBlobResponse) { - const results = response.body["EnumerationResults"]["Blobs"]; - const blobPrefix = results["BlobPrefix"]; - if (!blobPrefix) { - return []; - } - const data = Array.isArray(blobPrefix) ? blobPrefix : [blobPrefix]; - return data.map(x => x["Name"].slice(0, -1)); - } - - private _getBlobAsBuffer(container: string, blob: string, options: StorageRequestOptions): Promise { - return new Promise((resolve, reject) => { - const chunks = []; - const stream = this.client.createReadStream(container, blob, options, (error, text, response) => { - if (error) { - reject(error); - } - }); - stream.on("data", (chunk) => { - chunks.push(chunk); - }); - - stream.on("end", () => { - const buffer = Buffer.concat(chunks); - resolve(buffer); - }); - - stream.on("error", (error) => { - reject(error); - }); - }); + public async uploadFile( + containerName: string, + fileName: string, + blobName: string + ): Promise { + return this.remote.send(storageIpc.uploadFile, + { ...this.storageInfo, containerName, fileName, blobName }); } } diff --git a/src/app/services/storage/models/blob-storage-result.ts b/src/app/services/storage/models/blob-storage-result.ts deleted file mode 100644 index 1676c76e53..0000000000 --- a/src/app/services/storage/models/blob-storage-result.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface BlobStorageResult { - continuationToken?: any; - data: any; -} diff --git a/src/app/services/storage/models/index.ts b/src/app/services/storage/models/index.ts index b55e6458d9..000b6f45b3 100644 --- a/src/app/services/storage/models/index.ts +++ b/src/app/services/storage/models/index.ts @@ -1,3 +1,2 @@ -export * from "./blob-storage-result"; +export * from "./storage-blob"; export * from "./shared-access-policy"; -export * from "./storage-request-options"; diff --git a/src/app/services/storage/models/shared-access-policy.ts b/src/app/services/storage/models/shared-access-policy.ts index f2e06b796e..09d1d8dfac 100644 --- a/src/app/services/storage/models/shared-access-policy.ts +++ b/src/app/services/storage/models/shared-access-policy.ts @@ -15,11 +15,14 @@ export interface SharedAccessPolicy { AccessPolicy: AccessPolicy; } +export type PermissionType = "r" | "a" | "c" | "w" | "d" | "x" | "t" | "m" | + "e" | "i" | "y" | "l"; + export interface AccessPolicy { /** * The permission type */ - Permissions: string; + Permissions: PermissionType[]; /** * The time at which the Shared Access Signature becomes valid (The UTC value will be used). diff --git a/src/app/services/storage/models/storage-blob.ts b/src/app/services/storage/models/storage-blob.ts new file mode 100644 index 0000000000..7cf2cf58f7 --- /dev/null +++ b/src/app/services/storage/models/storage-blob.ts @@ -0,0 +1,137 @@ +import { ContainerItem, BlobUploadCommonResponse, ContainerGetPropertiesResponse, CommonOptions } from "@azure/storage-blob"; +import { Model, Prop, Record } from "@batch-flask/core"; +import { SharedAccessPolicy } from "./shared-access-policy"; + +// Placeholder; we don't use any options to storage-blob API requests +export type RequestOptions = Partial; + +export interface BaseParams { + url: string; + account: string; + key: string; +} + +export interface ContainerParams extends BaseParams { + containerName: string; +} + +export interface BlobParams extends ContainerParams { + blobName: string; +} + +export interface ListContainersParams extends BaseParams { + prefix?: string; + continuationToken?: string; + options?: RequestOptions & { + maxPages?: number; + maxPageSize?: number; + }; +} + +export interface GetBlobPropertiesParams extends BlobParams { + blobPrefix?: string, + options?: RequestOptions +} + +export interface GetContainerPropertiesParams extends ContainerParams { + options?: RequestOptions; +} + +export interface ListBlobsParams extends ContainerParams { + continuationToken?: string; + options?: ListBlobOptions; +} + +export interface GetBlobContentParams extends BlobParams { + options?: GetBlobContentOptions; +} + +export interface GetBlobContentOptions extends RequestOptions { + rangeStart?: number; + rangeEnd?: number; +} + +export interface DeleteContainerParams extends ContainerParams { + options?: RequestOptions; +} + +export interface CreateContainerParams extends ContainerParams { + unlessExists?: boolean; + options?: RequestOptions; +} + +export interface GenerateSasUrlParams extends ContainerParams { + blobName?: string; + accessPolicy?: SharedAccessPolicy; +} + +export interface DownloadBlobParams extends BlobParams { + localFileName: string; + options?: RequestOptions; +} + +export interface UploadFileParams extends BlobParams { + fileName: string; +} + +export interface DeleteBlobParams extends BlobParams { + options?: RequestOptions; +} + +export interface StorageBlobResult { + data: T; + continuationToken?: string; +} + +export type ListContainersResult = StorageBlobResult; +export type GetBlobPropertiesResult = StorageBlobResult; +export type ListBlobsResult = StorageBlobResult; +export type GetContainerPropertiesResult = + StorageBlobResult; +export type UploadFileResult = BlobUploadCommonResponse; + +export interface GetBlobContentResult { + content: string; +} + +export interface BlobProperties { + name: string; + url: string; + isDirectory: boolean; + properties: { + contentLength: number; + contentType: string; + creationTime: Date | string; + lastModified: Date | string; + }; +} + +@Model() +export class BlobItem extends Record { + @Prop() name: string; + @Prop() url: string; + @Prop() isDirectory: boolean; + @Prop() properties: { + contentLength: number; + contentType: string; + creationTime: Date | string; + lastModified: Date | string; + } +} + +export type ContainerProperties = ContainerGetPropertiesResponse; + +export interface ListBlobOptions { + /** + * Filter for the path.(Relative to the prefix if given) + */ + folder?: string; + + /** + * If it should list all files or 1 directory deep. + */ + recursive?: boolean; + + maxPageSize?: number; + maxPages?: number; +} diff --git a/src/app/services/storage/models/storage-request-options.ts b/src/app/services/storage/models/storage-request-options.ts deleted file mode 100644 index 03245aa93e..0000000000 --- a/src/app/services/storage/models/storage-request-options.ts +++ /dev/null @@ -1,2 +0,0 @@ -export interface StorageRequestOptions { -} diff --git a/src/app/services/storage/storage-blob.service.ts b/src/app/services/storage/storage-blob.service.ts index 628d6a33a4..336eb1cd69 100644 --- a/src/app/services/storage/storage-blob.service.ts +++ b/src/app/services/storage/storage-blob.service.ts @@ -1,4 +1,5 @@ import { Injectable, NgZone } from "@angular/core"; +import { ContainerClient, ContainerProperties } from "@azure/storage-blob"; import { DataCache, EntityView, @@ -10,15 +11,14 @@ import { enterZone, } from "@batch-flask/core"; import { FileSystemService } from "@batch-flask/electron"; -import { File, FileLoadOptions, FileLoader, FileNavigator } from "@batch-flask/ui"; +import { File, FileLoadOptions, FileLoader, FileNavigator, FileLoadResult } from "@batch-flask/ui"; import { CloudPathUtils, log } from "@batch-flask/utils"; import { StorageEntityGetter, StorageListGetter } from "app/services/core"; -import { SharedAccessPolicy } from "app/services/storage/models"; -import { BlobService, createBlobServiceWithSas } from "azure-storage"; +import { ListBlobOptions, SharedAccessPolicy, StorageBlobResult, UploadFileResult } from "app/services/storage/models"; import { Constants } from "common"; import { AsyncSubject, Observable, from, of, throwError } from "rxjs"; -import { catchError, concat, concatMap, flatMap, map, share, take } from "rxjs/operators"; -import { BlobStorageClientProxy, ListBlobOptions } from "./blob-storage-client-proxy"; +import { catchError, concat, concatMap, map, share, switchMap, take } from "rxjs/operators"; +import { BlobStorageClientProxy } from "./blob-storage-client-proxy"; import { StorageClientService } from "./storage-client.service"; export interface ListBlobParams { @@ -27,15 +27,23 @@ export interface ListBlobParams { } export interface BlobFileParams extends ListBlobParams { - storageAccountId: string; blobPrefix?: string; blobName?: string; } +export interface CreateBlobParams { + accountUrl: string; + container: string; + blob: string; + sasToken: string; +} + export interface BlobContentResult { content: string; } +export type StorageContainerProperties = ContainerProperties; + export interface NavigateBlobsOptions { /** * Optional callback that gets called when an error is returned listing files. @@ -78,10 +86,18 @@ export class InvalidSasUrlError extends Error { // Regex to extract the host, container and blob from a sasUrl const storageBlobUrlRegex = /^(https:\/\/[\w\._\-]+)\/([\w\-_]+)\/([\w\-_.]+)\?(.*)$/i; +function createBlobClient(params: CreateBlobParams) { + const containerClient = new ContainerClient( + params.accountUrl + params.sasToken, + params.container + ); + return containerClient.getBlockBlobClient(params.blob); +} + @Injectable({ providedIn: "root" }) export class StorageBlobService { - public maxBlobPageSize: number = 100; // 500 slows down the UI too much. - public maxContainerPageSize: number = 50; + public maxBlobPageSize: number = 200; // 500 slows down the UI too much. + public maxContainerPageSize: number = 100; private _blobListCache = new TargetedDataCache({ key: ({ storageAccountId, container }) => `${storageAccountId}/${container}`, @@ -104,12 +120,21 @@ export class StorageBlobService { this._blobListGetter = new StorageListGetter(File, this.storageClient, { cache: (params) => this.getBlobFileCache(params), - getData: (client, params, options, continuationToken) => { + getData: (client: BlobStorageClientProxy, + params, options, continuationToken) => { + const blobOptions: ListBlobOptions = { + folder: options.original.folder, + recursive: options.original.recursive, + maxPages: options.original.limit, + maxPageSize: this.maxBlobPageSize + }; + + // N.B. `BlobItem` and `File` are nearly identical return client.listBlobs( params.container, - options.original, + blobOptions, continuationToken, - ); + ) as Promise>; }, logIgnoreError: storageIgnoredErrors, }); @@ -199,12 +224,12 @@ export class StorageBlobService { properties: () => { return this.get(storageAccountId, container, blobName, blobPrefix); }, - content: (options: FileLoadOptions) => { - return this._callStorageClient(storageAccountId, (client) => { + content: (options: FileLoadOptions): Observable => + this._callStorageClient(storageAccountId, (client) => { const pathToBlob = `${blobPrefix || ""}${blobName}`; - return client.getBlobContent(container, pathToBlob, options); - }); - }, + return client.getBlobContent(container, pathToBlob, + options); + }), download: (dest: string) => { return this._callStorageClient(storageAccountId, (client) => { const pathToBlob = `${blobPrefix || ""}${blobName}`; @@ -263,8 +288,7 @@ export class StorageBlobService { sharedAccessPolicy: SharedAccessPolicy): Observable { return this._callStorageClient(storageAccountId, (client) => { - const sasToken = client.generateSharedAccessSignature(container, blob, sharedAccessPolicy); - return Promise.resolve(client.getUrl(container, blob, sasToken)); + return client.generateSasUrl(container, blob, sharedAccessPolicy); }, (error) => { // TODO-Andrew: test that errors are caught log.error(`Error generating container SAS: ${container}`, { ...error }); @@ -272,22 +296,17 @@ export class StorageBlobService { } public uploadToSasUrl(sasUrl: string, filePath: string): Observable { - const subject = new AsyncSubject(); - - const { accountUrl, sasToken, container, blob } = this._parseSasUrl(sasUrl); - const service = createBlobServiceWithSas(accountUrl, sasToken); - service.createBlockBlobFromLocalFile(container, blob, filePath, - (error: any, result: BlobService.BlobResult) => { - this.zone.run(() => { - - if (error) { - subject.error(ServerError.fromStorage(error)); - subject.complete(); - } + const subject = new AsyncSubject(); + + const blobParams = this._parseSasUrl(sasUrl); + const blobClient = createBlobClient(blobParams); + this.zone.run(() => { + blobClient.uploadFile(filePath) + .then(result => { subject.next(result); - subject.complete(); - }); - }); + }).catch(error => subject.error(ServerError.fromStorage(error)) + ).finally(() => subject.complete()); + }); return subject.asObservable(); } @@ -296,16 +315,17 @@ export class StorageBlobService { * Upload a single file to storage. * @param container Container Id * @param file Absolute path to the local file - * @param remotePath Blob name + * @param blobName Blob name */ public uploadFile( storageAccountId: string, container: string, file: string, - remotePath: string): Observable { + blobName: string + ): Observable { return this._callStorageClient(storageAccountId, - (client) => client.uploadFile(container, file, remotePath), (error) => { + (client) => client.uploadFile(container, file, blobName), (error) => { log.error(`Error upload file ${file} to container ${container}`, error); }); } @@ -338,7 +358,7 @@ export class StorageBlobService { }; const blob = remotePath ? CloudPathUtils.join(remotePath, file.remotePath) : file.remotePath; const uploadObs = this.uploadFile(storageAccountId, container, file.localPath, blob).pipe( - map(x => ({ + map(() => ({ uploaded: index + 1, total, current: file, @@ -384,9 +404,7 @@ export class StorageBlobService { return this.storageClient.getFor(storageAccountId).pipe( take(1), - flatMap((client) => { - return from>(promise(client)); - }), + switchMap(client => from>(promise(client))), catchError((err) => { const serverError = ServerError.fromStorage(err); if (errorCallback) { @@ -400,7 +418,7 @@ export class StorageBlobService { ); } - private _parseSasUrl(sasUrl: string) { + private _parseSasUrl(sasUrl: string): CreateBlobParams { const match = storageBlobUrlRegex.exec(sasUrl); if (match.length < 5) { diff --git a/src/app/services/storage/storage-client-proxy-factory.ts b/src/app/services/storage/storage-client-proxy-factory.ts index 8fd365c86c..8a45621a91 100644 --- a/src/app/services/storage/storage-client-proxy-factory.ts +++ b/src/app/services/storage/storage-client-proxy-factory.ts @@ -1,4 +1,4 @@ -import * as storage from "azure-storage"; +import { ElectronRemote } from "@batch-flask/electron"; import { BlobStorageClientProxy } from "./blob-storage-client-proxy"; export interface StorageAccountSharedKeyOptions { @@ -16,19 +16,17 @@ export class StorageClientProxyFactory { private _sharedKeyOptions: StorageAccountSharedKeyOptions = {} as any; private _blobSharedKeyClient: BlobStorageClientProxy = null; - public getBlobServiceForSharedKey(options: StorageAccountSharedKeyOptions) { + constructor(private remote: ElectronRemote) { } + + public async getBlobServiceForSharedKey(options: StorageAccountSharedKeyOptions) { if (!this._compareSharedKeyOptions(options)) { this._sharedKeyOptions = options; - const connectionString = [ - `DefaultEndpointsProtocol=https;`, - `AccountName=${options.account};`, - `AccountKey=${options.key};`, - `EndpointSuffix=${options.endpoint};`, - ].join(""); - const blobService = storage.createBlobService(connectionString) - .withFilter(new storage.ExponentialRetryPolicyFilter()); - this._blobSharedKeyClient = new BlobStorageClientProxy(blobService); + this._blobSharedKeyClient = new BlobStorageClientProxy( + this.remote, + options.account, options.key, + `blob.${options.endpoint}` + ); } return this._blobSharedKeyClient; diff --git a/src/app/services/storage/storage-client.service.ts b/src/app/services/storage/storage-client.service.ts index 79866fc4ba..f879dd6c3c 100644 --- a/src/app/services/storage/storage-client.service.ts +++ b/src/app/services/storage/storage-client.service.ts @@ -1,10 +1,11 @@ import { Injectable } from "@angular/core"; import { ServerError } from "@batch-flask/core"; +import { ElectronRemote } from "@batch-flask/electron"; import { StorageKeys } from "app/models"; import { BatchExplorerService } from "app/services/batch-explorer.service"; import { ArmResourceUtils } from "app/utils"; import { Observable, throwError } from "rxjs"; -import { first, flatMap, map, share } from "rxjs/operators"; +import { first, flatMap, map, share, switchMap } from "rxjs/operators"; import { BatchAccountService } from "../batch-account"; import { BlobStorageClientProxy } from "./blob-storage-client-proxy"; import { StorageAccountKeysService } from "./storage-account-keys.service"; @@ -22,7 +23,7 @@ export interface StorageKeyCachedItem { keys: StorageKeys; } -@Injectable({providedIn: "root"}) +@Injectable({ providedIn: "root" }) export class StorageClientService { public hasAutoStorage: Observable; public hasArmAutoStorage: Observable; @@ -33,9 +34,11 @@ export class StorageClientService { constructor( private batchExplorer: BatchExplorerService, private accountService: BatchAccountService, - private storageKeysService: StorageAccountKeysService) { + private storageKeysService: StorageAccountKeysService, + remote: ElectronRemote + ) { - this._storageClientFactory = new StorageClientProxyFactory(); + this._storageClientFactory = new StorageClientProxyFactory(remote); this.hasAutoStorage = this.accountService.currentAccount.pipe(map((account) => { return Boolean(account.autoStorage); @@ -65,13 +68,17 @@ export class StorageClientService { } public getFor(storageAccountId: string): Observable { - return this.storageKeysService.getFor(storageAccountId).pipe(map((keys) => { - return this._storageClientFactory.getBlobServiceForSharedKey({ - account: ArmResourceUtils.getAccountNameFromResourceId(storageAccountId), - key: keys.primaryKey, - endpoint: this.batchExplorer.azureEnvironment.storageEndpoint, - }); - })); + return this.storageKeysService.getFor(storageAccountId).pipe( + switchMap((keys) => + this._storageClientFactory.getBlobServiceForSharedKey({ + account: ArmResourceUtils + .getAccountNameFromResourceId(storageAccountId), + key: keys.primaryKey, + endpoint: + this.batchExplorer.azureEnvironment.storageEndpoint, + }) + ) + ); } public clearCurrentStorageKeys() { diff --git a/src/app/services/storage/storage-container.service.ts b/src/app/services/storage/storage-container.service.ts index 49ffdd14de..a08887577c 100644 --- a/src/app/services/storage/storage-container.service.ts +++ b/src/app/services/storage/storage-container.service.ts @@ -11,7 +11,6 @@ import { log } from "@batch-flask/utils"; import { BlobContainer } from "app/models"; import { StorageEntityGetter, StorageListGetter } from "app/services/core"; import { SharedAccessPolicy } from "app/services/storage/models"; -import { Constants } from "common"; import { Observable, Subject, from, throwError } from "rxjs"; import { catchError, flatMap, share } from "rxjs/operators"; import { BlobStorageClientProxy } from "./blob-storage-client-proxy"; @@ -32,7 +31,9 @@ const storageIgnoredErrors = [ HttpCode.Conflict, ]; -@Injectable({providedIn: "root"}) +const storageAccountPollRate = 30_000; + +@Injectable({ providedIn: "root" }) export class StorageContainerService { /** * Triggered only when a file group is added through this app. @@ -112,7 +113,7 @@ export class StorageContainerService { return new EntityView({ cache: params => this._containerCache.getCache(params), getter: this._containerGetter, - poll: Constants.PollRate.entity, + poll: storageAccountPollRate, }); } @@ -121,8 +122,7 @@ export class StorageContainerService { container: string, sharedAccessPolicy: SharedAccessPolicy): Observable { return this._callStorageClient(storageAccountId, (client) => { - const sasToken = client.generateSharedAccessSignature(container, null, sharedAccessPolicy); - return Promise.resolve(client.getUrl(container, null, sasToken)); + return client.generateSasUrl(container, null, sharedAccessPolicy); }, (error) => { log.error(`Error generating container SAS: ${container}`, { ...error }); }); diff --git a/src/client/client.module.ts b/src/client/client.module.ts index a233923a80..5575bb31f8 100644 --- a/src/client/client.module.ts +++ b/src/client/client.module.ts @@ -19,6 +19,7 @@ import { BatchExplorerApplication } from "./core/batch-explorer-application"; import { BatchExplorerInitializer } from "./core/batch-explorer-initializer"; import { BatchExplorerProcess } from "./core/batch-explorer-process"; import { BlIpcMain } from "./core/bl-ipc-main"; +import { StorageBlobAdapter } from "./core/storage"; import { LocalDataStore } from "./core/local-data-store"; import { BatchExplorerProperties } from "./core/properties"; import { ClientTelemetryModule } from "./core/telemetry"; @@ -68,6 +69,7 @@ export function initializeServices(injector) { BatchExplorerProperties, AADService, BlIpcMain, + StorageBlobAdapter, AzureEnvironmentService, ...servicesToInitialize, ], diff --git a/src/client/core/batch-explorer-application.ts b/src/client/core/batch-explorer-application.ts index 8bfc37088e..c855c7dd9d 100644 --- a/src/client/core/batch-explorer-application.ts +++ b/src/client/core/batch-explorer-application.ts @@ -24,6 +24,7 @@ import { RecoverWindow } from "../recover-window"; import { AADService, AuthenticationState, AuthenticationWindow, LogoutError } from "./aad"; import { BatchExplorerInitializer } from "./batch-explorer-initializer"; import { MainWindowManager } from "./main-window-manager"; +import { StorageBlobAdapter } from "./storage"; const osName = `${os.platform()}-${os.arch()}/${os.release()}`; const isDev = ClientConstants.isDev ? "-dev" : ""; @@ -56,7 +57,8 @@ export class BatchExplorerApplication { public properties: BatchExplorerProperties, private telemetryService: TelemetryService, private telemetryManager: TelemetryManager, - private ipcMain: BlIpcMain) { + private ipcMain: BlIpcMain, + private storageBlobAdapter: StorageBlobAdapter) { this.windows = new MainWindowManager(this, this.telemetryManager); this.state = this._state.asObservable(); @@ -81,6 +83,7 @@ export class BatchExplorerApplication { this._setupProcessEvents(); this._registerFileProtocol(); await this.proxySettings.init(); + this.storageBlobAdapter.init(); } /** @@ -331,7 +334,7 @@ export class BatchExplorerApplication { } private _registerFileProtocol() { - protocol.registerFileProtocol("file", (request, callback) => { + protocol.registerFileProtocol("file", (request, callback) => { const pathName = decodeURI(request.url.replace("file:///", "")); callback(pathName); }); diff --git a/src/client/core/index.ts b/src/client/core/index.ts index 7c4f05a793..290cd38c26 100644 --- a/src/client/core/index.ts +++ b/src/client/core/index.ts @@ -4,3 +4,4 @@ export * from "./subprocess"; export * from "./batch-explorer-application"; export * from "./smart-card-certificate"; export * from "./client-locale.service"; +export * from "./storage"; diff --git a/src/client/core/storage/index.ts b/src/client/core/storage/index.ts new file mode 100644 index 0000000000..213398e761 --- /dev/null +++ b/src/client/core/storage/index.ts @@ -0,0 +1 @@ +export * from "./storage-blob-adapter"; diff --git a/src/client/core/storage/storage-blob-adapter.spec.ts b/src/client/core/storage/storage-blob-adapter.spec.ts new file mode 100644 index 0000000000..1a922b5730 --- /dev/null +++ b/src/client/core/storage/storage-blob-adapter.spec.ts @@ -0,0 +1,224 @@ +import { StorageBlobAdapter } from "./storage-blob-adapter"; + +describe("StorageBlobAdapter", () => { + let adapter: StorageBlobAdapter; + beforeEach(() => adapter = new StorageBlobAdapter(null)); + + describe("listContainer", () => { + let serviceClientSpy; + it("should return paged results", async () => { + const baseParams = { url: "url", account: "account", key: "key" }; + serviceClientSpy = spyOn(adapter, "getServiceClient") + .and.returnValue({ + listContainers: () => ({ + byPage: containerCountGenerator(20) + }) + }); + let response = await adapter.listContainers(baseParams); + expect(response.data.length).toEqual(20); + + serviceClientSpy.calls.reset(); + + response = await adapter.listContainers({ + ...baseParams, + options: { maxPageSize: 6, maxPages: 3 } + }); + expect(response.data.length).toEqual(18); + + response = await adapter.listContainers({ + ...baseParams, + continuationToken: response.continuationToken, + options: { maxPageSize: 6, maxPages: 3 } + }); + expect(response.data.length).toEqual(2); + }); + }); + + describe("listBlobs", () => { + const baseParams = { + url: "url", + account: "account", + key: "key", + containerName: "container", + }; + let containerClientSpy; + function mockListBlobs(params: { count?: number; blobs?: string[] }) { + const { count, blobs } = params; + containerClientSpy = + spyOn(adapter, "getContainerClient").and.returnValue({ + listBlobsByHierarchy: () => ({ + byPage: count ? + blobCountGenerator(count) : + blobItemGenerator(blobs) + }), + listBlobsFlat: () => ({ + byPage: count ? + blobCountGenerator(count) : + blobItemGenerator(blobs, true) + }) + }); + } + + it("should return paged results", async () => { + const params = { + ...baseParams, + options: { + maxPages: 2, + maxPageSize: 50 + } + }; + mockListBlobs({ count: 180 }); + let response = await adapter.listBlobs(params); + expect(response.data.length).toEqual(100); + response = await adapter.listBlobs({ + ...params, continuationToken: response.continuationToken + }); + expect(response.data.length).toEqual(80); + expect(response.continuationToken).not.toBeDefined(); + }); + it("should handle the recursive option", async () => { + const fakeBlobs = [ + "file1.txt", + "file2.txt", + "file3.txt", + "prefixA/file5.txt", + "prefixA/file6.txt" + ]; + mockListBlobs({ blobs: fakeBlobs }); + let response = await adapter.listBlobs({ + ...baseParams, options: { recursive: false } + }); + expect(response.data.length).toEqual(4); + expect(response.data[0]).toEqual(jasmine.objectContaining({ + name: "prefixA", + url: "container/prefixA", + isDirectory: true, + })); + ["file1.txt", "file2.txt", "file3.txt"].forEach((name, i) => { + expect(response.data[i + 1]).toEqual(jasmine.objectContaining({ + name, + isDirectory: false + })); + }); + + containerClientSpy.calls.reset(); + + response = await adapter.listBlobs({ + ...baseParams, options: { recursive: true } + }); + expect(response.data.length).toEqual(5); + expect(response.data).toEqual( + fakeBlobs.map(name => jasmine.objectContaining({ + name, isDirectory: false + })) + ); + }); + }); +}); + +const indexes = { + file: 1, + folder: 1, + container: 1 +}; + +function nitems(num, type, transformer) { + return [...Array(num).keys()].map(() => { + // eslint-disable-next-line security/detect-object-injection + const index = indexes[type]++; + return transformer(`${type}${index}`); + }); +} + +function blobItem(name) { + return { + name, + properties: { + contentLength: 20, + contentType: "type", + createdOn: null, + lastModified: null + } + } +} + +function containerItem(name) { + return { + name, + properties: { + foo: "bar" + }, + version: "1.0", + metadata: { + key1: "value1" + } + }; +} + +function blobPrefix(name) { + return { name }; +} + +function containerCountGenerator(count: number) { + return countGenerator(count, num => ({ + containerItems: nitems(num, "container", containerItem) + })); +} + +function blobCountGenerator(count: number) { + return countGenerator(count, (num) => ({ + segment: { + blobItems: nitems(num, "file", blobItem), + blobPrefixes: [] + } + })); +} + +function countGenerator(count: number, pageContents: (num: number) => T) { + return async function* (options) { + let token = options.continuationToken; + if (!token) { + token = "token:0"; + } + let index = + parseInt(token.split(":")[1], 10); + if (count) { + while (index < count) { + const size = + Math.min(options.maxPageSize, count - index); + index += size; + token = (index >= count) ? undefined : + `token:${index}`; + yield { + ...pageContents(size), + continuationToken: token + } + } + } + } +} + +function blobItemGenerator(items: string[], flat = false) { + const blobs = [], prefixes = {}; + items.forEach(item => { + const parts = item.split("/"); + // listBlobsHierarchy only lists top-level prefixes + if (parts.length > 1) { + if (flat) { + blobs.push(blobItem(item)); + } else { + prefixes[parts[0]] = blobPrefix(parts[0]); + } + } else { + blobs.push(blobItem(item)); + } + }); + return async function* () { + yield { + segment: { + blobItems: blobs, + blobPrefixes: Object.values(prefixes) + } + } + } +} diff --git a/src/client/core/storage/storage-blob-adapter.ts b/src/client/core/storage/storage-blob-adapter.ts new file mode 100644 index 0000000000..b6eb67238e --- /dev/null +++ b/src/client/core/storage/storage-blob-adapter.ts @@ -0,0 +1,269 @@ +import { Injectable } from "@angular/core"; +import { BlobHierarchyListSegment, BlobSASPermissions, BlobServiceClient, ContainerSASPermissions, StorageSharedKeyCredential } from "@azure/storage-blob"; +import { autobind } from "@batch-flask/core"; +import { EncodingUtils } from "@batch-flask/utils"; +import * as blob from "app/services/storage/models/storage-blob"; +import { IpcEvent } from "common/constants"; +import { BlIpcMain } from "../bl-ipc-main"; + + +const storageIpc = IpcEvent.storageBlob; + +const MAX_CONTAINER_LIST_PAGE_SIZE = 100; +const MAX_BLOB_LIST_PAGE_SIZE = 200; + +@Injectable({ providedIn: "root" }) +export class StorageBlobAdapter { + constructor(private ipcMain: BlIpcMain) { + } + + public init() { + // TODO: Replace with method decorators? + this.ipcMain.on(storageIpc.listContainers, this.listContainers); + this.ipcMain.on(storageIpc.getContainerProperties, + this.getContainerProperties); + this.ipcMain.on(storageIpc.deleteContainer, this.deleteContainer); + this.ipcMain.on(storageIpc.createContainer, this.createContainer); + + this.ipcMain.on(storageIpc.listBlobs, this.listBlobs); + this.ipcMain.on(storageIpc.getBlobProperties, this.getBlobProperties); + this.ipcMain.on(storageIpc.getBlobContent, this.getBlobContent); + this.ipcMain.on(storageIpc.generateSasUrl, this.generateSasUrl); + this.ipcMain.on(storageIpc.downloadBlob, this.downloadBlob); + this.ipcMain.on(storageIpc.uploadFile, this.uploadFile); + this.ipcMain.on(storageIpc.deleteBlob, this.deleteBlob); + } + + @autobind() + public async listContainers(params: blob.ListContainersParams): + Promise { + const { prefix, continuationToken, options = {} } = params; + const client = this.getServiceClient(params); + const containers = []; + + const { maxPageSize = MAX_CONTAINER_LIST_PAGE_SIZE, maxPages } = + options; + + const pages = client.listContainers({ prefix }).byPage({ + maxPageSize: maxPageSize, + // byPage() treats null token as valid, returning 0 pages + continuationToken: continuationToken ?? undefined + }); + + let pageIndex = 0; + let nextToken; + for await (const page of pages) { + pageIndex++; + nextToken = page.continuationToken; + for (const container of page.containerItems) { + containers.push({ + ...container.properties, + metadata: container.metadata, + version: container.version, + deleted: container.deleted, + id: container.name + }); + } + nextToken = page.continuationToken; + if (maxPages && pageIndex >= maxPages) { + break; + } + } + return { + data: containers, + continuationToken: nextToken + }; + } + + @autobind() + public async getBlobProperties(params: blob.GetBlobPropertiesParams): + Promise { + const { containerName, blobName, blobPrefix = "", options } = params; + + const client = this.getBlobClient(params); + const blobPath = blobPrefix + blobName; + const props = await client.getProperties(options); + + return { + data: { + name: blobName, + url: `${containerName}/${blobPath}`, + isDirectory: false, + properties: { + contentLength: props.contentLength, + contentType: props.contentType, + creationTime: props.createdOn, + lastModified: props.lastModified, + }, + }, + }; + } + + @autobind() + public async getContainerProperties( + params: blob.GetContainerPropertiesParams + ): Promise { + + const client = this.getContainerClient(params); + const props = await client.getProperties(params.options); + + return { data: props }; + } + + @autobind() + public async listBlobs(params: blob.ListBlobsParams): + Promise { + const { containerName, options = {}, continuationToken } = params; + const { + folder: prefix, + maxPageSize = MAX_BLOB_LIST_PAGE_SIZE, + maxPages, + recursive + } = options; + const client = this.getContainerClient(params); + const blobs = []; + + const pages = (recursive + ? client.listBlobsFlat({ prefix }) + : client.listBlobsByHierarchy("/", { prefix }) + ).byPage({ + maxPageSize, + // byPage() treats null token as valid, returning 0 pages + continuationToken: continuationToken ?? undefined + }); + + let pageIndex = 0; + let nextToken; + for await (const page of pages) { + pageIndex++; + const segment = page.segment; + nextToken = page.continuationToken; + if (!recursive) { + for (const prefix of (segment as BlobHierarchyListSegment).blobPrefixes) { + blobs.push({ + name: prefix.name, + url: `${containerName}/${prefix.name}`, + isDirectory: true + }); + } + } + for (const blob of segment.blobItems) { + blobs.push({ + name: blob.name, + url: `${containerName}/${blob.name}`, + isDirectory: false, + properties: { + contentLength: blob.properties.contentLength, + contentType: blob.properties.contentType, + creationTime: blob.properties.createdOn, + lastModified: blob.properties.lastModified, + } + }); + } + if (maxPages && pageIndex >= maxPages) { + break; + } + } + + return { data: blobs, continuationToken: nextToken }; + } + + @autobind() + public async getBlobContent(params: blob.GetBlobContentParams): + Promise { + const { options } = params; + const client = this.getBlobClient(params); + const buffer = await client.downloadToBuffer(0, null, options); + + const { encoding } = await EncodingUtils.detectEncodingFromBuffer({ buffer, bytesRead: buffer.length }); + let content; + if (encoding) { + content = new TextDecoder(encoding).decode(buffer); + } else { + content = buffer.toString(); + } + + return { content }; + } + + @autobind() + public async deleteContainer(params: blob.DeleteContainerParams): + Promise { + const { containerName, options } = params; + const client = this.getServiceClient(params); + await client.deleteContainer(containerName, options); + return; + } + + @autobind() + public async createContainer(params: blob.CreateContainerParams): + Promise { + const { containerName, unlessExists = false, options } = params; + if (unlessExists) { + const client = this.getContainerClient(params); + const response = await client.createIfNotExists(options); + return response.succeeded; + } else { + await this.getServiceClient(params).createContainer(containerName); + return true; + } + } + + @autobind() + public async generateSasUrl(params: blob.GenerateSasUrlParams): + Promise { + const { blobName, accessPolicy } = params; + const permissions = accessPolicy.AccessPolicy.Permissions; + if (blobName) { + return this.getBlobClient( + params as blob.BlobParams + ).generateSasUrl({ + permissions: BlobSASPermissions.parse(permissions.join("")) + }); + } else { + return this.getContainerClient(params).generateSasUrl({ + permissions: ContainerSASPermissions.parse(permissions.join("")) + }); + } + } + + @autobind() + public async downloadBlob(params: blob.DownloadBlobParams): + Promise { + const { localFileName, options } = params; + const client = this.getBlobClient(params); + await client.downloadToFile(localFileName, undefined, undefined, + options); + return; + } + + @autobind() + public async uploadFile(params: blob.UploadFileParams): + Promise { + return this.getBlobClient(params).uploadFile(params.fileName); + } + + @autobind() + public async deleteBlob(params: blob.DeleteBlobParams): + Promise { + const client = this.getBlobClient(params); + const response = await client.deleteIfExists(params.options); + return response.succeeded; + } + + private getServiceClient(params: blob.BaseParams) { + const { url, account, key } = params; + const credential = new StorageSharedKeyCredential(account, key); + return new BlobServiceClient(url, credential); + } + + private getContainerClient(params: blob.ContainerParams) { + const { containerName } = params; + return this.getServiceClient(params).getContainerClient(containerName); + } + + private getBlobClient(params: blob.BlobParams) { + const { blobName } = params; + return this.getContainerClient(params).getBlockBlobClient(blobName); + } +} diff --git a/src/common/constants/constants.ts b/src/common/constants/constants.ts index c26f6d97d7..b0f347bd0f 100644 --- a/src/common/constants/constants.ts +++ b/src/common/constants/constants.ts @@ -118,7 +118,7 @@ export const ApiVersion = { costManagement: "2019-01-01", }; -export const providersApiVersion: {[resourceProvider: string]: string} = { +export const providersApiVersion: { [resourceProvider: string]: string } = { "microsoft.batch": ApiVersion.armBatch, "microsoft.classicstorage": ApiVersion.armClassicStorage, "microsoft.storage": ApiVersion.armStorage, @@ -213,6 +213,20 @@ export const IpcEvent = { logoutAndLogin: "LOGOUT_AND_LOGIN", sendTelemetry: "SEND_TELEMETRY", log: "SEND_LOG", + storageBlob: { + listContainers: "STORAGE_BLOB_LIST_CONTAINERS", + getContainerProperties: "STORAGE_BLOB_GET_CONTAINER_PROPERTIES", + deleteContainer: "STORAGE_BLOB_GET_DELETE_CONTAINER", + createContainer: "STORAGE_BLOB_CREATE_CONTAINER", + + listBlobs: "STORAGE_BLOB_LIST_BLOBS", + getBlobProperties: "STORAGE_BLOB_GET_BLOB_PROPERTIES", + getBlobContent: "STORAGE_BLOB_GET_BLOB_CONTENT", + generateSasUrl: "STORAGE_BLOB_GENERATE_SAS_URL", + downloadBlob: "STORAGE_BLOB_DOWNLOAD_BLOB", + uploadFile: "STORAGE_BLOB_UPLOAD_FILE", + deleteBlob: "STORAGE_BLOB_DELETE_BLOB", + } }; export const ExternalApplication = { From 0d6b28f17771d5ccd4c584c913516070ef932957 Mon Sep 17 00:00:00 2001 From: Shiran Pasternak Date: Wed, 30 Nov 2022 13:24:53 -0500 Subject: [PATCH 3/3] Improves container delete dialog --- .../delete-container-dialog.component.ts | 2 ++ .../delete/delete-container-dialog.html | 25 ++++++++----------- .../delete/delete-container-dialog.i18n.yml | 6 +++++ .../delete/delete-container-dialog.scss | 15 +++++++++++ 4 files changed, 34 insertions(+), 14 deletions(-) create mode 100644 src/app/components/data/action/delete/delete-container-dialog.i18n.yml create mode 100644 src/app/components/data/action/delete/delete-container-dialog.scss diff --git a/src/app/components/data/action/delete/delete-container-dialog.component.ts b/src/app/components/data/action/delete/delete-container-dialog.component.ts index f3dac4053f..c6441612b5 100644 --- a/src/app/components/data/action/delete/delete-container-dialog.component.ts +++ b/src/app/components/data/action/delete/delete-container-dialog.component.ts @@ -4,6 +4,8 @@ import { autobind } from "@batch-flask/core"; import { ConfirmationDialog } from "@batch-flask/ui"; import { BlobContainer } from "app/models"; +import "./delete-container-dialog.scss"; + @Component({ selector: "bl-delete-container-dialog", templateUrl: "delete-container-dialog.html", diff --git a/src/app/components/data/action/delete/delete-container-dialog.html b/src/app/components/data/action/delete/delete-container-dialog.html index bf4d50ef13..3d69bc551f 100644 --- a/src/app/components/data/action/delete/delete-container-dialog.html +++ b/src/app/components/data/action/delete/delete-container-dialog.html @@ -1,18 +1,15 @@ + size="small" title="{{'delete-container-dialog.title' | i18n}}">
-

Do you want to permantly delete file group(s):

- - - - - - - -
Name
{{container.name}}
-

- Warning! Deleting the file group listed above is irreversible. - The action you are about to take cannot be undone. -

+

{{"delete-container-dialog.message" | i18n}}

+
    +
  • + {{ container.name}} +
  • +
+
+   + {{"delete-container-dialog.warning" | i18n}} +
diff --git a/src/app/components/data/action/delete/delete-container-dialog.i18n.yml b/src/app/components/data/action/delete/delete-container-dialog.i18n.yml new file mode 100644 index 0000000000..b64d42225d --- /dev/null +++ b/src/app/components/data/action/delete/delete-container-dialog.i18n.yml @@ -0,0 +1,6 @@ +delete-container-dialog: + title: Delete Containers + message: Are you sure you want to permanently delete the following + container(s)? + warning: Deleting the container(s) listed above is irreversible. The action + you are about to take cannot be undone. diff --git a/src/app/components/data/action/delete/delete-container-dialog.scss b/src/app/components/data/action/delete/delete-container-dialog.scss new file mode 100644 index 0000000000..3446984454 --- /dev/null +++ b/src/app/components/data/action/delete/delete-container-dialog.scss @@ -0,0 +1,15 @@ +@import "app/styles/variables"; + +bl-simple-form .message { + padding: 1em; + .storage-container-list { + padding: 1em; + li { + font-weight: bold; + } + } + .warning-message { + color: $danger-color; + font-weight: bold; + } +}