Просмотр исходного кода

Merge branch 'master' into minor

Michael Bromley 3 лет назад
Родитель
Сommit
0fcf1d1d7b
45 измененных файлов с 910 добавлено и 328 удалено
  1. 24 0
      CHANGELOG.md
  2. 1 1
      lerna.json
  3. 3 3
      packages/admin-ui-plugin/package.json
  4. 16 16
      packages/admin-ui/package.json
  5. 61 21
      packages/admin-ui/src/lib/catalog/src/components/facet-detail/facet-detail.component.ts
  6. 1 1
      packages/admin-ui/src/lib/core/src/common/version.ts
  7. 32 0
      packages/admin-ui/src/lib/core/src/data/utils/remove-readonly-custom-fields.spec.ts
  8. 29 8
      packages/admin-ui/src/lib/core/src/data/utils/remove-readonly-custom-fields.ts
  9. 1 1
      packages/admin-ui/src/lib/core/src/shared/components/tabbed-custom-fields/tabbed-custom-fields.component.ts
  10. 1 1
      packages/admin-ui/src/lib/settings/src/components/channel-list/channel-list.component.html
  11. 3 3
      packages/asset-server-plugin/package.json
  12. 0 1
      packages/asset-server-plugin/src/plugin.ts
  13. 1 1
      packages/common/package.json
  14. 2 2
      packages/common/src/generated-shop-types.ts
  15. 18 0
      packages/common/src/unique.spec.ts
  16. 9 12
      packages/common/src/unique.ts
  17. 68 0
      packages/core/e2e/facet.e2e-spec.ts
  18. 34 0
      packages/core/e2e/graphql/generated-e2e-admin-types.ts
  19. 2 2
      packages/core/e2e/graphql/generated-e2e-shop-types.ts
  20. 24 24
      packages/core/e2e/order-promotion.e2e-spec.ts
  21. 6 4
      packages/core/e2e/product-channel.e2e-spec.ts
  22. 2 2
      packages/core/package.json
  23. 20 17
      packages/core/src/api/common/parse-context.ts
  24. 3 3
      packages/core/src/api/resolvers/admin/channel.resolver.ts
  25. 10 1
      packages/core/src/api/resolvers/entity/product-entity.resolver.ts
  26. 3 1
      packages/core/src/common/types/entity-relation-paths.ts
  27. 0 1
      packages/core/src/config/catalog/default-product-variant-price-calculation-strategy.ts
  28. 1 0
      packages/core/src/config/index.ts
  29. 9 4
      packages/core/src/config/order/order-item-price-calculation-strategy.ts
  30. 0 2
      packages/core/src/plugin/default-search-plugin/search-job-buffer/collection-job-buffer.ts
  31. 4 5
      packages/core/src/plugin/default-search-plugin/search-job-buffer/search-index-job-buffer.ts
  32. 0 1
      packages/core/src/service/helpers/entity-hydrator/entity-hydrator.service.ts
  33. 3 1
      packages/core/src/service/services/product-variant.service.ts
  34. 3 3
      packages/create/package.json
  35. 9 9
      packages/dev-server/package.json
  36. 62 0
      packages/dev-server/test-plugins/fill-buffer-plugin.ts
  37. 3 3
      packages/elasticsearch-plugin/package.json
  38. 3 3
      packages/email-plugin/package.json
  39. 3 3
      packages/job-queue-plugin/package.json
  40. 2 2
      packages/payments-plugin/e2e/graphql/generated-shop-types.ts
  41. 4 4
      packages/payments-plugin/package.json
  42. 4 5
      packages/payments-plugin/src/braintree/braintree.resolver.ts
  43. 3 3
      packages/testing/package.json
  44. 8 8
      packages/ui-devkit/package.json
  45. 415 146
      yarn.lock

+ 24 - 0
CHANGELOG.md

@@ -1,3 +1,27 @@
+## <small>1.4.7 (2022-02-22)</small>
+
+
+#### Fixes
+
+* **admin-ui** Allow non-SuperAdmins to perform CRUD on Channels ([791d47d](https://github.com/vendure-ecommerce/vendure/commit/791d47d)), closes [#1402](https://github.com/vendure-ecommerce/vendure/issues/1402)
+* **admin-ui** Correctly remove readonly custom field inputs ([75780ce](https://github.com/vendure-ecommerce/vendure/commit/75780ce)), closes [#1403](https://github.com/vendure-ecommerce/vendure/issues/1403)
+* **admin-ui** Fix issues with creating FacetValues with custom fields ([d4d4ee2](https://github.com/vendure-ecommerce/vendure/commit/d4d4ee2)), closes [#1434](https://github.com/vendure-ecommerce/vendure/issues/1434)
+* **admin-ui** Update to latest Angular v12, fix build error ([e54a2f2](https://github.com/vendure-ecommerce/vendure/commit/e54a2f2)), closes [#1408](https://github.com/vendure-ecommerce/vendure/issues/1408)
+* **core** Allow CRUD on Channels for non-SuperAdmins with permissions ([fd2f039](https://github.com/vendure-ecommerce/vendure/commit/fd2f039)), closes [#1402](https://github.com/vendure-ecommerce/vendure/issues/1402)
+* **core** Correctly display unitPrice of cancelled OrderLines ([e7c4373](https://github.com/vendure-ecommerce/vendure/commit/e7c4373)), closes [#1414](https://github.com/vendure-ecommerce/vendure/issues/1414)
+* **core** Fix entity relation paths typings in TS 4.5 ([c5e6c04](https://github.com/vendure-ecommerce/vendure/commit/c5e6c04)), closes [#1409](https://github.com/vendure-ecommerce/vendure/issues/1409)
+* **core** Fix error when using internal relation custom fields ([753470a](https://github.com/vendure-ecommerce/vendure/commit/753470a)), closes [#1416](https://github.com/vendure-ecommerce/vendure/issues/1416)
+* **core** Fix for job cancellation issue (#1420) ([2862dda](https://github.com/vendure-ecommerce/vendure/commit/2862dda)), closes [#1420](https://github.com/vendure-ecommerce/vendure/issues/1420) [#1127](https://github.com/vendure-ecommerce/vendure/issues/1127)
+* **core** Fix regression in accessing OrderLine.items when not defined ([32f2cd7](https://github.com/vendure-ecommerce/vendure/commit/32f2cd7))
+* **core** Fix variant price/tax calculation when assigning to channel ([1a13e73](https://github.com/vendure-ecommerce/vendure/commit/1a13e73)), closes [#1421](https://github.com/vendure-ecommerce/vendure/issues/1421)
+* **core** Improve api context detection & error handling ([42d70f3](https://github.com/vendure-ecommerce/vendure/commit/42d70f3)), closes [#1426](https://github.com/vendure-ecommerce/vendure/issues/1426)
+* **core** Omit private facet values from Product.facetValues ([82e0b26](https://github.com/vendure-ecommerce/vendure/commit/82e0b26)), closes [#1435](https://github.com/vendure-ecommerce/vendure/issues/1435)
+* **payments-plugin** Fix logic for looking up Braintree payment method ([ad4ccf3](https://github.com/vendure-ecommerce/vendure/commit/ad4ccf3))
+
+#### Perf
+
+* **common** Increase perf of `unique` helper by ~1000x ([910adf8](https://github.com/vendure-ecommerce/vendure/commit/910adf8)), closes [#1433](https://github.com/vendure-ecommerce/vendure/issues/1433)
+
 ## <small>1.4.6 (2022-02-04)</small>
 
 

+ 1 - 1
lerna.json

@@ -2,7 +2,7 @@
   "packages": [
     "packages/*"
   ],
-  "version": "1.4.6",
+  "version": "1.4.7",
   "npmClient": "yarn",
   "useWorkspaces": true,
   "command": {

+ 3 - 3
packages/admin-ui-plugin/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/admin-ui-plugin",
-  "version": "1.4.6",
+  "version": "1.4.7",
   "main": "lib/index.js",
   "types": "lib/index.d.ts",
   "files": [
@@ -21,8 +21,8 @@
   "devDependencies": {
     "@types/express": "^4.17.8",
     "@types/fs-extra": "^9.0.1",
-    "@vendure/common": "^1.4.6",
-    "@vendure/core": "^1.4.6",
+    "@vendure/common": "^1.4.7",
+    "@vendure/core": "^1.4.7",
     "express": "^4.17.1",
     "rimraf": "^3.0.2",
     "typescript": "4.3.5"

+ 16 - 16
packages/admin-ui/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/admin-ui",
-  "version": "1.4.6",
+  "version": "1.4.7",
   "license": "MIT",
   "scripts": {
     "ng": "ng",
@@ -20,15 +20,15 @@
     "directory": "package"
   },
   "dependencies": {
-    "@angular/animations": "12.2.2",
-    "@angular/cdk": "12.2.2",
-    "@angular/common": "12.2.2",
-    "@angular/core": "12.2.2",
-    "@angular/forms": "12.2.2",
-    "@angular/language-service": "12.2.2",
-    "@angular/platform-browser": "12.2.2",
-    "@angular/platform-browser-dynamic": "12.2.2",
-    "@angular/router": "12.2.2",
+    "@angular/animations": "12.2.16",
+    "@angular/cdk": "12.2.13",
+    "@angular/common": "12.2.16",
+    "@angular/core": "12.2.16",
+    "@angular/forms": "12.2.16",
+    "@angular/language-service": "12.2.16",
+    "@angular/platform-browser": "12.2.16",
+    "@angular/platform-browser-dynamic": "12.2.16",
+    "@angular/router": "12.2.16",
     "@apollo/client": "^3.5.5",
     "@biesbjerg/ngx-translate-extract-marker": "^1.0.0",
     "@cds/core": "^5.5.2",
@@ -39,7 +39,7 @@
     "@ng-select/ng-select": "^7.2.0",
     "@ngx-translate/core": "^13.0.0",
     "@ngx-translate/http-loader": "^6.0.0",
-    "@vendure/common": "^1.4.6",
+    "@vendure/common": "^1.4.7",
     "@webcomponents/custom-elements": "^1.4.3",
     "apollo-angular": "^2.6.0",
     "apollo-upload-client": "^16.0.0",
@@ -65,10 +65,10 @@
     "zone.js": "~0.11.4"
   },
   "devDependencies": {
-    "@angular-devkit/build-angular": "~12.2.2",
-    "@angular/cli": "12.2.2",
-    "@angular/compiler": "12.2.2",
-    "@angular/compiler-cli": "12.2.2",
+    "@angular-devkit/build-angular": "~12.2.16",
+    "@angular/cli": "12.2.16",
+    "@angular/compiler": "12.2.16",
+    "@angular/compiler-cli": "12.2.16",
     "@biesbjerg/ngx-translate-extract": "^7.0.3",
     "@types/jasmine": "~3.6.0",
     "@types/jasminewd2": "~2.0.6",
@@ -88,7 +88,7 @@
     "karma-jasmine": "~4.0.0",
     "karma-jasmine-html-reporter": "^1.5.0",
     "karma-mocha-reporter": "^2.2.5",
-    "ng-packagr": "^12.2.0",
+    "ng-packagr": "12.2.7",
     "protractor": "~7.0.0",
     "puppeteer": "^8.0.0",
     "rimraf": "^3.0.2",

+ 61 - 21
packages/admin-ui/src/lib/catalog/src/components/facet-detail/facet-detail.component.ts

@@ -104,15 +104,25 @@ export class FacetDetailComponent
     addFacetValue() {
         const valuesFormArray = this.detailForm.get('values') as FormArray | null;
         if (valuesFormArray) {
-            valuesFormArray.insert(
-                valuesFormArray.length,
-                this.formBuilder.group({
-                    id: '',
-                    name: ['', Validators.required],
-                    code: '',
-                }),
-            );
-            this.values.push({ name: '', code: '' });
+            const valueGroup = this.formBuilder.group({
+                id: '',
+                name: ['', Validators.required],
+                code: '',
+            });
+            const newValue: any = { name: '', code: '' };
+            if (this.customValueFields.length) {
+                const customValueFieldsGroup = new FormGroup({});
+                newValue.customFields = {};
+
+                for (const fieldDef of this.customValueFields) {
+                    const key = fieldDef.name;
+                    customValueFieldsGroup.addControl(key, new FormControl());
+                }
+
+                valueGroup.addControl('customFields', customValueFieldsGroup);
+            }
+            valuesFormArray.insert(valuesFormArray.length, valueGroup);
+            this.values.push(newValue);
         }
     }
 
@@ -169,17 +179,15 @@ export class FacetDetailComponent
                     }
                     const valuesArray = this.detailForm.get('values');
                     if (valuesArray && valuesArray.dirty) {
-                        const newValues: CreateFacetValueInput[] = (valuesArray as FormArray).controls
-                            .filter(c => !c.value.id)
-                            .map(c => ({
-                                facetId: facet.id,
-                                code: c.value.code,
-                                translations: [{ name: c.value.name, languageCode }],
-                            }));
-                        if (newValues.length) {
+                        const createdValues = this.getCreatedFacetValues(
+                            facet,
+                            valuesArray as FormArray,
+                            languageCode,
+                        );
+                        if (createdValues.length) {
                             updateOperations.push(
                                 this.dataService.facet
-                                    .createFacetValues(newValues)
+                                    .createFacetValues(createdValues)
                                     .pipe(switchMap(() => this.dataService.facet.getFacet(this.id).single$)),
                             );
                         }
@@ -301,7 +309,9 @@ export class FacetDetailComponent
             };
             const valueControl = currentValuesFormArray.at(i);
             if (valueControl) {
-                valueControl.setValue(group);
+                valueControl.get('id')?.setValue(group.id);
+                valueControl.get('code')?.setValue(group.code);
+                valueControl.get('name')?.setValue(group.name);
             } else {
                 currentValuesFormArray.insert(i, this.formBuilder.group(group));
             }
@@ -358,8 +368,38 @@ export class FacetDetailComponent
     }
 
     /**
-     * Given an array of facet values and the values from the detailForm, this method creates an new array
-     * which can be persisted to the API.
+     * Given an array of facet values and the values from the detailForm, this method creates a new array
+     * which can be persisted to the API via a createFacetValues mutation.
+     */
+    private getCreatedFacetValues(
+        facet: FacetWithValues.Fragment,
+        valuesFormArray: FormArray,
+        languageCode: LanguageCode,
+    ): CreateFacetValueInput[] {
+        return valuesFormArray.controls
+            .filter(c => !c.value.id)
+            .map(c => c.value)
+            .map(value =>
+                createUpdatedTranslatable({
+                    translatable: { ...value, translations: [] as any },
+                    updatedFields: value,
+                    customFieldConfig: this.customValueFields,
+                    languageCode,
+                    defaultTranslation: {
+                        languageCode,
+                        name: '',
+                    },
+                }),
+            )
+            .map(input => ({
+                facetId: facet.id,
+                ...input,
+            }));
+    }
+
+    /**
+     * Given an array of facet values and the values from the detailForm, this method creates a new array
+     * which can be persisted to the API via an updateFacetValues mutation.
      */
     private getUpdatedFacetValues(
         facet: FacetWithValues.Fragment,

+ 1 - 1
packages/admin-ui/src/lib/core/src/common/version.ts

@@ -1,2 +1,2 @@
 // Auto-generated by the set-version.js script.
-export const ADMIN_UI_VERSION = '1.4.6';
+export const ADMIN_UI_VERSION = '1.4.7';

+ 32 - 0
packages/admin-ui/src/lib/core/src/data/utils/remove-readonly-custom-fields.spec.ts

@@ -90,4 +90,36 @@ describe('removeReadonlyCustomFields', () => {
             },
         } as any);
     });
+
+    it('with array of input objects', () => {
+        const config: CustomFieldConfig[] = [
+            { name: 'weight', type: 'int', list: false },
+            { name: 'rating', type: 'float', readonly: true, list: false },
+        ];
+        const entity = {
+            input: [
+                {
+                    id: 1,
+                    name: 'test',
+                    customFields: {
+                        weight: 500,
+                        rating: 123,
+                    },
+                },
+            ],
+        };
+
+        const result = removeReadonlyCustomFields(entity, config);
+        expect(result).toEqual({
+            input: [
+                {
+                    id: 1,
+                    name: 'test',
+                    customFields: {
+                        weight: 500,
+                    },
+                },
+            ],
+        } as any);
+    });
 });

+ 29 - 8
packages/admin-ui/src/lib/core/src/data/utils/remove-readonly-custom-fields.ts

@@ -6,6 +6,17 @@ import { CustomFieldConfig } from '../../common/generated-types';
 const CREATE_ENTITY_REGEX = /Create([A-Za-z]+)Input/;
 const UPDATE_ENTITY_REGEX = /Update([A-Za-z]+)Input/;
 
+type InputWithOptionalCustomFields = Record<string, any> & {
+    customFields?: Record<string, any>;
+};
+type InputWithCustomFields = Record<string, any> & {
+    customFields: Record<string, any>;
+};
+
+type EntityInput = InputWithOptionalCustomFields & {
+    translations?: InputWithOptionalCustomFields[];
+};
+
 /**
  * Checks the current documentNode for an operation with a variable named "Create<Entity>Input" or "Update<Entity>Input"
  * and if a match is found, returns the <Entity> name.
@@ -48,17 +59,27 @@ function extractInputType(type: TypeNode): NamedTypeNode {
  * Removes any `readonly` custom fields from an entity (including its translations).
  * To be used before submitting the entity for a create or update request.
  */
-export function removeReadonlyCustomFields<T extends { input?: any } & Record<string, any> = any>(
-    variables: T,
+export function removeReadonlyCustomFields(
+    variables: { input?: EntityInput | EntityInput[] } | EntityInput | EntityInput[],
     customFieldConfig: CustomFieldConfig[],
-): T {
-    if (variables.input) {
-        removeReadonly(variables.input, customFieldConfig);
+): { input?: EntityInput | EntityInput[] } | EntityInput | EntityInput[] {
+    if (!Array.isArray(variables)) {
+        if (Array.isArray(variables.input)) {
+            for (const input of variables.input) {
+                removeReadonly(input, customFieldConfig);
+            }
+        } else {
+            removeReadonly(variables.input, customFieldConfig);
+        }
+    } else {
+        for (const input of variables) {
+            removeReadonly(input, customFieldConfig);
+        }
     }
     return removeReadonly(variables, customFieldConfig);
 }
 
-function removeReadonly(input: any, customFieldConfig: CustomFieldConfig[]) {
+function removeReadonly(input: InputWithOptionalCustomFields, customFieldConfig: CustomFieldConfig[]) {
     for (const field of customFieldConfig) {
         if (field.readonly) {
             if (field.type === 'localeString') {
@@ -82,10 +103,10 @@ function removeReadonly(input: any, customFieldConfig: CustomFieldConfig[]) {
     return input;
 }
 
-function hasCustomFields(input: any): input is { customFields: { [key: string]: any } } {
+function hasCustomFields(input: any): input is InputWithCustomFields {
     return input != null && input.hasOwnProperty('customFields');
 }
 
-function hasTranslations(input: any): input is { translations: any[] } {
+function hasTranslations(input: any): input is { translations: InputWithOptionalCustomFields[] } {
     return input != null && input.hasOwnProperty('translations');
 }

+ 1 - 1
packages/admin-ui/src/lib/core/src/shared/components/tabbed-custom-fields/tabbed-custom-fields.component.ts

@@ -27,7 +27,7 @@ export class TabbedCustomFieldsComponent implements OnInit {
     }
 
     customFieldIsSet(name: string): boolean {
-        return !!this.customFieldsFormGroup.get(name);
+        return !!this.customFieldsFormGroup?.get(name);
     }
 
     private groupByTabs(customFieldConfigs: CustomFieldConfig[]): GroupedCustomFields {

+ 1 - 1
packages/admin-ui/src/lib/settings/src/components/channel-list/channel-list.component.html

@@ -1,7 +1,7 @@
 <vdr-action-bar>
     <vdr-ab-right>
         <vdr-action-bar-items locationId="channel-list"></vdr-action-bar-items>
-        <a class="btn btn-primary" [routerLink]="['./create']" *vdrIfPermissions="'SuperAdmin'">
+        <a class="btn btn-primary" [routerLink]="['./create']" *vdrIfPermissions="['SuperAdmin', 'CreateChannel']">
             <clr-icon shape="plus"></clr-icon>
             {{ 'settings.create-new-channel' | translate }}
         </a>

+ 3 - 3
packages/asset-server-plugin/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/asset-server-plugin",
-  "version": "1.4.6",
+  "version": "1.4.7",
   "main": "lib/index.js",
   "types": "lib/index.d.ts",
   "files": [
@@ -24,8 +24,8 @@
     "@types/fs-extra": "^9.0.8",
     "@types/node-fetch": "^2.5.8",
     "@types/sharp": "^0.27.1",
-    "@vendure/common": "^1.4.6",
-    "@vendure/core": "^1.4.6",
+    "@vendure/common": "^1.4.7",
+    "@vendure/core": "^1.4.7",
     "aws-sdk": "^2.856.0",
     "express": "^4.17.1",
     "node-fetch": "^2.6.1",

+ 0 - 1
packages/asset-server-plugin/src/plugin.ts

@@ -46,7 +46,6 @@ import { AssetServerOptions, ImageTransformPreset } from './types';
  *     AssetServerPlugin.init({
  *       route: 'assets',
  *       assetUploadDir: path.join(__dirname, 'assets'),
- *       port: 4000,
  *     }),
  *   ],
  * };

+ 1 - 1
packages/common/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/common",
-  "version": "1.4.6",
+  "version": "1.4.7",
   "main": "index.js",
   "license": "MIT",
   "scripts": {

+ 2 - 2
packages/common/src/generated-shop-types.ts

@@ -1622,7 +1622,7 @@ export type Mutation = {
     /**
      * Verify a Customer email address with the token sent to that address. Only applicable if `authOptions.requireVerification` is set to true.
      *
-     * If the Customer was not registered with a password in the `registerCustomerAccount` mutation, the password _must_ be
+     * If the Customer was not registered with a password in the `registerCustomerAccount` mutation, the a password _must_ be
      * provided here.
      */
     verifyCustomerAccount: VerifyCustomerAccountResult;
@@ -2834,7 +2834,7 @@ export type SearchResult = {
     facetValueIds: Array<Scalars['ID']>;
     /** An array of ids of the Collections in which this result appears */
     collectionIds: Array<Scalars['ID']>;
-    /** A relevance score for the result. Differs between database implementations */
+    /** A relevence score for the result. Differs between database implementations */
     score: Scalars['Float'];
 };
 

+ 18 - 0
packages/common/src/unique.spec.ts

@@ -30,4 +30,22 @@ describe('unique()', () => {
     it('works an empty array', () => {
         expect(unique([])).toEqual([]);
     });
+
+    it('perf on primitive array', async () => {
+        const bigArray = Array.from({ length: 50000 }).map(() => Math.random().toString().substr(2, 5));
+        const timeStart = new Date().getTime();
+        unique(bigArray);
+        const timeEnd = new Date().getTime();
+        expect(timeEnd - timeStart).toBeLessThan(100);
+    });
+
+    it('perf on object array', async () => {
+        const bigArray = Array.from({ length: 50000 })
+            .map(() => Math.random().toString().substr(2, 5))
+            .map(id => ({ id }));
+        const timeStart = new Date().getTime();
+        unique(bigArray, 'id');
+        const timeEnd = new Date().getTime();
+        expect(timeEnd - timeStart).toBeLessThan(100);
+    });
 });

+ 9 - 12
packages/common/src/unique.ts

@@ -1,19 +1,16 @@
 /**
+ * @description
  * Returns an array with only unique values. Objects are compared by reference,
  * unless the `byKey` argument is supplied, in which case matching properties will
  * be used to check duplicates
  */
+import { isObject } from './shared-utils';
+
 export function unique<T>(arr: T[], byKey?: keyof T): T[] {
-    return arr.filter((item, index, self) => {
-        return (
-            index ===
-            self.findIndex(i => {
-                if (byKey === undefined) {
-                    return i === item;
-                } else {
-                    return i[byKey] === item[byKey];
-                }
-            })
-        );
-    });
+    if (byKey == null) {
+        return Array.from(new Set(arr));
+    } else {
+        // Based on https://stackoverflow.com/a/58429784/772859
+        return [...new Map(arr.map(item => [item[byKey], item])).values()];
+    }
 }

+ 68 - 0
packages/core/e2e/facet.e2e-spec.ts

@@ -21,6 +21,7 @@ import {
     GetFacetList,
     GetFacetWithValues,
     GetProductListWithVariants,
+    GetProductWithFacetValues,
     GetProductWithVariants,
     LanguageCode,
     UpdateFacet,
@@ -178,6 +179,52 @@ describe('Facet resolver', () => {
         expect(result.facet!.name).toBe('Speaker Category');
     });
 
+    it('product.facetValues resolver omits private facets in shop-api', async () => {
+        const publicFacetValue = brandFacet.values[0];
+        const privateFacetValue = speakerTypeFacet.values[0];
+        await adminClient.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
+            input: {
+                id: 'T_1',
+                facetValueIds: [publicFacetValue.id, privateFacetValue.id],
+            },
+        });
+        const { product } = await shopClient.query<
+            GetProductWithFacetValues.Query,
+            GetProductWithFacetValues.Variables
+        >(GET_PRODUCT_WITH_FACET_VALUES, {
+            id: 'T_1',
+        });
+
+        expect(product?.facetValues.map(v => v.id).includes(publicFacetValue.id)).toBe(true);
+        expect(product?.facetValues.map(v => v.id).includes(privateFacetValue.id)).toBe(false);
+    });
+
+    it('productVariant.facetValues resolver omits private facets in shop-api', async () => {
+        const publicFacetValue = brandFacet.values[0];
+        const privateFacetValue = speakerTypeFacet.values[0];
+        await adminClient.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
+            UPDATE_PRODUCT_VARIANTS,
+            {
+                input: [
+                    {
+                        id: 'T_1',
+                        facetValueIds: [publicFacetValue.id, privateFacetValue.id],
+                    },
+                ],
+            },
+        );
+        const { product } = await shopClient.query<
+            GetProductWithFacetValues.Query,
+            GetProductWithFacetValues.Variables
+        >(GET_PRODUCT_WITH_FACET_VALUES, {
+            id: 'T_1',
+        });
+
+        const productVariant1 = product?.variants.find(v => v.id === 'T_1');
+        expect(productVariant1?.facetValues.map(v => v.id).includes(publicFacetValue.id)).toBe(true);
+        expect(productVariant1?.facetValues.map(v => v.id).includes(privateFacetValue.id)).toBe(false);
+    });
+
     describe('deletion', () => {
         let products: GetProductListWithVariants.Items[];
 
@@ -672,6 +719,27 @@ const DELETE_FACET = gql`
     }
 `;
 
+const GET_PRODUCT_WITH_FACET_VALUES = gql`
+    query GetProductWithFacetValues($id: ID!) {
+        product(id: $id) {
+            id
+            facetValues {
+                id
+                name
+                code
+            }
+            variants {
+                id
+                facetValues {
+                    id
+                    name
+                    code
+                }
+            }
+        }
+    }
+`;
+
 const GET_PRODUCTS_LIST_WITH_VARIANTS = gql`
     query GetProductListWithVariants {
         products {

+ 34 - 0
packages/core/e2e/graphql/generated-e2e-admin-types.ts

@@ -5465,6 +5465,21 @@ export type DeleteFacetMutationVariables = Exact<{
 
 export type DeleteFacetMutation = { deleteFacet: Pick<DeletionResponse, 'result' | 'message'> };
 
+export type GetProductWithFacetValuesQueryVariables = Exact<{
+    id: Scalars['ID'];
+}>;
+
+export type GetProductWithFacetValuesQuery = {
+    product?: Maybe<
+        Pick<Product, 'id'> & {
+            facetValues: Array<Pick<FacetValue, 'id' | 'name' | 'code'>>;
+            variants: Array<
+                Pick<ProductVariant, 'id'> & { facetValues: Array<Pick<FacetValue, 'id' | 'name' | 'code'>> }
+            >;
+        }
+    >;
+};
+
 export type GetProductListWithVariantsQueryVariables = Exact<{ [key: string]: never }>;
 
 export type GetProductListWithVariantsQuery = {
@@ -7807,6 +7822,25 @@ export namespace DeleteFacet {
     export type DeleteFacet = NonNullable<DeleteFacetMutation['deleteFacet']>;
 }
 
+export namespace GetProductWithFacetValues {
+    export type Variables = GetProductWithFacetValuesQueryVariables;
+    export type Query = GetProductWithFacetValuesQuery;
+    export type Product = NonNullable<GetProductWithFacetValuesQuery['product']>;
+    export type FacetValues = NonNullable<
+        NonNullable<NonNullable<GetProductWithFacetValuesQuery['product']>['facetValues']>[number]
+    >;
+    export type Variants = NonNullable<
+        NonNullable<NonNullable<GetProductWithFacetValuesQuery['product']>['variants']>[number]
+    >;
+    export type _FacetValues = NonNullable<
+        NonNullable<
+            NonNullable<
+                NonNullable<NonNullable<GetProductWithFacetValuesQuery['product']>['variants']>[number]
+            >['facetValues']
+        >[number]
+    >;
+}
+
 export namespace GetProductListWithVariants {
     export type Variables = GetProductListWithVariantsQueryVariables;
     export type Query = GetProductListWithVariantsQuery;

+ 2 - 2
packages/core/e2e/graphql/generated-e2e-shop-types.ts

@@ -1568,7 +1568,7 @@ export type Mutation = {
     /**
      * Verify a Customer email address with the token sent to that address. Only applicable if `authOptions.requireVerification` is set to true.
      *
-     * If the Customer was not registered with a password in the `registerCustomerAccount` mutation, the password _must_ be
+     * If the Customer was not registered with a password in the `registerCustomerAccount` mutation, the a password _must_ be
      * provided here.
      */
     verifyCustomerAccount: VerifyCustomerAccountResult;
@@ -2736,7 +2736,7 @@ export type SearchResult = {
     facetValueIds: Array<Scalars['ID']>;
     /** An array of ids of the Collections in which this result appears */
     collectionIds: Array<Scalars['ID']>;
-    /** A relevance score for the result. Differs between database implementations */
+    /** A relevence score for the result. Differs between database implementations */
     score: Scalars['Float'];
 };
 

+ 24 - 24
packages/core/e2e/order-promotion.e2e-spec.ts

@@ -624,7 +624,7 @@ describe('Promotions applied to Orders', () => {
                     quantity: 1,
                 });
                 orderResultGuard.assertSuccess(addItemToOrder);
-                expect(addItemToOrder!.totalWithTax).toBe(5000);
+                expect(addItemToOrder!.totalWithTax).toBe(6000);
                 expect(addItemToOrder!.discounts.length).toBe(0);
 
                 const { applyCouponCode } = await shopClient.query<
@@ -636,7 +636,7 @@ describe('Promotions applied to Orders', () => {
                 orderResultGuard.assertSuccess(applyCouponCode);
                 expect(applyCouponCode!.discounts.length).toBe(1);
                 expect(applyCouponCode!.discounts[0].description).toBe('20% discount on order');
-                expect(applyCouponCode!.totalWithTax).toBe(4000);
+                expect(applyCouponCode!.totalWithTax).toBe(4800);
             });
         });
 
@@ -702,7 +702,7 @@ describe('Promotions applied to Orders', () => {
                     quantity: 1,
                 });
                 orderResultGuard.assertSuccess(addItemToOrder);
-                expect(addItemToOrder!.totalWithTax).toBe(5000);
+                expect(addItemToOrder!.totalWithTax).toBe(6000);
                 expect(addItemToOrder!.discounts.length).toBe(0);
 
                 const { applyCouponCode } = await shopClient.query<
@@ -714,7 +714,7 @@ describe('Promotions applied to Orders', () => {
                 orderResultGuard.assertSuccess(applyCouponCode);
                 expect(applyCouponCode!.discounts.length).toBe(1);
                 expect(applyCouponCode!.discounts[0].description).toBe('$10 discount on order');
-                expect(applyCouponCode!.totalWithTax).toBe(4000);
+                expect(applyCouponCode!.totalWithTax).toBe(5000);
             });
         });
 
@@ -828,8 +828,8 @@ describe('Promotions applied to Orders', () => {
                 orderResultGuard.assertSuccess(addItemToOrder);
                 expect(addItemToOrder!.discounts.length).toBe(0);
                 expect(getItemSale1Line(addItemToOrder!.lines).discounts.length).toBe(0);
-                expect(addItemToOrder!.total).toBe(1832);
-                expect(addItemToOrder!.totalWithTax).toBe(2200);
+                expect(addItemToOrder!.total).toBe(2200);
+                expect(addItemToOrder!.totalWithTax).toBe(2640);
 
                 const { applyCouponCode } = await shopClient.query<
                     ApplyCouponCode.Mutation,
@@ -839,8 +839,8 @@ describe('Promotions applied to Orders', () => {
                 });
                 orderResultGuard.assertSuccess(applyCouponCode);
 
-                expect(applyCouponCode!.total).toBe(1334);
-                expect(applyCouponCode!.totalWithTax).toBe(1600);
+                expect(applyCouponCode!.total).toBe(1600);
+                expect(applyCouponCode!.totalWithTax).toBe(1920);
                 expect(getItemSale1Line(applyCouponCode!.lines).discounts.length).toBe(1); // 1x promotion
 
                 const { removeCouponCode } = await shopClient.query<
@@ -851,13 +851,13 @@ describe('Promotions applied to Orders', () => {
                 });
 
                 expect(getItemSale1Line(removeCouponCode!.lines).discounts.length).toBe(0);
-                expect(removeCouponCode!.total).toBe(1832);
-                expect(removeCouponCode!.totalWithTax).toBe(2200);
+                expect(removeCouponCode!.total).toBe(2200);
+                expect(removeCouponCode!.totalWithTax).toBe(2640);
 
                 const { activeOrder } = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
                 expect(getItemSale1Line(activeOrder!.lines).discounts.length).toBe(0);
-                expect(activeOrder!.total).toBe(1832);
-                expect(activeOrder!.totalWithTax).toBe(2200);
+                expect(activeOrder!.total).toBe(2200);
+                expect(activeOrder!.totalWithTax).toBe(2640);
             });
         });
 
@@ -931,8 +931,8 @@ describe('Promotions applied to Orders', () => {
                 orderResultGuard.assertSuccess(addItemToOrder);
                 expect(addItemToOrder!.discounts.length).toBe(0);
                 expect(addItemToOrder!.lines[0].discounts.length).toBe(0);
-                expect(addItemToOrder!.total).toBe(4167);
-                expect(addItemToOrder!.totalWithTax).toBe(5000);
+                expect(addItemToOrder!.total).toBe(5000);
+                expect(addItemToOrder!.totalWithTax).toBe(6000);
 
                 const { applyCouponCode } = await shopClient.query<
                     ApplyCouponCode.Mutation,
@@ -942,8 +942,8 @@ describe('Promotions applied to Orders', () => {
                 });
                 orderResultGuard.assertSuccess(applyCouponCode);
 
-                expect(applyCouponCode!.total).toBe(2083);
-                expect(applyCouponCode!.totalWithTax).toBe(2500);
+                expect(applyCouponCode!.total).toBe(2500);
+                expect(applyCouponCode!.totalWithTax).toBe(3000);
                 expect(applyCouponCode!.lines[0].discounts.length).toBe(1); // 1x promotion
             });
         });
@@ -1068,8 +1068,8 @@ describe('Promotions applied to Orders', () => {
                 expect(setOrderShippingMethod.discounts).toEqual([]);
                 expect(setOrderShippingMethod.shipping).toBe(287);
                 expect(setOrderShippingMethod.shippingWithTax).toBe(345);
-                expect(setOrderShippingMethod.total).toBe(4454);
-                expect(setOrderShippingMethod.totalWithTax).toBe(5345);
+                expect(setOrderShippingMethod.total).toBe(5287);
+                expect(setOrderShippingMethod.totalWithTax).toBe(6345);
 
                 const { applyCouponCode } = await shopClient.query<
                     ApplyCouponCode.Mutation,
@@ -1083,8 +1083,8 @@ describe('Promotions applied to Orders', () => {
                 expect(applyCouponCode.discounts[0].description).toBe('Free shipping');
                 expect(applyCouponCode.shipping).toBe(0);
                 expect(applyCouponCode.shippingWithTax).toBe(0);
-                expect(applyCouponCode.total).toBe(4167);
-                expect(applyCouponCode.totalWithTax).toBe(5000);
+                expect(applyCouponCode.total).toBe(5000);
+                expect(applyCouponCode.totalWithTax).toBe(6000);
             });
 
             // https://github.com/vendure-ecommerce/vendure/pull/1150
@@ -1251,8 +1251,8 @@ describe('Promotions applied to Orders', () => {
                     saleItemLine.discounts.find(a => a.type === AdjustmentType.PROMOTION)?.description,
                 ).toBe('item promo');
                 expect(apply1.discounts.length).toBe(1);
-                expect(apply1.total).toBe(5001);
-                expect(apply1.totalWithTax).toBe(6000);
+                expect(apply1.total).toBe(6000);
+                expect(apply1.totalWithTax).toBe(7200);
 
                 // Apply the Order-level promo
                 const { applyCouponCode: apply2 } = await shopClient.query<
@@ -1267,8 +1267,8 @@ describe('Promotions applied to Orders', () => {
                     'item promo',
                     'order promo',
                 ]);
-                expect(apply2.total).toBe(4250);
-                expect(apply2.totalWithTax).toBe(5100);
+                expect(apply2.total).toBe(5100);
+                expect(apply2.totalWithTax).toBe(6120);
             });
         });
     });

+ 6 - 4
packages/core/e2e/product-channel.e2e-spec.ts

@@ -20,6 +20,7 @@ import {
     ProductVariantFragment,
     RemoveProductsFromChannel,
     RemoveProductVariantsFromChannel,
+    UpdateChannel,
     UpdateProduct,
 } from './graphql/generated-e2e-admin-types';
 import {
@@ -33,6 +34,7 @@ import {
     GET_PRODUCT_WITH_VARIANTS,
     REMOVE_PRODUCTVARIANT_FROM_CHANNEL,
     REMOVE_PRODUCT_FROM_CHANNEL,
+    UPDATE_CHANNEL,
     UPDATE_PRODUCT,
 } from './graphql/shared-definitions';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
@@ -169,11 +171,11 @@ describe('ChannelAware Products and ProductVariants', () => {
             });
 
             expect(product!.variants.map(v => v.price)).toEqual(
-                product1.variants.map(v => Math.round((v.price * PRICE_FACTOR) / 1.2)),
+                product1.variants.map(v => Math.round(v.price * PRICE_FACTOR)),
             );
             // Second Channel is configured to include taxes in price, so they should be the same.
             expect(product!.variants.map(v => v.priceWithTax)).toEqual(
-                product1.variants.map(v => Math.round((v.priceWithTax * PRICE_FACTOR) / 1.2)),
+                product1.variants.map(v => Math.round(v.priceWithTax * PRICE_FACTOR)),
             );
         });
 
@@ -305,11 +307,11 @@ describe('ChannelAware Products and ProductVariants', () => {
             });
             expect(product!.channels.map(c => c.id).sort()).toEqual(['T_3']);
             expect(product!.variants.map(v => v.price)).toEqual([
-                Math.round((product1.variants[0].price * PRICE_FACTOR) / 1.2),
+                Math.round(product1.variants[0].price * PRICE_FACTOR),
             ]);
             // Third Channel is configured to include taxes in price, so they should be the same.
             expect(product!.variants.map(v => v.priceWithTax)).toEqual([
-                product1.variants[0].price * PRICE_FACTOR,
+                Math.round(product1.variants[0].priceWithTax * PRICE_FACTOR),
             ]);
 
             await adminClient.setChannelToken(E2E_DEFAULT_CHANNEL_TOKEN);

+ 2 - 2
packages/core/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/core",
-  "version": "1.4.6",
+  "version": "1.4.7",
   "description": "A modern, headless ecommerce framework",
   "repository": {
     "type": "git",
@@ -49,7 +49,7 @@
     "@nestjs/testing": "7.6.17",
     "@nestjs/typeorm": "7.1.5",
     "@types/fs-extra": "^9.0.1",
-    "@vendure/common": "^1.4.6",
+    "@vendure/common": "^1.4.7",
     "apollo-server-express": "2.24.1",
     "bcrypt": "^5.0.0",
     "body-parser": "^1.19.0",

+ 20 - 17
packages/core/src/api/common/parse-context.ts

@@ -1,8 +1,10 @@
 import { ArgumentsHost, ExecutionContext } from '@nestjs/common';
-import { GqlExecutionContext } from '@nestjs/graphql';
+import { GqlContextType, GqlExecutionContext } from '@nestjs/graphql';
 import { Request, Response } from 'express';
 import { GraphQLResolveInfo } from 'graphql';
 
+import { InternalServerError } from '../../common/error/errors';
+
 export type RestContext = { req: Request; res: Response; isGraphQL: false; info: undefined };
 export type GraphQLContext = {
     req: Request;
@@ -26,22 +28,23 @@ export function parseContext(context: ExecutionContext | ArgumentsHost): RestCon
         };
     }
 
-    const graphQlContext = GqlExecutionContext.create(context as ExecutionContext);
-    const info = graphQlContext.getInfo();
-    let req: Request;
-    let res: Response;
-    if (info) {
-        const ctx = graphQlContext.getContext();
-        req = ctx.req;
-        res = ctx.res;
+    if (context.getType() === 'http') {
+        const httpContext = context.switchToHttp();
+        return {
+            isGraphQL: false,
+            req: httpContext.getRequest(),
+            res: httpContext.getResponse(),
+            info: undefined,
+        };
+    } else if (context.getType<GqlContextType>() === 'graphql') {
+        const gqlContext = GqlExecutionContext.create(context as ExecutionContext);
+        return {
+            isGraphQL: true,
+            req: gqlContext.getContext().req,
+            res: gqlContext.getContext().res,
+            info: gqlContext.getInfo(),
+        };
     } else {
-        req = context.switchToHttp().getRequest();
-        res = context.switchToHttp().getResponse();
+        throw new InternalServerError(`Context "${context.getType()}" is not supported.`);
     }
-    return {
-        req,
-        res,
-        info,
-        isGraphQL: !!info,
-    };
 }

+ 3 - 3
packages/core/src/api/resolvers/admin/channel.resolver.ts

@@ -43,7 +43,7 @@ export class ChannelResolver {
 
     @Transaction()
     @Mutation()
-    @Allow(Permission.SuperAdmin)
+    @Allow(Permission.SuperAdmin, Permission.CreateChannel)
     async createChannel(
         @Ctx() ctx: RequestContext,
         @Args() args: MutationCreateChannelArgs,
@@ -61,7 +61,7 @@ export class ChannelResolver {
 
     @Transaction()
     @Mutation()
-    @Allow(Permission.SuperAdmin)
+    @Allow(Permission.SuperAdmin, Permission.UpdateChannel)
     async updateChannel(
         @Ctx() ctx: RequestContext,
         @Args() args: MutationUpdateChannelArgs,
@@ -75,7 +75,7 @@ export class ChannelResolver {
 
     @Transaction()
     @Mutation()
-    @Allow(Permission.SuperAdmin)
+    @Allow(Permission.SuperAdmin, Permission.DeleteChannel)
     async deleteChannel(
         @Ctx() ctx: RequestContext,
         @Args() args: MutationDeleteChannelArgs,

+ 10 - 1
packages/core/src/api/resolvers/entity/product-entity.resolver.ts

@@ -89,6 +89,7 @@ export class ProductEntityResolver {
     async facetValues(
         @Ctx() ctx: RequestContext,
         @Parent() product: Product,
+        @Api() apiType: ApiType,
     ): Promise<Array<Translated<FacetValue>>> {
         if (product.facetValues?.length === 0) {
             return [];
@@ -99,7 +100,15 @@ export class ProductEntityResolver {
         } else {
             facetValues = await this.productService.getFacetValuesForProduct(ctx, product.id);
         }
-        return facetValues.filter(fv => fv.channels.find(c => idsAreEqual(c.id, ctx.channelId)));
+        return facetValues.filter(fv => {
+            if (!fv.channels.find(c => idsAreEqual(c.id, ctx.channelId))) {
+                return false;
+            }
+            if (apiType === 'shop' && fv.facet.isPrivate) {
+                return false;
+            }
+            return true;
+        });
     }
 
     @ResolveField()

+ 3 - 1
packages/core/src/common/types/entity-relation-paths.ts

@@ -49,7 +49,9 @@ export type PathsToStringProps2<T extends VendureEntity> = T extends string
     : {
           [K in EntityRelationKeys<T>]: T[K] extends VendureEntity[]
               ? [K, PathsToStringProps1<T[K][number]>]
-              : [K, PathsToStringProps1<T[K]>];
+              : T[K] extends VendureEntity
+              ? [K, PathsToStringProps1<T[K]>]
+              : never;
       }[Extract<EntityRelationKeys<T>, string>];
 
 export type TripleDotPath = `${string}.${string}.${string}`;

+ 0 - 1
packages/core/src/config/catalog/default-product-variant-price-calculation-strategy.ts

@@ -25,7 +25,6 @@ export class DefaultProductVariantPriceCalculationStrategy implements ProductVar
         const { inputPrice, activeTaxZone, ctx, taxCategory } = args;
         let price = inputPrice;
         let priceIncludesTax = false;
-        const taxRate = await this.taxRateService.getApplicableTaxRate(ctx, activeTaxZone, taxCategory);
 
         if (ctx.channel.pricesIncludeTax) {
             const isDefaultZone = idsAreEqual(activeTaxZone.id, ctx.channel.defaultTaxZone.id);

+ 1 - 0
packages/core/src/config/index.ts

@@ -10,6 +10,7 @@ export * from './auth/password-hashing-strategy';
 export * from './auth/password-validation-strategy';
 export * from './catalog/collection-filter';
 export * from './catalog/default-collection-filters';
+export * from './catalog/product-variant-price-calculation-strategy';
 export * from './config.module';
 export * from './config.service';
 export * from './custom-field/custom-field-types';

+ 9 - 4
packages/core/src/config/order/order-item-price-calculation-strategy.ts

@@ -5,9 +5,14 @@ import { ProductVariant } from '../../entity/product-variant/product-variant.ent
 
 /**
  * @description
- * The OrderItemPriceCalculationStrategy defines the price of an OrderItem when a ProductVariant gets added
- * to an order via the `addItemToOrder` mutation. By default the {@link DefaultOrderItemPriceCalculationStrategy}
- * is used.
+ * The OrderItemPriceCalculationStrategy defines the price of an OrderItem. By default the 
+ * {@link DefaultOrderItemPriceCalculationStrategy} is used.
+ * 
+ * ### When is the strategy invoked ?
+ * * addItemToOrder (only on the new order line)
+ * * adjustOrderLine  (only on the adjusted order line)
+ * * setOrderShippingAddress (on all order lines)
+ * * setOrderBillingAddress (on all order lines)
  *
  * ### OrderItemPriceCalculationStrategy vs Promotions
  * Both the OrderItemPriceCalculationStrategy and Promotions can be used to alter the price paid for a product.
@@ -34,7 +39,7 @@ import { ProductVariant } from '../../entity/product-variant/product-variant.ent
  * * A gift-wrapping service, where a boolean custom field is defined on the OrderLine. If `true`,
  *   a gift-wrapping surcharge would be added to the price.
  * * A product-configurator where e.g. various finishes, colors, and materials can be selected and stored
- *   as OrderLine custom fields.
+ *   as OrderLine custom fields (see [Customizing models](/docs/developer-guide/customizing-models/#configurable-order-products).
  *
  * @docsCategory Orders
  */

+ 0 - 2
packages/core/src/plugin/default-search-plugin/search-job-buffer/collection-job-buffer.ts

@@ -3,7 +3,6 @@ import { unique } from '@vendure/common/lib/unique';
 
 import { Job, JobBuffer } from '../../../job-queue/index';
 import { ApplyCollectionFiltersJobData } from '../../../service/services/collection.service';
-import { UpdateIndexQueueJobData, UpdateVariantsByIdJobData, UpdateVariantsJobData } from '../types';
 
 export class CollectionJobBuffer implements JobBuffer<ApplyCollectionFiltersJobData> {
     readonly id = 'search-plugin-apply-collection-filters';
@@ -27,7 +26,6 @@ export class CollectionJobBuffer implements JobBuffer<ApplyCollectionFiltersJobD
                 applyToChangedVariantsOnly: referenceJob.data.applyToChangedVariantsOnly,
             },
         });
-
         return [batchedCollectionJob];
     }
 }

+ 4 - 5
packages/core/src/plugin/default-search-plugin/search-job-buffer/search-index-job-buffer.ts

@@ -28,14 +28,14 @@ export class SearchIndexJobBuffer implements JobBuffer<UpdateIndexQueueJobData>
             collectedJobs,
             item => item.data.type === 'update-product',
         );
-
         const jobsToAdd = [...collectedJobs];
 
         if (variantsJobs.length) {
-            const variantIdsToUpdate = variantsJobs.reduce((result, job) => {
+            const variantIdsToUpdate: ID[] = [];
+            for (const job of variantsJobs) {
                 const ids = job.data.type === 'update-variants-by-id' ? job.data.ids : job.data.variantIds;
-                return [...result, ...ids];
-            }, [] as ID[]);
+                variantIdsToUpdate.push(...ids);
+            }
 
             const referenceJob = variantsJobs[0];
             const batchedVariantJob = new Job<UpdateVariantsByIdJobData>({
@@ -62,7 +62,6 @@ export class SearchIndexJobBuffer implements JobBuffer<UpdateIndexQueueJobData>
             }
             jobsToAdd.push(...(uniqueProductJobs as Job[]));
         }
-
         return jobsToAdd;
     }
 

+ 0 - 1
packages/core/src/service/helpers/entity-hydrator/entity-hydrator.service.ts

@@ -5,7 +5,6 @@ import { unique } from '@vendure/common/lib/unique';
 
 import { RequestContext } from '../../../api/common/request-context';
 import { InternalServerError } from '../../../common/error/errors';
-import { Translatable } from '../../../common/types/locale-types';
 import { TransactionalConnection } from '../../../connection/transactional-connection';
 import { VendureEntity } from '../../../entity/base/base.entity';
 import { ProductVariant } from '../../../entity/product-variant/product-variant.entity';

+ 3 - 1
packages/core/src/service/services/product-variant.service.ts

@@ -635,10 +635,12 @@ export class ProductVariantService {
             await this.applyChannelPriceAndTax(variant, ctx);
             await this.channelService.assignToChannels(ctx, Product, variant.productId, [input.channelId]);
             await this.channelService.assignToChannels(ctx, ProductVariant, variant.id, [input.channelId]);
+            const targetChannel = await this.channelService.findOne(ctx, input.channelId);
+            const price = targetChannel?.pricesIncludeTax ? variant.priceWithTax : variant.price;
             await this.createOrUpdateProductVariantPrice(
                 ctx,
                 variant.id,
-                variant.price * priceFactor,
+                Math.round(price * priceFactor),
                 input.channelId,
             );
             const assetIds = variant.assets?.map(a => a.assetId) || [];

+ 3 - 3
packages/create/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/create",
-  "version": "1.4.6",
+  "version": "1.4.7",
   "license": "MIT",
   "bin": {
     "create": "./index.js"
@@ -28,13 +28,13 @@
     "@types/handlebars": "^4.1.0",
     "@types/listr": "^0.14.2",
     "@types/semver": "^6.2.2",
-    "@vendure/core": "^1.4.6",
+    "@vendure/core": "^1.4.7",
     "rimraf": "^3.0.2",
     "ts-node": "^10.2.1",
     "typescript": "4.3.5"
   },
   "dependencies": {
-    "@vendure/common": "^1.4.6",
+    "@vendure/common": "^1.4.7",
     "chalk": "^4.1.0",
     "commander": "^7.1.0",
     "cross-spawn": "^7.0.3",

+ 9 - 9
packages/dev-server/package.json

@@ -1,6 +1,6 @@
 {
   "name": "dev-server",
-  "version": "1.4.6",
+  "version": "1.4.7",
   "main": "index.js",
   "license": "MIT",
   "private": true,
@@ -14,18 +14,18 @@
     "load-test:100k": "node -r ts-node/register load-testing/run-load-test.ts 100000"
   },
   "dependencies": {
-    "@vendure/admin-ui-plugin": "^1.4.6",
-    "@vendure/asset-server-plugin": "^1.4.6",
-    "@vendure/common": "^1.4.6",
-    "@vendure/core": "^1.4.6",
-    "@vendure/elasticsearch-plugin": "^1.4.6",
-    "@vendure/email-plugin": "^1.4.6",
+    "@vendure/admin-ui-plugin": "^1.4.7",
+    "@vendure/asset-server-plugin": "^1.4.7",
+    "@vendure/common": "^1.4.7",
+    "@vendure/core": "^1.4.7",
+    "@vendure/elasticsearch-plugin": "^1.4.7",
+    "@vendure/email-plugin": "^1.4.7",
     "typescript": "4.3.5"
   },
   "devDependencies": {
     "@types/csv-stringify": "^3.1.0",
-    "@vendure/testing": "^1.4.6",
-    "@vendure/ui-devkit": "^1.4.6",
+    "@vendure/testing": "^1.4.7",
+    "@vendure/ui-devkit": "^1.4.7",
     "concurrently": "^5.0.0",
     "csv-stringify": "^5.3.3"
   }

+ 62 - 0
packages/dev-server/test-plugins/fill-buffer-plugin.ts

@@ -0,0 +1,62 @@
+import { Mutation, Query, Resolver } from '@nestjs/graphql';
+import {
+    CollectionService,
+    Ctx,
+    Logger,
+    PluginCommonModule,
+    ProductVariantService,
+    RequestContext,
+    VendurePlugin,
+} from '@vendure/core';
+import gql from 'graphql-tag';
+
+@Resolver()
+class FillBufferResolver {
+    constructor(
+        private productVariantService: ProductVariantService,
+        private collectionService: CollectionService,
+    ) {}
+
+    @Mutation()
+    async fillBuffer(@Ctx() ctx: RequestContext) {
+        const { items: variants } = await this.productVariantService.findAll(ctx);
+        const { items: collections } = await this.collectionService.findAll(ctx);
+        const limit = 2000;
+        for (let i = 0; i < limit; i++) {
+            const variant = variants[i % variants.length];
+            await this.productVariantService.update(ctx, [
+                {
+                    id: variant.id,
+                    enabled: !variant.enabled,
+                },
+            ]);
+            const collection = collections[i % collections.length];
+            await this.collectionService.update(ctx, {
+                id: collection.id,
+                isPrivate: !collection.isPrivate,
+            });
+            if (i % 100 === 0) {
+                Logger.info(`Updated ${i} / ${limit} items...`);
+            }
+        }
+        Logger.info(`Done!`);
+        return true;
+    }
+}
+
+/**
+ * Plugin to create a lot of buffered jobs to test help investigate and fix
+ * issue https://github.com/vendure-ecommerce/vendure/issues/1433
+ */
+@VendurePlugin({
+    imports: [PluginCommonModule],
+    adminApiExtensions: {
+        schema: gql`
+            extend type Mutation {
+                fillBuffer: Boolean!
+            }
+        `,
+        resolvers: [FillBufferResolver],
+    },
+})
+export class FillBufferPlugin {}

+ 3 - 3
packages/elasticsearch-plugin/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/elasticsearch-plugin",
-  "version": "1.4.6",
+  "version": "1.4.7",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "lib/index.d.ts",
@@ -25,8 +25,8 @@
     "fast-deep-equal": "^3.1.3"
   },
   "devDependencies": {
-    "@vendure/common": "^1.4.6",
-    "@vendure/core": "^1.4.6",
+    "@vendure/common": "^1.4.7",
+    "@vendure/core": "^1.4.7",
     "rimraf": "^3.0.2",
     "typescript": "4.3.5"
   }

+ 3 - 3
packages/email-plugin/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/email-plugin",
-  "version": "1.4.6",
+  "version": "1.4.7",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "lib/index.d.ts",
@@ -35,8 +35,8 @@
     "@types/fs-extra": "^9.0.1",
     "@types/handlebars": "^4.1.0",
     "@types/mjml": "^4.0.4",
-    "@vendure/common": "^1.4.6",
-    "@vendure/core": "^1.4.6",
+    "@vendure/common": "^1.4.7",
+    "@vendure/core": "^1.4.7",
     "rimraf": "^3.0.2",
     "typescript": "4.3.5"
   }

+ 3 - 3
packages/job-queue-plugin/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/job-queue-plugin",
-  "version": "1.4.6",
+  "version": "1.4.7",
   "license": "MIT",
   "main": "package/index.js",
   "types": "package/index.d.ts",
@@ -24,8 +24,8 @@
   "devDependencies": {
     "@google-cloud/pubsub": "^2.8.0",
     "@types/redis": "^2.8.28",
-    "@vendure/common": "^1.4.6",
-    "@vendure/core": "^1.4.6",
+    "@vendure/common": "^1.4.7",
+    "@vendure/core": "^1.4.7",
     "bullmq": "^1.40.1",
     "redis": "^3.0.2",
     "rimraf": "^3.0.2",

+ 2 - 2
packages/payments-plugin/e2e/graphql/generated-shop-types.ts

@@ -1568,7 +1568,7 @@ export type Mutation = {
     /**
      * Verify a Customer email address with the token sent to that address. Only applicable if `authOptions.requireVerification` is set to true.
      *
-     * If the Customer was not registered with a password in the `registerCustomerAccount` mutation, the password _must_ be
+     * If the Customer was not registered with a password in the `registerCustomerAccount` mutation, the a password _must_ be
      * provided here.
      */
     verifyCustomerAccount: VerifyCustomerAccountResult;
@@ -2736,7 +2736,7 @@ export type SearchResult = {
     facetValueIds: Array<Scalars['ID']>;
     /** An array of ids of the Collections in which this result appears */
     collectionIds: Array<Scalars['ID']>;
-    /** A relevance score for the result. Differs between database implementations */
+    /** A relevence score for the result. Differs between database implementations */
     score: Scalars['Float'];
 };
 

+ 4 - 4
packages/payments-plugin/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@vendure/payments-plugin",
-    "version": "1.4.6",
+    "version": "1.4.7",
     "license": "MIT",
     "main": "package/index.js",
     "types": "package/index.d.ts",
@@ -28,9 +28,9 @@
     "devDependencies": {
         "@mollie/api-client": "^3.5.1",
         "@types/braintree": "^2.22.15",
-        "@vendure/common": "^1.4.6",
-        "@vendure/core": "^1.4.6",
-        "@vendure/testing": "^1.4.6",
+        "@vendure/common": "^1.4.7",
+        "@vendure/core": "^1.4.7",
+        "@vendure/testing": "^1.4.7",
         "braintree": "^3.0.0",
         "nock": "^13.1.4",
         "rimraf": "^3.0.2",

+ 4 - 5
packages/payments-plugin/src/braintree/braintree.resolver.ts

@@ -47,11 +47,10 @@ export class BraintreeResolver {
     }
 
     private async getPaymentMethodArgs(ctx: RequestContext): Promise<PaymentMethodArgsHash> {
-        const method = await this.connection.getRepository(ctx, PaymentMethod).findOne({
-            where: {
-                code: braintreePaymentMethodHandler.code,
-            },
-        });
+        const method = (await this.connection.getRepository(ctx, PaymentMethod).find()).find(
+            m => m.handler.code === braintreePaymentMethodHandler.code,
+        );
+
         if (!method) {
             throw new InternalServerError(`[${loggerCtx}] Could not find Braintree PaymentMethod`);
         }

+ 3 - 3
packages/testing/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/testing",
-  "version": "1.4.6",
+  "version": "1.4.7",
   "description": "End-to-end testing tools for Vendure projects",
   "keywords": [
     "vendure",
@@ -34,7 +34,7 @@
   },
   "dependencies": {
     "@types/node-fetch": "^2.5.4",
-    "@vendure/common": "^1.4.6",
+    "@vendure/common": "^1.4.7",
     "faker": "^4.1.0",
     "form-data": "^3.0.0",
     "graphql": "15.5.1",
@@ -45,7 +45,7 @@
   "devDependencies": {
     "@types/mysql": "^2.15.15",
     "@types/pg": "^7.14.5",
-    "@vendure/core": "^1.4.6",
+    "@vendure/core": "^1.4.7",
     "mysql": "^2.18.1",
     "pg": "^8.4.0",
     "rimraf": "^3.0.0",

+ 8 - 8
packages/ui-devkit/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/ui-devkit",
-  "version": "1.4.6",
+  "version": "1.4.7",
   "description": "A library for authoring Vendure Admin UI extensions",
   "keywords": [
     "vendure",
@@ -36,12 +36,12 @@
     "url": "https://github.com/vendure-ecommerce/vendure/issues"
   },
   "dependencies": {
-    "@angular-devkit/build-angular": "12.2.2",
-    "@angular/cli": "12.2.2",
-    "@angular/compiler": "12.2.2",
-    "@angular/compiler-cli": "12.2.2",
-    "@vendure/admin-ui": "^1.4.6",
-    "@vendure/common": "^1.4.6",
+    "@angular-devkit/build-angular": "12.2.16",
+    "@angular/cli": "12.2.16",
+    "@angular/compiler": "12.2.16",
+    "@angular/compiler-cli": "12.2.16",
+    "@vendure/admin-ui": "^1.4.7",
+    "@vendure/common": "^1.4.7",
     "chalk": "^4.1.0",
     "chokidar": "^3.5.1",
     "fs-extra": "^10.0.0",
@@ -52,7 +52,7 @@
     "@rollup/plugin-node-resolve": "^11.2.0",
     "@types/fs-extra": "^9.0.8",
     "@types/glob": "^7.1.3",
-    "@vendure/core": "^1.4.6",
+    "@vendure/core": "^1.4.7",
     "rimraf": "^3.0.2",
     "rollup": "^2.40.0",
     "rollup-plugin-terser": "^7.0.2",

+ 415 - 146
yarn.lock

@@ -10,24 +10,24 @@
     "@jridgewell/resolve-uri" "1.0.0"
     sourcemap-codec "1.4.8"
 
-"@angular-devkit/architect@0.1202.2":
-  version "0.1202.2"
-  resolved "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1202.2.tgz#212f07d3edb23cfeab0052fa62ba56b4a96be8e2"
-  integrity sha512-ylceL10SlftuhE4/rNzDeLLTm+e3Wt1PmMvBd4e+Q2bk1E+Ws/scGKpwfnYPzFaACn5kjg5qZoJOkHSprIfNlQ==
+"@angular-devkit/architect@0.1202.16":
+  version "0.1202.16"
+  resolved "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1202.16.tgz#8d11fe26a1fb693b70043f98b8bcb7e4960adc62"
+  integrity sha512-VUGyAr+5RmlcPjo8mZSRJ/wkm3hCPn9PJyorAnc1IzrqD+XkgcDME86HP3YheLsOsc1Mn7j6Zh3T1rAclAWw/w==
   dependencies:
-    "@angular-devkit/core" "12.2.2"
+    "@angular-devkit/core" "12.2.16"
     rxjs "6.6.7"
 
-"@angular-devkit/build-angular@12.2.2", "@angular-devkit/build-angular@~12.2.2":
-  version "12.2.2"
-  resolved "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-12.2.2.tgz#0089b9a3a8a5a9d290140e7e6069c0839d8a77cc"
-  integrity sha512-/KBz8YlujmRZwWqk3fjV5S4IWKEfDE6Vhpr2uo1GC6KtdK7/tA9usvm3ZGAFMu3DXn3eJwe2StgUnegPg3gqxA==
+"@angular-devkit/build-angular@12.2.16", "@angular-devkit/build-angular@~12.2.16":
+  version "12.2.16"
+  resolved "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-12.2.16.tgz#99c2fae84133f22bbe7d13995a2b57cf1f650088"
+  integrity sha512-0UlSaX3LngsBUp6Da69XlptyeZn7CTt/3sKijQDtkEw/J37vpMcXYD4nUuU7q7mVMoPQwMyPWdF+JBsBFz6f7A==
   dependencies:
     "@ampproject/remapping" "1.0.1"
-    "@angular-devkit/architect" "0.1202.2"
-    "@angular-devkit/build-optimizer" "0.1202.2"
-    "@angular-devkit/build-webpack" "0.1202.2"
-    "@angular-devkit/core" "12.2.2"
+    "@angular-devkit/architect" "0.1202.16"
+    "@angular-devkit/build-optimizer" "0.1202.16"
+    "@angular-devkit/build-webpack" "0.1202.16"
+    "@angular-devkit/core" "12.2.16"
     "@babel/core" "7.14.8"
     "@babel/generator" "7.14.8"
     "@babel/helper-annotate-as-pure" "7.14.5"
@@ -39,7 +39,7 @@
     "@babel/template" "7.14.5"
     "@discoveryjs/json-ext" "0.5.3"
     "@jsdevtools/coverage-istanbul-loader" "3.0.5"
-    "@ngtools/webpack" "12.2.2"
+    "@ngtools/webpack" "12.2.16"
     ansi-colors "4.1.1"
     babel-loader "8.2.2"
     browserslist "^4.9.1"
@@ -48,10 +48,10 @@
     circular-dependency-plugin "5.2.2"
     copy-webpack-plugin "9.0.1"
     core-js "3.16.0"
-    critters "0.0.10"
+    critters "0.0.12"
     css-loader "6.2.0"
     css-minimizer-webpack-plugin "3.0.2"
-    esbuild "0.12.17"
+    esbuild-wasm "0.13.8"
     find-cache-dir "3.3.1"
     glob "7.1.7"
     https-proxy-agent "5.0.0"
@@ -61,7 +61,7 @@
     less-loader "10.0.1"
     license-webpack-plugin "2.3.20"
     loader-utils "2.0.0"
-    mini-css-extract-plugin "2.1.0"
+    mini-css-extract-plugin "2.4.2"
     minimatch "3.0.4"
     open "8.2.1"
     ora "5.4.1"
@@ -89,31 +89,33 @@
     tslib "2.3.0"
     webpack "5.50.0"
     webpack-dev-middleware "5.0.0"
-    webpack-dev-server "3.11.2"
+    webpack-dev-server "3.11.3"
     webpack-merge "5.8.0"
     webpack-subresource-integrity "1.5.2"
+  optionalDependencies:
+    esbuild "0.13.8"
 
-"@angular-devkit/build-optimizer@0.1202.2":
-  version "0.1202.2"
-  resolved "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1202.2.tgz#301c3410875f6b468c1a4b9be35e3b2825bf6a89"
-  integrity sha512-53CV0mmDV5lmRiuBgX4WuSUta6wzRyFUebZmMaLjSCjXXt6Ca0RaVcVWeojZ8aiuIcFQhn1+WLYL7WmFZ8ICrQ==
+"@angular-devkit/build-optimizer@0.1202.16":
+  version "0.1202.16"
+  resolved "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1202.16.tgz#af63ac5b09cc3d15509137c1ef87bd9cee36fa60"
+  integrity sha512-7DO195vDwOnCCHrzjr6ajSNkvCzW6O/ERbVezCA46NZuS9lW9LnWpP3CE9iDnhzup7hjrM336/dXmYJO8Hw1MA==
   dependencies:
     source-map "0.7.3"
     tslib "2.3.0"
     typescript "4.3.5"
 
-"@angular-devkit/build-webpack@0.1202.2":
-  version "0.1202.2"
-  resolved "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1202.2.tgz#669c8154b443c384460271b03b5551851f1d7f33"
-  integrity sha512-UeD2q16UKIFPkFBH2afA8qChSBvjfSEDtov3VjRujXn3l5SXB6OQEFdiI5ga4IgpRE4+kuCKwNWUsiZHQ0ucCw==
+"@angular-devkit/build-webpack@0.1202.16":
+  version "0.1202.16"
+  resolved "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1202.16.tgz#7eb9c2f9159efa9926fed6920cda0101a7e3de32"
+  integrity sha512-Zhnr0+W1og1rtbfKDPxD7jWDL4cg9LA21ls2pJKXc/h+YXdQ5uj4mFg69npL6aY0yver1w2rIz+Il/wdC/W1gw==
   dependencies:
-    "@angular-devkit/architect" "0.1202.2"
+    "@angular-devkit/architect" "0.1202.16"
     rxjs "6.6.7"
 
-"@angular-devkit/core@12.2.2":
-  version "12.2.2"
-  resolved "https://registry.npmjs.org/@angular-devkit/core/-/core-12.2.2.tgz#48e8f627abf54474b885c75ac8ae48dc076d62cb"
-  integrity sha512-iaPQc0M9FZWvE4MmxRFm5qFNBefvyN7H96pQIIPqT2yalSoiWv1HeQg/OS0WY61lvFPSHnR1n4DZsHCvLdZrFA==
+"@angular-devkit/core@12.2.16":
+  version "12.2.16"
+  resolved "https://registry.npmjs.org/@angular-devkit/core/-/core-12.2.16.tgz#3a833fde6be1997f4961565da8bec1bb58fb8b8d"
+  integrity sha512-cnVtUYSET27B5mRIBp38mpKIX0iHv/hWKiPo74WCGrNwTgwmMHngjgQ4ySn/w1W4s8LL6TDW55ZkRdwyk8TVMQ==
   dependencies:
     ajv "8.6.2"
     ajv-formats "2.1.0"
@@ -122,40 +124,40 @@
     rxjs "6.6.7"
     source-map "0.7.3"
 
-"@angular-devkit/schematics@12.2.2":
-  version "12.2.2"
-  resolved "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-12.2.2.tgz#bfeaddcd161b9491fa30c1f018d0d835a27b8e9a"
-  integrity sha512-KHPxZCSCbVFjaIlBMaxnoA96FnU62HDk8TpWRSnQY2dIkvEUU7+9UmWVodISaQ+MIYur35bFHPJ19im0YkR0tg==
+"@angular-devkit/schematics@12.2.16":
+  version "12.2.16"
+  resolved "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-12.2.16.tgz#b1e9df5bcbb557199256f655bf839d447630ceec"
+  integrity sha512-ToyZBCGilSeeLmhAxmeJ0PykmbKLoME+uK78gC64xJtNu9e3oVnmog8b8g9Ay9hTwZJ96HvNa16po11Gfbbn6A==
   dependencies:
-    "@angular-devkit/core" "12.2.2"
+    "@angular-devkit/core" "12.2.16"
     ora "5.4.1"
     rxjs "6.6.7"
 
-"@angular/animations@12.2.2":
-  version "12.2.2"
-  resolved "https://registry.npmjs.org/@angular/animations/-/animations-12.2.2.tgz#da221d892d8da9056e833da04dced0707720e5ba"
-  integrity sha512-arJzev1GYJYU5cR0x02WFm98ucuPaMuEjAoD+Yggl8Y0usefUm282ZZ+Jl4wCgbhus1JAjpFpoVQuZCVSn6F1g==
+"@angular/animations@12.2.16":
+  version "12.2.16"
+  resolved "https://registry.npmjs.org/@angular/animations/-/animations-12.2.16.tgz#16f1e0d05f026a83847235ff34acc36623830759"
+  integrity sha512-Kf6C7Ta+fCMq5DvT9JNVhBkcECrqFa3wumiC6ssGo5sNaEzXz+tlep9ZgEbqfxSn7gAN7L1DgsbS9u0O6tbUkg==
   dependencies:
     tslib "^2.2.0"
 
-"@angular/cdk@12.2.2":
-  version "12.2.2"
-  resolved "https://registry.npmjs.org/@angular/cdk/-/cdk-12.2.2.tgz#9c804be21e4b1a4c1d4b04ae2f9f09926a057e94"
-  integrity sha512-aF8pfzJYfTeMpc+71nXcSaHOOjAdk+XEKTbTSmOO++OLoOevUoQU0ap0g+H3pfKyO5QCx28IZQxtQHV134QrqA==
+"@angular/cdk@12.2.13":
+  version "12.2.13"
+  resolved "https://registry.npmjs.org/@angular/cdk/-/cdk-12.2.13.tgz#1fdbe814adfd6b4ff906c6d9c4c6df07b83f09d8"
+  integrity sha512-zSKRhECyFqhingIeyRInIyTvYErt4gWo+x5DQr0b7YLUbU8DZSwWnG4w76Ke2s4U8T7ry1jpJBHoX/e8YBpGMg==
   dependencies:
     tslib "^2.2.0"
   optionalDependencies:
     parse5 "^5.0.0"
 
-"@angular/cli@12.2.2":
-  version "12.2.2"
-  resolved "https://registry.npmjs.org/@angular/cli/-/cli-12.2.2.tgz#8900cf22e946a9be667044ba13856e9b065f85a5"
-  integrity sha512-pk+UR8m0paDb1FaED6122JpN3ky+g4c/ccJx7vHZXnXa0/H76cXNoifxkZiGySjxyQyQxUCOuQwgc2cCaX8OPQ==
+"@angular/cli@12.2.16":
+  version "12.2.16"
+  resolved "https://registry.npmjs.org/@angular/cli/-/cli-12.2.16.tgz#2c65c9a7ecb9a8ba8a4e065be40e31d9137e67fa"
+  integrity sha512-4DUi8aHIZWzQycw8SN55ziHhRusHVYJ+2rzsfhy0eDgkry7gYVd4wUn+6Q4D8irywA21soNq1S5JjGAZwRDS6g==
   dependencies:
-    "@angular-devkit/architect" "0.1202.2"
-    "@angular-devkit/core" "12.2.2"
-    "@angular-devkit/schematics" "12.2.2"
-    "@schematics/angular" "12.2.2"
+    "@angular-devkit/architect" "0.1202.16"
+    "@angular-devkit/core" "12.2.16"
+    "@angular-devkit/schematics" "12.2.16"
+    "@schematics/angular" "12.2.16"
     "@yarnpkg/lockfile" "1.1.0"
     ansi-colors "4.1.1"
     debug "4.3.2"
@@ -166,23 +168,23 @@
     npm-pick-manifest "6.1.1"
     open "8.2.1"
     ora "5.4.1"
-    pacote "11.3.5"
+    pacote "12.0.2"
     resolve "1.20.0"
     semver "7.3.5"
     symbol-observable "4.0.0"
     uuid "8.3.2"
 
-"@angular/common@12.2.2":
-  version "12.2.2"
-  resolved "https://registry.npmjs.org/@angular/common/-/common-12.2.2.tgz#ae4736432387018ea29d81a3c73b09106e823276"
-  integrity sha512-cAfPHis8ynpR+qV9ViztCuNBjJ8YRDivvpUXtXecJYYBUPQt9uIiMLeqvBuWmFr+zKD+yAhWywbHEo/4m1JVtQ==
+"@angular/common@12.2.16":
+  version "12.2.16"
+  resolved "https://registry.npmjs.org/@angular/common/-/common-12.2.16.tgz#dff620b1c81903059685be7e8887e9c720d53195"
+  integrity sha512-FEqTXTEsnbDInqV1yFlm97Tz1OFqZS5t0TUkm8gzXRgpIce/F/jLwAg0u1VQkgOsno6cNm0xTWPoZgu85NI4ug==
   dependencies:
     tslib "^2.2.0"
 
-"@angular/compiler-cli@12.2.2":
-  version "12.2.2"
-  resolved "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-12.2.2.tgz#dbc15b3eb3a3e0a41a33099b30010b5e31050c61"
-  integrity sha512-n4X7nE7NEJm8QfKUkPgTXBqyF66FnLtFhjsTnqqSi9u1CdqpBLY7mJkWvQuYob4QfRoKoi2+UxNhoi26fvngCw==
+"@angular/compiler-cli@12.2.16":
+  version "12.2.16"
+  resolved "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-12.2.16.tgz#6d7f20081a5c7c7ac82ac4a65bd14cad3480bdb9"
+  integrity sha512-tlalh8SJvdCWbUPRUR5GamaP+wSc/GuCsoUZpSbcczGKgSlbaEVXUYtVXm8/wuT6Slk2sSEbRs7tXGF2i7qxVw==
   dependencies:
     "@babel/core" "^7.8.6"
     "@babel/types" "^7.8.6"
@@ -199,10 +201,10 @@
     tslib "^2.2.0"
     yargs "^17.0.0"
 
-"@angular/compiler@12.2.2":
-  version "12.2.2"
-  resolved "https://registry.npmjs.org/@angular/compiler/-/compiler-12.2.2.tgz#317b946568f934628ed566bbbf4eddb68c75c450"
-  integrity sha512-4RFFfpAfsT9/xSHRlp1flNAG1dj8WFgTBYb+wu496PziorTBRx/0jsLjxhz547ty6Bn1WZNwQbqBHzx67ehJBg==
+"@angular/compiler@12.2.16":
+  version "12.2.16"
+  resolved "https://registry.npmjs.org/@angular/compiler/-/compiler-12.2.16.tgz#1aa9b3fbd3fe900118ab371d30c090fbc137a15f"
+  integrity sha512-nsYEw+yu8QyeqPf9nAmG419i1mtGM4v8+U+S3eQHQFXTgJzLymMykWHYu2ETdjUpNSLK6xcIQDBWtWnWSfJjAA==
   dependencies:
     tslib "^2.2.0"
 
@@ -211,10 +213,10 @@
   resolved "https://registry.npmjs.org/@angular/compiler/-/compiler-9.0.0.tgz#87e0bef4c369b6cadae07e3a4295778fc93799d5"
   integrity sha512-ctjwuntPfZZT2mNj2NDIVu51t9cvbhl/16epc5xEwyzyDt76pX9UgwvY+MbXrf/C/FWwdtmNtfP698BKI+9leQ==
 
-"@angular/core@12.2.2":
-  version "12.2.2"
-  resolved "https://registry.npmjs.org/@angular/core/-/core-12.2.2.tgz#bf10931a059ee9e95ecbc3171303f2304958a601"
-  integrity sha512-zNgH1iFB1vCVNk9PZ+GCo0sZXD19Zt3BobgmHkWJ+PVXRPuKpuLBXWsq7d9IXdbFopQQWWfVHo0eDagIicrSFQ==
+"@angular/core@12.2.16":
+  version "12.2.16"
+  resolved "https://registry.npmjs.org/@angular/core/-/core-12.2.16.tgz#ec1dd07526ab2b8f808d21e8d5ab50f1a06c6aa8"
+  integrity sha512-jsmvaRdAfng99z2a9mAmkfcsCE1wm+tBYVDxnc5JquSXznwtncjzcoc2X0J0dzrkCDvzFfpTsZ9vehylytBc+A==
   dependencies:
     tslib "^2.2.0"
 
@@ -223,36 +225,36 @@
   resolved "https://registry.npmjs.org/@angular/core/-/core-9.0.0.tgz#227dc53e1ac81824f998c6e76000b7efc522641e"
   integrity sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w==
 
-"@angular/forms@12.2.2":
-  version "12.2.2"
-  resolved "https://registry.npmjs.org/@angular/forms/-/forms-12.2.2.tgz#278aea61ed59f1ddba13a1e4f1d0fc25c4f0b290"
-  integrity sha512-v0zYUdbL+odeDWJNYGq9KZ3535+esDuPaPjXkZkq05/DPCMZym35hx6RlFWn5DElSSfxn4n15mfZXaIWbJNbEQ==
+"@angular/forms@12.2.16":
+  version "12.2.16"
+  resolved "https://registry.npmjs.org/@angular/forms/-/forms-12.2.16.tgz#7c3395a558a89b509fbde0b891d93c1dd1840220"
+  integrity sha512-sb+gpNun5aN7CZfHXS6X7vJcd/0A1P/gRBZpYtQTzBYnqEFCOFIvR62eb05aHQ4JhgKaSPpIXrbz/bAwY/njZw==
   dependencies:
     tslib "^2.2.0"
 
-"@angular/language-service@12.2.2":
-  version "12.2.2"
-  resolved "https://registry.npmjs.org/@angular/language-service/-/language-service-12.2.2.tgz#cbe4138adffc0ef49554259dde7a1ac441463925"
-  integrity sha512-7gsAxLMg1WVncWXV8lvJMDrUrz7bE5eszBrWNTXTTNXzjTeggWfyMOYuyFHcPYkFffIFTLuVpHjulzzRmmscJQ==
+"@angular/language-service@12.2.16":
+  version "12.2.16"
+  resolved "https://registry.npmjs.org/@angular/language-service/-/language-service-12.2.16.tgz#f470a7884acec6080489300079feb6c316a01e00"
+  integrity sha512-eDOd46Lu+4Nc/UA9q4G1xUTeIT2JXDdpedSRCk1fM+trYUZm7Xy2FZasP3pUSdtz04wt0kV9Mi5i3oCxfqU2Wg==
 
-"@angular/platform-browser-dynamic@12.2.2":
-  version "12.2.2"
-  resolved "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.2.2.tgz#ad335fb890785507c91e98e0fa9eef206596c39f"
-  integrity sha512-Ig0gyntnO9nt7ZLkRhDpdyqKH2kgza1i7L5fxtyw72JdPaUcgPSPvL06GST/ak4WQ04hEb28IEYQGqLKCOUvEA==
+"@angular/platform-browser-dynamic@12.2.16":
+  version "12.2.16"
+  resolved "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.2.16.tgz#06adbf7a4dd3dfbc0baff5ed905ca4de70fb91cb"
+  integrity sha512-XGxoACAMW/bc3atiVRpaiYwU4LkobYwVzwlxTT/BxOfsdt8ILb5wU8Fx1TMKNECOQHSGdK0qqhch4pTBZ3cb2g==
   dependencies:
     tslib "^2.2.0"
 
-"@angular/platform-browser@12.2.2":
-  version "12.2.2"
-  resolved "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.2.tgz#e967454d83836f5de03e5bb85f064b6cdee712dd"
-  integrity sha512-uI/tBCzGl7ifQZB7euidO4OOY4qz2jrlMH2Ri6nVuXlLFl4/39ekq75xbJtIQ9/Nf4sWYpUytkq1oW820ZOtcA==
+"@angular/platform-browser@12.2.16":
+  version "12.2.16"
+  resolved "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.16.tgz#129143eb12624d83ac89b54dbe461783bc0508e1"
+  integrity sha512-T855ppLeQO6hRHi7lGf5fwPoUVt+c0h2rgkV5jHElc3ylaGnhecmZc6fnWLX4pw82TMJUgUV88CY8JCFabJWwg==
   dependencies:
     tslib "^2.2.0"
 
-"@angular/router@12.2.2":
-  version "12.2.2"
-  resolved "https://registry.npmjs.org/@angular/router/-/router-12.2.2.tgz#c837d86f042fcf58a448017ce880fbb5b602b481"
-  integrity sha512-zG6VtWqdPBUJq5JlZIJM4CegcPN7FE2s/I0tIhtzMO2lr65+V6X+RVWUXhDHnKR8dBmten+XZpLBYb1ZNhUUUw==
+"@angular/router@12.2.16":
+  version "12.2.16"
+  resolved "https://registry.npmjs.org/@angular/router/-/router-12.2.16.tgz#e1bd5e2cd714824208ed597e731cc1f01d5e53bd"
+  integrity sha512-LuFXSMIvX/VrB4jbYhigG2Y2pGQ9ULsSBUwDWwQCf4kr0eVI37LBJ2Vr74GBEznjgQ0UmWE89E+XYI80UhERTw==
   dependencies:
     tslib "^2.2.0"
 
@@ -3276,10 +3278,10 @@
   dependencies:
     tslib "^2.0.0"
 
-"@ngtools/webpack@12.2.2":
-  version "12.2.2"
-  resolved "https://registry.npmjs.org/@ngtools/webpack/-/webpack-12.2.2.tgz#485098f90b88fc28f5b788d69aaa3e9263e405e5"
-  integrity sha512-GmzdsYtnuTDVZlUmWteT752K54JohjeID/o03Tau/BlnBukzh2m817z57bZS1nkSD2cPD51lg9oeRbZTkkd9LA==
+"@ngtools/webpack@12.2.16":
+  version "12.2.16"
+  resolved "https://registry.npmjs.org/@ngtools/webpack/-/webpack-12.2.16.tgz#262fe1f8e75453ff6bcb01693af4838737ce45f3"
+  integrity sha512-Y2wYX0ybpTYCuSXrG7+3FAtL4dSa7D1vMxymeJEmPTf5QcFTbllwcGQ82Q9lzTn3UDxvt6ZqAYfmWZHGp2GQ9w==
 
 "@ngx-translate/core@^13.0.0":
   version "13.0.0"
@@ -3373,6 +3375,16 @@
     node-gyp "^7.1.0"
     read-package-json-fast "^2.0.1"
 
+"@npmcli/run-script@^2.0.0":
+  version "2.0.0"
+  resolved "https://registry.npmjs.org/@npmcli/run-script/-/run-script-2.0.0.tgz#9949c0cab415b17aaac279646db4f027d6f1e743"
+  integrity sha512-fSan/Pu11xS/TdaTpTB0MRn9guwGU8dye+x56mEVgBEd/QsybBbYcAL0phPXi8SGWFEChkQd6M9qL4y6VOpFig==
+  dependencies:
+    "@npmcli/node-gyp" "^1.0.2"
+    "@npmcli/promise-spawn" "^1.3.2"
+    node-gyp "^8.2.0"
+    read-package-json-fast "^2.0.1"
+
 "@nuxtjs/opencollective@0.3.2":
   version "0.3.2"
   resolved "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz#620ce1044f7ac77185e825e1936115bb38e2681c"
@@ -3626,13 +3638,13 @@
   dependencies:
     any-observable "^0.3.0"
 
-"@schematics/angular@12.2.2":
-  version "12.2.2"
-  resolved "https://registry.npmjs.org/@schematics/angular/-/angular-12.2.2.tgz#3eaa470b1adf639fc17034969298777021930551"
-  integrity sha512-Nqw9rHOTUIzhCxAgj/J1S9C7YLhrsbLbEKJ8gVy6Aakj4jdJBJ9oqPCLnVpP+48k8hSyIZ6TA5X9eVmrUhDDWQ==
+"@schematics/angular@12.2.16":
+  version "12.2.16"
+  resolved "https://registry.npmjs.org/@schematics/angular/-/angular-12.2.16.tgz#0d0684f00ca704ec35eb132db3c6042754b0de0a"
+  integrity sha512-EITPMaRE7iCosf0nyZFOpxTDAiPD3qm4QUxHKcwIaJTrzi89nBoUubw8+pFy5/Gtpadww80YD8ODV64B1bPGMA==
   dependencies:
-    "@angular-devkit/core" "12.2.2"
-    "@angular-devkit/schematics" "12.2.2"
+    "@angular-devkit/core" "12.2.16"
+    "@angular-devkit/schematics" "12.2.16"
     jsonc-parser "3.0.0"
 
 "@sindresorhus/is@^0.14.0":
@@ -4894,10 +4906,10 @@ ansi-gray@^0.1.1:
   dependencies:
     ansi-wrap "0.1.0"
 
-ansi-html@0.0.7:
-  version "0.0.7"
-  resolved "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e"
-  integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4=
+ansi-html-community@0.0.8:
+  version "0.0.8"
+  resolved "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41"
+  integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==
 
 ansi-regex@^2.0.0:
   version "2.1.1"
@@ -4919,6 +4931,11 @@ ansi-regex@^5.0.0:
   resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
   integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
 
+ansi-regex@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+  integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
 ansi-styles@^2.2.1:
   version "2.2.1"
   resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
@@ -5219,7 +5236,7 @@ aproba@^1.0.3:
   resolved "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
   integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
 
-aproba@^2.0.0:
+"aproba@^1.0.3 || ^2.0.0", aproba@^2.0.0:
   version "2.0.0"
   resolved "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
   integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==
@@ -5229,6 +5246,14 @@ archy@^1.0.0:
   resolved "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40"
   integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=
 
+are-we-there-yet@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz#ba20bd6b553e31d62fc8c31bd23d22b95734390d"
+  integrity sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw==
+  dependencies:
+    delegates "^1.0.0"
+    readable-stream "^3.6.0"
+
 are-we-there-yet@~1.1.2:
   version "1.1.5"
   resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
@@ -6666,7 +6691,7 @@ color-string@^1.6.0:
     color-name "^1.0.0"
     simple-swizzle "^0.2.2"
 
-color-support@^1.1.3:
+color-support@^1.1.2, color-support@^1.1.3:
   version "1.1.3"
   resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
   integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
@@ -6865,7 +6890,7 @@ consola@^2.15.0:
   resolved "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550"
   integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==
 
-console-control-strings@^1.0.0, console-control-strings@~1.1.0:
+console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control-strings@~1.1.0:
   version "1.1.0"
   resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
   integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
@@ -7139,15 +7164,16 @@ crelt@^1.0.0:
   resolved "https://registry.npmjs.org/crelt/-/crelt-1.0.5.tgz#57c0d52af8c859e354bace1883eb2e1eb182bb94"
   integrity sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==
 
-critters@0.0.10:
-  version "0.0.10"
-  resolved "https://registry.npmjs.org/critters/-/critters-0.0.10.tgz#edd0e962fc5af6c4adb6dbf1a71bae2d3f917000"
-  integrity sha512-p5VKhP1803+f+0Jq5P03w1SbiHtpAKm+1EpJHkiPxQPq0Vu9QLZHviJ02GRrWi0dlcJqrmzMWInbwp4d22RsGw==
+critters@0.0.12:
+  version "0.0.12"
+  resolved "https://registry.npmjs.org/critters/-/critters-0.0.12.tgz#32baa87526e053a41b67e19921673ed92264e2ab"
+  integrity sha512-ujxKtKc/mWpjrOKeaACTaQ1aP0O31M0ZPWhfl85jZF1smPU4Ivb9va5Ox2poif4zVJQQo0LCFlzGtEZAsCAPcw==
   dependencies:
     chalk "^4.1.0"
-    css "^3.0.0"
+    css-select "^4.1.3"
     parse5 "^6.0.1"
     parse5-htmlparser2-tree-adapter "^6.0.1"
+    postcss "^8.3.7"
     pretty-bytes "^5.3.0"
 
 cron-parser@^2.7.3:
@@ -7310,15 +7336,6 @@ css@^2.0.0:
     source-map-resolve "^0.5.2"
     urix "^0.1.0"
 
-css@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.npmjs.org/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d"
-  integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==
-  dependencies:
-    inherits "^2.0.4"
-    source-map "^0.6.1"
-    source-map-resolve "^0.6.0"
-
 cssauron@^1.4.0:
   version "1.4.0"
   resolved "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz#a6602dff7e04a8306dc0db9a551e92e8b5662ad8"
@@ -8297,15 +8314,118 @@ es6-weak-map@^2.0.1:
     es6-iterator "^2.0.3"
     es6-symbol "^3.1.1"
 
-esbuild@0.12.17:
-  version "0.12.17"
-  resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.12.17.tgz#5816f905c2905de0ebbc658860df7b5b48afbcd3"
-  integrity sha512-GshKJyVYUnlSXIZj/NheC2O0Kblh42CS7P1wJyTbbIHevTG4jYMS9NNw8EOd8dDWD0dzydYHS01MpZoUcQXB4g==
-
-esbuild@^0.12.15:
-  version "0.12.22"
-  resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.12.22.tgz#6031a1257b8d0307d306bed673b79c3668607f51"
-  integrity sha512-yWCr9RoFehpqoe/+MwZXJpYOEIt7KOEvNnjIeMZpMSyQt+KCBASM3y7yViiN5dJRphf1wGdUz1+M4rTtWd/ulA==
+esbuild-android-arm64@0.13.8:
+  version "0.13.8"
+  resolved "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.8.tgz#c20e875c3c98164b1ffba9b28637bdf96f5e9e7c"
+  integrity sha512-AilbChndywpk7CdKkNSZ9klxl+9MboLctXd9LwLo3b0dawmOF/i/t2U5d8LM6SbT1Xw36F8yngSUPrd8yPs2RA==
+
+esbuild-darwin-64@0.13.8:
+  version "0.13.8"
+  resolved "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.8.tgz#f46e6b471ddbf62265234808a6a1aa91df18a417"
+  integrity sha512-b6sdiT84zV5LVaoF+UoMVGJzR/iE2vNUfUDfFQGrm4LBwM/PWXweKpuu6RD9mcyCq18cLxkP6w/LD/w9DtX3ng==
+
+esbuild-darwin-arm64@0.13.8:
+  version "0.13.8"
+  resolved "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.8.tgz#a991157a6013facd4f2e14159b7da52626c90154"
+  integrity sha512-R8YuPiiJayuJJRUBG4H0VwkEKo6AvhJs2m7Tl0JaIer3u1FHHXwGhMxjJDmK+kXwTFPriSysPvcobXC/UrrZCQ==
+
+esbuild-freebsd-64@0.13.8:
+  version "0.13.8"
+  resolved "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.8.tgz#301601d2e443ad458960e359b402a17d9500be9d"
+  integrity sha512-zBn6urrn8FnKC+YSgDxdof9jhPCeU8kR/qaamlV4gI8R3KUaUK162WYM7UyFVAlj9N0MyD3AtB+hltzu4cysTw==
+
+esbuild-freebsd-arm64@0.13.8:
+  version "0.13.8"
+  resolved "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.8.tgz#039a63acc12ec0892006c147ea221e55f9125a9f"
+  integrity sha512-pWW2slN7lGlkx0MOEBoUGwRX5UgSCLq3dy2c8RIOpiHtA87xAUpDBvZK10MykbT+aMfXc0NI2lu1X+6kI34xng==
+
+esbuild-linux-32@0.13.8:
+  version "0.13.8"
+  resolved "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.8.tgz#c537b67d7e694b60bfa2786581412838c6ba0284"
+  integrity sha512-T0I0ueeKVO/Is0CAeSEOG9s2jeNNb8jrrMwG9QBIm3UU18MRB60ERgkS2uV3fZ1vP2F8i3Z2e3Zju4lg9dhVmw==
+
+esbuild-linux-64@0.13.8:
+  version "0.13.8"
+  resolved "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.8.tgz#0092fc8a064001a777bfa0e3b425bb8be8f96e6a"
+  integrity sha512-Bm8SYmFtvfDCIu9sjKppFXzRXn2BVpuCinU1ChTuMtdKI/7aPpXIrkqBNOgPTOQO9AylJJc1Zw6EvtKORhn64w==
+
+esbuild-linux-arm64@0.13.8:
+  version "0.13.8"
+  resolved "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.8.tgz#5cd3f2bb924212971482e8dbc25c4afd09b28110"
+  integrity sha512-X4pWZ+SL+FJ09chWFgRNO3F+YtvAQRcWh0uxKqZSWKiWodAB20flsW/OWFYLXBKiVCTeoGMvENZS/GeVac7+tQ==
+
+esbuild-linux-arm@0.13.8:
+  version "0.13.8"
+  resolved "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.8.tgz#ad634f96bf2975536907aeb9fdb75a3194f4ddce"
+  integrity sha512-4/HfcC40LJ4GPyboHA+db0jpFarTB628D1ifU+/5bunIgY+t6mHkJWyxWxAAE8wl/ZIuRYB9RJFdYpu1AXGPdg==
+
+esbuild-linux-mips64le@0.13.8:
+  version "0.13.8"
+  resolved "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.8.tgz#57857edfebf9bf65766dc8be1637f2179c990572"
+  integrity sha512-o7e0D+sqHKT31v+mwFircJFjwSKVd2nbkHEn4l9xQ1hLR+Bv8rnt3HqlblY3+sBdlrOTGSwz0ReROlKUMJyldA==
+
+esbuild-linux-ppc64le@0.13.8:
+  version "0.13.8"
+  resolved "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.8.tgz#fdb82a059a5b86bb10fb42091b4ebcf488b9cd46"
+  integrity sha512-eZSQ0ERsWkukJp2px/UWJHVNuy0lMoz/HZcRWAbB6reoaBw7S9vMzYNUnflfL3XA6WDs+dZn3ekHE4Y2uWLGig==
+
+esbuild-netbsd-64@0.13.8:
+  version "0.13.8"
+  resolved "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.8.tgz#d7879e7123d3b2c04754ece8bd061aa6866deeff"
+  integrity sha512-gZX4kP7gVvOrvX0ZwgHmbuHczQUwqYppxqtoyC7VNd80t5nBHOFXVhWo2Ad/Lms0E8b+wwgI/WjZFTCpUHOg9Q==
+
+esbuild-openbsd-64@0.13.8:
+  version "0.13.8"
+  resolved "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.8.tgz#88b280b6cb0a3f6adb60abf27fc506c506a35cf0"
+  integrity sha512-afzza308X4WmcebexbTzAgfEWt9MUkdTvwIa8xOu4CM2qGbl2LanqEl8/LUs8jh6Gqw6WsicEK52GPrS9wvkcw==
+
+esbuild-sunos-64@0.13.8:
+  version "0.13.8"
+  resolved "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.8.tgz#229ae7c7703196a58acd0f0291ad9bebda815d63"
+  integrity sha512-mWPZibmBbuMKD+LDN23LGcOZ2EawMYBONMXXHmbuxeT0XxCNwadbCVwUQ/2p5Dp5Kvf6mhrlIffcnWOiCBpiVw==
+
+esbuild-wasm@0.13.8:
+  version "0.13.8"
+  resolved "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.13.8.tgz#f34134c187ffcfc22d476e925917f70bab40f8b0"
+  integrity sha512-UbD+3nloiSpJWXTCInZQrqPe8Y+RLfDkY/5kEHiXsw/lmaEvibe69qTzQu16m5R9je/0bF7VYQ5jaEOq0z9lLA==
+
+esbuild-windows-32@0.13.8:
+  version "0.13.8"
+  resolved "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.8.tgz#892d093e32a21c0c9135e5a0ffdc380aeb70e763"
+  integrity sha512-QsZ1HnWIcnIEApETZWw8HlOhDSWqdZX2SylU7IzGxOYyVcX7QI06ety/aDcn437mwyO7Ph4RrbhB+2ntM8kX8A==
+
+esbuild-windows-64@0.13.8:
+  version "0.13.8"
+  resolved "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.8.tgz#7defd8d79ae3bb7e6f53b65a7190be7daf901686"
+  integrity sha512-76Fb57B9eE/JmJi1QmUW0tRLQZfGo0it+JeYoCDTSlbTn7LV44ecOHIMJSSgZADUtRMWT9z0Kz186bnaB3amSg==
+
+esbuild-windows-arm64@0.13.8:
+  version "0.13.8"
+  resolved "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.8.tgz#e59ae004496fd8a5ab67bfc7945a2e47480d6fb9"
+  integrity sha512-HW6Mtq5eTudllxY2YgT62MrVcn7oq2o8TAoAvDUhyiEmRmDY8tPwAhb1vxw5/cdkbukM3KdMYtksnUhF/ekWeg==
+
+esbuild@0.13.8:
+  version "0.13.8"
+  resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.13.8.tgz#bd7cc51b881ab067789f88e17baca74724c1ec4f"
+  integrity sha512-A4af7G7YZLfG5OnARJRMtlpEsCkq/zHZQXewgPA864l9D6VjjbH1SuFYK/OSV6BtHwDGkdwyRrX0qQFLnMfUcw==
+  optionalDependencies:
+    esbuild-android-arm64 "0.13.8"
+    esbuild-darwin-64 "0.13.8"
+    esbuild-darwin-arm64 "0.13.8"
+    esbuild-freebsd-64 "0.13.8"
+    esbuild-freebsd-arm64 "0.13.8"
+    esbuild-linux-32 "0.13.8"
+    esbuild-linux-64 "0.13.8"
+    esbuild-linux-arm "0.13.8"
+    esbuild-linux-arm64 "0.13.8"
+    esbuild-linux-mips64le "0.13.8"
+    esbuild-linux-ppc64le "0.13.8"
+    esbuild-netbsd-64 "0.13.8"
+    esbuild-openbsd-64 "0.13.8"
+    esbuild-sunos-64 "0.13.8"
+    esbuild-windows-32 "0.13.8"
+    esbuild-windows-64 "0.13.8"
+    esbuild-windows-arm64 "0.13.8"
 
 escalade@^3.1.1:
   version "3.1.1"
@@ -9141,6 +9261,21 @@ function-bind@^1.1.1:
   resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
   integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
 
+gauge@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.npmjs.org/gauge/-/gauge-4.0.1.tgz#82984bc08c90357d60b0a46c03a296beb1affec4"
+  integrity sha512-zJ4jePUHR8cceduZ53b6temRalyGpkC2Kc2r3ecNphmL+uWNoJ3YcOcUjpbG6WwoE/Ef6/+aEZz63neI2WIa1Q==
+  dependencies:
+    ansi-regex "^5.0.1"
+    aproba "^1.0.3 || ^2.0.0"
+    color-support "^1.1.2"
+    console-control-strings "^1.0.0"
+    has-unicode "^2.0.1"
+    signal-exit "^3.0.0"
+    string-width "^4.2.3"
+    strip-ansi "^6.0.1"
+    wide-align "^1.1.2"
+
 gauge@~2.7.3:
   version "2.7.4"
   resolved "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
@@ -12613,6 +12748,28 @@ make-fetch-happen@^9.0.1:
     socks-proxy-agent "^6.0.0"
     ssri "^8.0.0"
 
+make-fetch-happen@^9.1.0:
+  version "9.1.0"
+  resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968"
+  integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==
+  dependencies:
+    agentkeepalive "^4.1.3"
+    cacache "^15.2.0"
+    http-cache-semantics "^4.1.0"
+    http-proxy-agent "^4.0.1"
+    https-proxy-agent "^5.0.0"
+    is-lambda "^1.0.1"
+    lru-cache "^6.0.0"
+    minipass "^3.1.3"
+    minipass-collect "^1.0.2"
+    minipass-fetch "^1.3.2"
+    minipass-flush "^1.0.5"
+    minipass-pipeline "^1.2.4"
+    negotiator "^0.6.2"
+    promise-retry "^2.0.1"
+    socks-proxy-agent "^6.0.0"
+    ssri "^8.0.0"
+
 make-iterator@^1.0.0:
   version "1.0.1"
   resolved "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6"
@@ -12875,12 +13032,12 @@ min-indent@^1.0.0:
   resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
   integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
 
-mini-css-extract-plugin@2.1.0:
-  version "2.1.0"
-  resolved "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.1.0.tgz#4aa6558b527ad4c168fee4a20b6092ebe9f98309"
-  integrity sha512-SV1GgjMcfqy6hW07rAniUbQE4qS3inh3v4rZEUySkPRWy3vMbS3jUCjMOvNI4lUnDlQYJEmuUqKktTCNY5koFQ==
+mini-css-extract-plugin@2.4.2:
+  version "2.4.2"
+  resolved "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.2.tgz#b3508191ea479388a4715018c99dd3e6dd40d2d2"
+  integrity sha512-ZmqShkn79D36uerdED+9qdo1ZYG8C1YsWvXu0UMJxurZnSdgz7gQKO2EGv8T55MhDqG3DYmGtizZNpM/UbTlcA==
   dependencies:
-    schema-utils "^3.0.0"
+    schema-utils "^3.1.0"
 
 minimalistic-assert@^1.0.0:
   version "1.0.1"
@@ -13470,6 +13627,11 @@ nanoid@^3.1.23:
   resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152"
   integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==
 
+nanoid@^3.2.0:
+  version "3.3.1"
+  resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35"
+  integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==
+
 nanomatch@^1.2.9:
   version "1.2.13"
   resolved "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
@@ -13521,10 +13683,10 @@ next-tick@~1.0.0:
   resolved "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
   integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
 
-ng-packagr@^12.2.0:
-  version "12.2.0"
-  resolved "https://registry.npmjs.org/ng-packagr/-/ng-packagr-12.2.0.tgz#53fe47391b5ddaf5f2c24eaecb23d8a10235d887"
-  integrity sha512-M/qq78Gb4q13t6SFX70W2DrPxyooSkLwXzhWozjD8yWGihx4q+54a72ODGx7jIrB4fQgrGDcMUTM7t1zGYir8Q==
+ng-packagr@12.2.7:
+  version "12.2.7"
+  resolved "https://registry.npmjs.org/ng-packagr/-/ng-packagr-12.2.7.tgz#620684f79c16e46800d60918a6dca4745112c716"
+  integrity sha512-ccNeboOdLGOmqk3hvN/4tUO+e7VXZM5f/uj4SYVRtaLT9DuOR63Ln4kn4NOhlxkcmVgqJM8Iwd3M1hSn215nSg==
   dependencies:
     "@rollup/plugin-commonjs" "^20.0.0"
     "@rollup/plugin-json" "^4.1.0"
@@ -13536,7 +13698,7 @@ ng-packagr@^12.2.0:
     chokidar "^3.5.1"
     commander "^8.0.0"
     dependency-graph "^0.11.0"
-    esbuild "^0.12.15"
+    esbuild-wasm "0.13.8"
     find-cache-dir "^3.3.1"
     glob "^7.1.6"
     injection-js "^2.4.0"
@@ -13552,6 +13714,8 @@ ng-packagr@^12.2.0:
     rxjs "^6.5.0"
     sass "^1.32.8"
     stylus "^0.54.8"
+  optionalDependencies:
+    esbuild "0.13.8"
 
 ngx-pagination@^5.0.0:
   version "5.1.1"
@@ -13686,6 +13850,22 @@ node-gyp@^7.1.0:
     tar "^6.0.2"
     which "^2.0.2"
 
+node-gyp@^8.2.0:
+  version "8.4.1"
+  resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937"
+  integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==
+  dependencies:
+    env-paths "^2.2.0"
+    glob "^7.1.4"
+    graceful-fs "^4.2.6"
+    make-fetch-happen "^9.1.0"
+    nopt "^5.0.0"
+    npmlog "^6.0.0"
+    rimraf "^3.0.2"
+    semver "^7.3.5"
+    tar "^6.1.2"
+    which "^2.0.2"
+
 node-int64@^0.4.0:
   version "0.4.0"
   resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
@@ -13852,7 +14032,7 @@ npm-package-arg@8.1.5, npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-packa
     semver "^7.3.4"
     validate-npm-package-name "^3.0.0"
 
-npm-packlist@1.1.12, npm-packlist@^1.1.6, npm-packlist@^2.1.4:
+npm-packlist@1.1.12, npm-packlist@^1.1.6, npm-packlist@^2.1.4, npm-packlist@^3.0.0:
   version "1.1.12"
   resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.12.tgz#22bde2ebc12e72ca482abd67afc51eb49377243a"
   integrity sha512-WJKFOVMeAlsU/pjXuqVdzU0WfgtIBCupkEVwn+1Y0ERAbUfWw8R4GjgVbaKnUjRoD2FoQbHOCbOyT5Mbs9Lw4g==
@@ -13920,6 +14100,16 @@ npm-run-path@^4.0.0, npm-run-path@^4.0.1:
     gauge "~2.7.3"
     set-blocking "~2.0.0"
 
+npmlog@^6.0.0:
+  version "6.0.1"
+  resolved "https://registry.npmjs.org/npmlog/-/npmlog-6.0.1.tgz#06f1344a174c06e8de9c6c70834cfba2964bba17"
+  integrity sha512-BTHDvY6nrRHuRfyjt1MAufLxYdVXZfd099H4+i1f0lPywNQyI4foeNXJRObB/uy+TYqUW0vAD9gbdSOXPst7Eg==
+  dependencies:
+    are-we-there-yet "^3.0.0"
+    console-control-strings "^1.1.0"
+    gauge "^4.0.0"
+    set-blocking "^2.0.0"
+
 nth-check@^2.0.0:
   version "2.0.0"
   resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125"
@@ -14356,7 +14546,32 @@ packet-reader@1.0.0:
   resolved "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74"
   integrity sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==
 
-pacote@11.3.5, pacote@^11.2.6:
+pacote@12.0.2:
+  version "12.0.2"
+  resolved "https://registry.npmjs.org/pacote/-/pacote-12.0.2.tgz#14ae30a81fe62ec4fc18c071150e6763e932527c"
+  integrity sha512-Ar3mhjcxhMzk+OVZ8pbnXdb0l8+pimvlsqBGRNkble2NVgyqOGE3yrCGi/lAYq7E7NRDMz89R1Wx5HIMCGgeYg==
+  dependencies:
+    "@npmcli/git" "^2.1.0"
+    "@npmcli/installed-package-contents" "^1.0.6"
+    "@npmcli/promise-spawn" "^1.2.0"
+    "@npmcli/run-script" "^2.0.0"
+    cacache "^15.0.5"
+    chownr "^2.0.0"
+    fs-minipass "^2.1.0"
+    infer-owner "^1.0.4"
+    minipass "^3.1.3"
+    mkdirp "^1.0.3"
+    npm-package-arg "^8.0.1"
+    npm-packlist "^3.0.0"
+    npm-pick-manifest "^6.0.0"
+    npm-registry-fetch "^11.0.0"
+    promise-retry "^2.0.1"
+    read-package-json-fast "^2.0.1"
+    rimraf "^3.0.2"
+    ssri "^8.0.1"
+    tar "^6.1.0"
+
+pacote@^11.2.6:
   version "11.3.5"
   resolved "https://registry.npmjs.org/pacote/-/pacote-11.3.5.tgz#73cf1fc3772b533f575e39efa96c50be8c3dc9d2"
   integrity sha512-fT375Yczn4zi+6Hkk2TBe1x1sP8FgFsEIZ2/iWaXY2r/NkhDJfxbcn5paz1+RTFCyNf+dPnaoBDJoAxXSU8Bkg==
@@ -14699,6 +14914,11 @@ pgpass@1.x:
   dependencies:
     split2 "^3.1.1"
 
+picocolors@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
+  integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+
 picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3:
   version "2.3.0"
   resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
@@ -15375,6 +15595,15 @@ postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.32, postcss@^7.0.
     source-map "^0.6.1"
     supports-color "^6.1.0"
 
+postcss@^8.3.7:
+  version "8.4.6"
+  resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.6.tgz#c5ff3c3c457a23864f32cb45ac9b741498a09ae1"
+  integrity sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==
+  dependencies:
+    nanoid "^3.2.0"
+    picocolors "^1.0.0"
+    source-map-js "^1.0.2"
+
 postgres-array@~2.0.0:
   version "2.0.0"
   resolved "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e"
@@ -17156,6 +17385,11 @@ source-map-js@^0.6.2:
   resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"
   integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==
 
+source-map-js@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
+  integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
+
 source-map-loader@3.0.0:
   version "3.0.0"
   resolved "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.0.tgz#f2a04ee2808ad01c774dea6b7d2639839f3b3049"
@@ -17467,6 +17701,15 @@ string-width@^1.0.1, string-width@^1.0.2:
     is-fullwidth-code-point "^2.0.0"
     strip-ansi "^4.0.0"
 
+"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3:
+  version "4.2.3"
+  resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+  dependencies:
+    emoji-regex "^8.0.0"
+    is-fullwidth-code-point "^3.0.0"
+    strip-ansi "^6.0.1"
+
 string-width@^3.0.0, string-width@^3.1.0:
   version "3.1.0"
   resolved "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
@@ -17557,6 +17800,13 @@ strip-ansi@^6.0.0:
   dependencies:
     ansi-regex "^5.0.0"
 
+strip-ansi@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+  dependencies:
+    ansi-regex "^5.0.1"
+
 strip-bom@^2.0.0:
   version "2.0.0"
   resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
@@ -17831,6 +18081,18 @@ tar@^6.0.2, tar@^6.1.0:
     mkdirp "^1.0.3"
     yallist "^4.0.0"
 
+tar@^6.1.2:
+  version "6.1.11"
+  resolved "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"
+  integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==
+  dependencies:
+    chownr "^2.0.0"
+    fs-minipass "^2.0.0"
+    minipass "^3.0.0"
+    minizlib "^2.1.1"
+    mkdirp "^1.0.3"
+    yallist "^4.0.0"
+
 tcp-port-used@^1.0.1:
   version "1.0.2"
   resolved "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.2.tgz#9652b7436eb1f4cfae111c79b558a25769f6faea"
@@ -18902,12 +19164,12 @@ webpack-dev-middleware@^3.7.2:
     range-parser "^1.2.1"
     webpack-log "^2.0.0"
 
-webpack-dev-server@3.11.2:
-  version "3.11.2"
-  resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.2.tgz#695ebced76a4929f0d5de7fd73fafe185fe33708"
-  integrity sha512-A80BkuHRQfCiNtGBS1EMf2ChTUs0x+B3wGDFmOeT4rmJOHhHTCH2naNxIHhmkr0/UillP4U3yeIyv1pNp+QDLQ==
+webpack-dev-server@3.11.3:
+  version "3.11.3"
+  resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.3.tgz#8c86b9d2812bf135d3c9bce6f07b718e30f7c3d3"
+  integrity sha512-3x31rjbEQWKMNzacUZRE6wXvUFuGpH7vr0lIEbYpMAG9BOxi0928QU1BBswOAP3kg3H1O4hiS+sq4YyAn6ANnA==
   dependencies:
-    ansi-html "0.0.7"
+    ansi-html-community "0.0.8"
     bonjour "^3.5.0"
     chokidar "^2.1.8"
     compression "^1.7.4"
@@ -19116,6 +19378,13 @@ wide-align@^1.1.0:
   dependencies:
     string-width "^1.0.2 || 2"
 
+wide-align@^1.1.2:
+  version "1.1.5"
+  resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3"
+  integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==
+  dependencies:
+    string-width "^1.0.2 || 2 || 3 || 4"
+
 widest-line@^3.1.0:
   version "3.1.0"
   resolved "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca"