diff --git a/.gitignore b/.gitignore index 56add165a..f05ae11c7 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ .vscode/ assets/newrelic.js +output/ coverage/ tests/ platforms diff --git a/.npmrc b/.npmrc new file mode 100644 index 000000000..ea7212de2 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +audit-level=moderate diff --git a/angular.json b/angular.json index a82fddcaa..aeb8814ca 100644 --- a/angular.json +++ b/angular.json @@ -230,7 +230,10 @@ "include": ["projects/v3/src/**/*.spec.ts"], "assets": ["./projects/v3/src/assets/icon/favicon.ico", "projects/v3/src/assets"], "styles": ["projects/v3/src/styles.scss"], - "scripts": [] + "scripts": [], + "watch": false, + "progress": false, + "sourceMap": false } }, "lint": { diff --git a/docs/coverage-run-cookbook.md b/docs/coverage-run-cookbook.md new file mode 100644 index 000000000..279c1290b --- /dev/null +++ b/docs/coverage-run-cookbook.md @@ -0,0 +1,82 @@ +# Coverage Run Cookbook (v3) + +> Fast, repeatable coverage workflow for this repo. + +## Quick Rules + +- Use targeted coverage runs first; use full-suite only after targeted changes pass. +- Use workspace-relative include paths starting with `projects/v3/src/...`. +- Save logs under `./output` for every run. +- Prefer `ChromeHeadless` for CI-like behavior. + +## 1) Targeted Coverage Run (Single Spec) + +```bash +npm test -- --code-coverage --browsers=ChromeHeadless --include='projects/v3/src/app/pages/tabs/tabs.page.spec.ts' > ./output/coverage-targeted-tabs.log 2>&1 +``` + +## 2) Targeted Coverage Run (Multiple Specs) + +```bash +npm test -- --code-coverage --browsers=ChromeHeadless \ + --include='projects/v3/src/app/pages/activity-mobile/activity-mobile.page.spec.ts' \ + --include='projects/v3/src/app/pages/topic-mobile/topic-mobile.page.spec.ts' \ + --include='projects/v3/src/app/pages/tabs/tabs.page.spec.ts' \ + --include='projects/v3/src/app/pages/chat/chat-list/chat-list.component.spec.ts' \ + --include='projects/v3/src/app/pages/chat/chat-preview/chat-preview.component.spec.ts' \ + > ./output/coverage-targeted-mobile-chat-tabs-topic.log 2>&1 +``` + +## 3) Full-Suite Coverage Run + +```bash +npm run test -- --code-coverage --browsers=ChromeHeadless > ./output/full-suite-coverage.log 2>&1 +``` + +## 4) Open HTML Coverage Report + +```bash +open coverage/v3/index.html +``` + +Open folder-level reports directly when needed, for example: + +- `coverage/v3/src/app/pages/activity-mobile/index.html` +- `coverage/v3/src/app/pages/topic-mobile/index.html` +- `coverage/v3/src/app/pages/tabs/index.html` +- `coverage/v3/src/app/pages/chat/chat-list/index.html` + +## 5) Tail Logs Quickly + +```bash +wc -l ./output/full-suite-coverage.log && tail -n 60 ./output/full-suite-coverage.log +``` + +## 6) What Is Noise vs Blocker? + +Usually non-blocking during this repo’s test runs: + +- `NG0303/NG0304` unknown element/property warnings +- Ionic runtime warnings in templates +- SVG/asset `404` warnings in Karma output + +Treat as blockers: + +- TypeScript compile errors +- Karma `ERROR` status +- `Some of your tests did a full page reload!` + +## 7) Prevent Common Flaky Failures + +- Do not assign `window.location.href`/`location` directly in unit tests. +- Spy/mock navigation helpers or Angular Router instead. +- If a component destroys external instances in `ngOnDestroy`, initialize mocks with `destroy` spies in tests. +- Add explicit branch tests for keyboard guards (`Enter`, `Space`, unsupported keys). + +## 8) Suggested Workflow + +1. Run targeted specs for changed files. +2. Fix compile/runtime test failures. +3. Verify folder-level coverage HTML for target modules. +4. Run one full-suite coverage pass. +5. Save/hand over logs from `./output`. diff --git a/docs/unit-testing-guide.md b/docs/unit-testing-guide.md new file mode 100644 index 000000000..be8262762 --- /dev/null +++ b/docs/unit-testing-guide.md @@ -0,0 +1,831 @@ +# Unit Testing Guide for Practera AppV2 + +> Comprehensive guide for debugging, fixing, and maintaining unit tests in the Angular/Ionic 7 application. + +## Table of Contents + +1. [Overview](#overview) +2. [Test Environment Setup](#test-environment-setup) +3. [Common Failure Patterns & Solutions](#common-failure-patterns--solutions) +4. [Mocking Patterns](#mocking-patterns) +5. [Async Testing Patterns](#async-testing-patterns) +6. [Angular Forms Testing](#angular-forms-testing) +7. [Component Testing Best Practices](#component-testing-best-practices) +8. [Service Testing Best Practices](#service-testing-best-practices) +9. [Debugging Strategies](#debugging-strategies) +10. [Code Standards](#code-standards) +11. [Coverage Run Cookbook](#coverage-run-cookbook) + +--- + +## Overview + +This project uses: +- **Test Runner**: Karma with Jasmine +- **Framework**: Angular 17 with Ionic 7 +- **Total Tests**: ~900 tests +- **Test Location**: `*.spec.ts` files alongside source files + +### Running Tests + +```bash +# run all tests +npm test + +# run tests with output logging +npm test 2>&1 | tee test-run.log + +# run specific test file (modify karma.conf.js or use fdescribe/fit) +``` + +### Coverage Run Cookbook + +- Quick copy-paste coverage commands are in [coverage-run-cookbook.md](./coverage-run-cookbook.md). + +--- + +## Test Environment Setup + +### TestBed Configuration Pattern + +```typescript +import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed, fakeAsync, tick, flush } from '@angular/core/testing'; +import { ReactiveFormsModule } from '@angular/forms'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; + +describe('MyComponent', () => { + let component: MyComponent; + let fixture: ComponentFixture; + let serviceSpy: jasmine.SpyObj; + + beforeEach(async () => { + TestBed.configureTestingModule({ + imports: [ReactiveFormsModule, HttpClientTestingModule], + declarations: [MyComponent], + schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA], + providers: [ + { + provide: MyService, + useValue: jasmine.createSpyObj('MyService', ['method1', 'method2']) + } + ] + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(MyComponent); + component = fixture.componentInstance; + serviceSpy = TestBed.inject(MyService) as jasmine.SpyObj; + + // setup default spy return values + serviceSpy.method1.and.returnValue(of(mockData)); + }); +}); +``` + +### Schema Usage + +| Schema | Purpose | +|--------|---------| +| `CUSTOM_ELEMENTS_SCHEMA` | Suppresses errors for unknown custom elements (ionic components, child components) | +| `NO_ERRORS_SCHEMA` | Suppresses all template validation errors | + +**Best Practice**: Use both schemas together to avoid template-related test failures when testing component logic: + +```typescript +schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA] +``` + +--- + +## Common Failure Patterns & Solutions + +### 1. NG01203: No Value Accessor for Form Control + +**Error Message:** +``` +Error: NG01203: No value accessor for form control name: 'q-123' +``` + +**Cause**: Custom elements with `formControlName` directive don't implement `ControlValueAccessor`. + +**Solution**: Create a mock `ControlValueAccessor` directive in the test file: + +```typescript +import { Directive, forwardRef } from '@angular/core'; +import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms'; + +/** + * mock value accessor directive to satisfy formControlName bindings + * on custom elements like app-text, app-oneof, etc. + */ +@Directive({ + selector: '[formControlName]', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MockValueAccessorDirective), + multi: true + } + ] +}) +class MockValueAccessorDirective implements ControlValueAccessor { + writeValue(obj: any): void {} + registerOnChange(fn: any): void {} + registerOnTouched(fn: any): void {} +} + +// add to declarations +TestBed.configureTestingModule({ + declarations: [MyComponent, MockValueAccessorDirective], + // ... +}); +``` + +### 2. Spy Was Never Called + +**Error Message:** +``` +Expected spy someMethod to have been called. +``` + +**Common Causes & Solutions:** + +| Cause | Solution | +|-------|----------| +| Method signature changed | Update spy to match new method name/parameters | +| Async timing issue | Use `fakeAsync`/`tick()` or `async`/`await` | +| Component logic changed | Update test to reflect new implementation | +| Wrong service being called | Check if service was refactored to use different service | + +**Example Fix** (SettingsPage openSupportPopup): +```typescript +// BEFORE: Component used hubspotService.openSupportPopup() +expect(hubspotServiceSpy.openSupportPopup).toHaveBeenCalled(); + +// AFTER: Component now uses notificationsService.modal() +notificationsServiceSpy.modal.and.returnValue(Promise.resolve({ + present: jasmine.createSpy('present').and.returnValue(Promise.resolve()) +})); +// trigger the action +await component.openSupportPopup(); +expect(notificationsServiceSpy.modal).toHaveBeenCalled(); +``` + +### 3. Cannot Read Properties of Undefined + +**Error Message:** +``` +TypeError: Cannot read properties of undefined (reading 'someProperty') +``` + +**Common Causes & Solutions:** + +```typescript +// cause 1: service method not mocked +serviceSpy.getUser.and.returnValue({ id: 1, name: 'Test' }); + +// cause 2: async method needs proper mock +serviceSpy.modal.and.returnValue(Promise.resolve({ + present: jasmine.createSpy('present').and.returnValue(Promise.resolve()) +})); + +// cause 3: @input not initialized +component.btnDisabled$ = new BehaviorSubject(false); +component.savingMessage$ = new BehaviorSubject(''); + +// cause 4: component property not initialized +component.form = { + nativeElement: { + querySelector: jasmine.createSpy('querySelector').and.returnValue({ + classList: { add: jasmine.createSpy('add') } + }) + } +} as any; +``` + +### 4. Timer-Related Failures in fakeAsync + +**Error Message:** +``` +Error: 1 timer(s) still in the queue. +``` + +**Solution**: Use `flush()` to clear all pending timers: + +```typescript +it('should handle timers', fakeAsync(() => { + component.doSomething(); + tick(300); // advance specific time + + // verify expectations + expect(component.result).toBe(true); + + // clear any remaining timers + flush(); +})); +``` + +### 5. Observable Not Completing + +**Error Message:** +``` +Error: Timeout - Async callback was not invoked within 5000ms +``` + +**Solution**: Ensure observables complete or use `take(1)`: + +```typescript +// in test setup +serviceSpy.getData.and.returnValue(of(mockData)); // of() completes immediately + +// for subjects that don't complete +const subject = new BehaviorSubject(mockData); +serviceSpy.data$ = subject.asObservable(); +// later in test +subject.complete(); // or use takeUntil pattern +``` + +### 6. ExpressionChangedAfterItHasBeenCheckedError + +**Error Message:** +``` +Error: ExpressionChangedAfterItHasBeenCheckedError +``` + +**Solution**: Trigger change detection properly: + +```typescript +it('should update view', () => { + component.someProperty = 'new value'; + fixture.detectChanges(); // trigger change detection + + expect(page.element.textContent).toContain('new value'); +}); +``` + +--- + +## Mocking Patterns + +### Service Spies with jasmine.createSpyObj + +```typescript +// basic spy with methods +const serviceSpy = jasmine.createSpyObj('ServiceName', ['method1', 'method2']); + +// spy with properties +const serviceSpy = jasmine.createSpyObj('ServiceName', ['method1'], { + property1: 'value', + observable$: of(mockData) +}); + +// configure return values +serviceSpy.method1.and.returnValue(of(result)); +serviceSpy.method2.and.returnValue(Promise.resolve(result)); +serviceSpy.method3.and.throwError(new Error('test error')); +``` + +### Mock Router + +```typescript +class MockRouter { + navigate = jasmine.createSpy('navigate'); + navigateByUrl = jasmine.createSpy('navigateByUrl'); + events = of(new NavigationEnd(1, '/', '/')); +} + +// in providers +{ provide: Router, useClass: MockRouter } +``` + +### Mock ActivatedRoute + +```typescript +{ + provide: ActivatedRoute, + useValue: { + snapshot: { + paramMap: convertToParamMap({ + id: 1, + activityId: 2 + }), + data: { + action: 'assessment' + } + }, + params: of({ id: 1 }), + queryParams: of({ filter: 'active' }) + } +} +``` + +### Mock Modal Controller + +```typescript +const modalControllerSpy = jasmine.createSpyObj('ModalController', ['create', 'dismiss']); +const mockModal = { + present: jasmine.createSpy('present').and.returnValue(Promise.resolve()), + dismiss: jasmine.createSpy('dismiss').and.returnValue(Promise.resolve()), + onDidDismiss: jasmine.createSpy('onDidDismiss').and.returnValue(Promise.resolve({ data: null })) +}; +modalControllerSpy.create.and.returnValue(Promise.resolve(mockModal)); +``` + +### Mock BehaviorSubject Input + +```typescript +// when component has @Input() that is a BehaviorSubject +beforeEach(() => { + component.btnDisabled$ = new BehaviorSubject(false); + component.savingMessage$ = new BehaviorSubject(''); +}); + +// test value changes +it('should react to input changes', () => { + component.btnDisabled$.next(true); + fixture.detectChanges(); + expect(component.isDisabled).toBe(true); +}); +``` + +--- + +## Async Testing Patterns + +### fakeAsync with tick + +Use for timer-based operations (setTimeout, setInterval, debounce): + +```typescript +it('should handle debounced input', fakeAsync(() => { + component.onSearchChange('test'); + + // advance time past debounce period + tick(300); + + expect(serviceSpy.search).toHaveBeenCalledWith('test'); + + // clear remaining timers + flush(); +})); +``` + +### fakeAsync with flushMicrotasks + +Use for Promise-based operations: + +```typescript +it('should handle promises', fakeAsync(() => { + component.loadData(); + + // resolve all pending promises + flushMicrotasks(); + + expect(component.data).toBeDefined(); +})); +``` + +### async/await Pattern + +Use for straightforward async operations: + +```typescript +it('should load data', async () => { + serviceSpy.getData.and.returnValue(Promise.resolve(mockData)); + + await component.loadData(); + + expect(component.data).toEqual(mockData); +}); +``` + +### Combining fakeAsync with Promises + +```typescript +it('should handle mixed async', fakeAsync(() => { + serviceSpy.modal.and.returnValue(Promise.resolve({ + present: jasmine.createSpy('present').and.returnValue(Promise.resolve()) + })); + + component.openModal(); + + // handle promise resolution + tick(); + + expect(serviceSpy.modal).toHaveBeenCalled(); + flush(); +})); +``` + +--- + +## Angular Forms Testing + +### Pre-creating Form Controls + +When testing components that dynamically add form controls: + +```typescript +it('should handle dynamic form controls', fakeAsync(() => { + // pre-create form controls before triggering ngOnChanges + mockQuestions.forEach(q => { + component.questionsForm.addControl('q-' + q.id, new FormControl(null)); + }); + + component.ngOnChanges({ submission: {} as any }); + tick(350); + + expect(component.questionsForm.valid).toBe(false); + flush(); +})); +``` + +### Testing Form Validation + +```typescript +it('should validate required fields', () => { + component.form.controls['email'].setValue(''); + expect(component.form.controls['email'].valid).toBe(false); + expect(component.form.controls['email'].errors?.['required']).toBe(true); + + component.form.controls['email'].setValue('test@example.com'); + expect(component.form.controls['email'].valid).toBe(true); +}); +``` + +### Testing Form Submission + +```typescript +it('should submit valid form', fakeAsync(() => { + component.form.patchValue({ + email: 'test@example.com', + password: 'password123' + }); + + component.onSubmit(); + tick(); + + expect(serviceSpy.login).toHaveBeenCalledWith({ + email: 'test@example.com', + password: 'password123' + }); +})); +``` + +--- + +## Component Testing Best Practices + +### Page Object Pattern + +Create a Page class to encapsulate DOM queries: + +```typescript +class Page { + get submitButton() { + return this.query('#btn-submit'); + } + + get errorMessage() { + return this.query('.error-message'); + } + + get inputFields() { + return this.queryAll('input'); + } + + fixture: ComponentFixture; + + constructor(fixture: ComponentFixture) { + this.fixture = fixture; + } + + private query(selector: string): T { + return this.fixture.nativeElement.querySelector(selector); + } + + private queryAll(selector: string): T[] { + return this.fixture.nativeElement.querySelectorAll(selector); + } +} + +// usage in tests +let page: Page; + +beforeEach(() => { + fixture = TestBed.createComponent(MyComponent); + page = new Page(fixture); +}); + +it('should disable submit when form invalid', () => { + fixture.detectChanges(); + expect(page.submitButton.disabled).toBe(true); +}); +``` + +### Testing @Input Changes + +```typescript +it('should react to input changes', () => { + component.data = mockData; + component.ngOnChanges({ + data: new SimpleChange(null, mockData, true) + }); + + expect(component.processedData).toBeDefined(); +}); +``` + +### Testing @Output Events + +```typescript +it('should emit event on action', () => { + const emitSpy = spyOn(component.dataChanged, 'emit'); + + component.updateData(newData); + + expect(emitSpy).toHaveBeenCalledWith(newData); +}); +``` + +--- + +## Service Testing Best Practices + +### Testing HTTP Calls + +```typescript +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +describe('DataService', () => { + let service: DataService; + let httpMock: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [DataService] + }); + + service = TestBed.inject(DataService); + httpMock = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + httpMock.verify(); // verify no outstanding requests + }); + + it('should fetch data', () => { + const mockResponse = { id: 1, name: 'Test' }; + + service.getData().subscribe(data => { + expect(data).toEqual(mockResponse); + }); + + const req = httpMock.expectOne('/api/data'); + expect(req.request.method).toBe('GET'); + req.flush(mockResponse); + }); +}); +``` + +### Testing GraphQL with Apollo + +```typescript +import { ApolloTestingModule, ApolloTestingController } from 'apollo-angular/testing'; + +describe('GraphQLService', () => { + let service: GraphQLService; + let apolloController: ApolloTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ApolloTestingModule], + providers: [GraphQLService] + }); + + service = TestBed.inject(GraphQLService); + apolloController = TestBed.inject(ApolloTestingController); + }); + + it('should execute query', () => { + service.getUser(1).subscribe(user => { + expect(user.name).toBe('Test User'); + }); + + const op = apolloController.expectOne('GetUser'); + op.flush({ + data: { + user: { id: 1, name: 'Test User' } + } + }); + }); +}); +``` + +--- + +## Debugging Strategies + +### 1. Isolate Failing Tests + +```typescript +// run only this describe block +fdescribe('MyComponent', () => { ... }); + +// run only this test +fit('should do something', () => { ... }); + +// skip tests temporarily +xdescribe('SkippedSuite', () => { ... }); +xit('skipped test', () => { ... }); +``` + +### 2. Log Output Analysis + +```bash +# run tests with output to file +npm test 2>&1 | tee test-run.log + +# search for failures (accounting for ANSI codes) +grep -A 20 "FAILED" test-run.log + +# count failures +grep -c "FAILED" test-run.log +``` + +### 3. Read Test Logs Systematically + +When logs have ANSI escape codes: +1. Read the log file in chunks using `read_file` tool +2. Look for pattern `(X FAILED)` where X > 0 +3. The test name appears just before the FAILED count increments + +### 4. Console Logging in Tests + +```typescript +it('should process data', () => { + console.log('Input:', component.input); + component.process(); + console.log('Output:', component.output); + expect(component.output).toBeDefined(); +}); +``` + +### 5. Debugging Spy Calls + +```typescript +it('should call service correctly', () => { + component.doSomething(); + + // log all calls made to the spy + console.log('Calls:', serviceSpy.method.calls.all()); + console.log('Call count:', serviceSpy.method.calls.count()); + console.log('First call args:', serviceSpy.method.calls.first()?.args); + + expect(serviceSpy.method).toHaveBeenCalled(); +}); +``` + +### 6. Common Error Patterns to Search + +| Error Pattern | Likely Cause | +|---------------|--------------| +| `NG01203` | Missing ControlValueAccessor | +| `Cannot read properties of undefined` | Unmocked service/property | +| `timer(s) still in the queue` | Missing flush() in fakeAsync | +| `Async callback was not invoked` | Observable not completing | +| `Expected spy X to have been called` | Method renamed or logic changed | +| `ExpressionChangedAfterItHasBeenChecked` | Missing fixture.detectChanges() | + +--- + +## Code Standards + +### Test File Naming + +- Test files must be named `*.spec.ts` +- Place test files alongside source files + +### Test Structure + +```typescript +describe('ComponentName', () => { + // setup + + describe('methodName()', () => { + it('should [expected behavior] when [condition]', () => { + // arrange + // act + // assert + }); + }); +}); +``` + +### Naming Conventions + +- Use descriptive test names that explain the expected behavior +- Start with "should" for consistency +- Include the condition being tested + +```typescript +// good +it('should disable submit button when form is invalid', () => {}); +it('should display error message when login fails', () => {}); + +// avoid +it('test 1', () => {}); +it('works', () => {}); +``` + +### Assertion Best Practices + +```typescript +// prefer specific matchers +expect(value).toBe(true); // for booleans +expect(value).toEqual(expected); // for objects/arrays +expect(value).toContain('text'); // for strings/arrays +expect(value).toBeDefined(); // for existence +expect(value).toBeNull(); // for null checks + +// use toBeTrue()/toBeFalse() for explicit boolean checks +expect(component.isValid).toBeTrue(); +expect(component.hasErrors).toBeFalse(); +``` + +### Cleanup + +Always clean up subscriptions and timers: + +```typescript +afterEach(() => { + // if using fakeAsync + flush(); + + // clean up subscriptions + subscription?.unsubscribe(); +}); +``` + +--- + +## Quick Reference: Fix Checklist + +When fixing failing tests, check these in order: + +1. **Is the service mock configured correctly?** + - All used methods are in the spy object + - Return values are set up before triggering the action + +2. **Are async operations handled?** + - Use `fakeAsync`/`tick()`/`flush()` for timers + - Use `async`/`await` for promises + - Ensure observables complete + +3. **Are form controls set up correctly?** + - Pre-create dynamic form controls + - Use MockValueAccessorDirective for custom elements + +4. **Is change detection triggered?** + - Call `fixture.detectChanges()` after property changes + +5. **Are @Input properties initialized?** + - Set BehaviorSubject inputs in beforeEach + - Initialize required input properties + +6. **Has the component implementation changed?** + - Check if methods were renamed + - Check if different services are now being used + - Update test to match new implementation + +--- + +## Appendix: Common Imports + +```typescript +// angular testing +import { ComponentFixture, TestBed, fakeAsync, tick, flush, flushMicrotasks } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +// angular core +import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, Directive, forwardRef } from '@angular/core'; +import { ReactiveFormsModule, FormGroup, FormControl, NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms'; + +// angular router +import { Router, ActivatedRoute, convertToParamMap } from '@angular/router'; + +// rxjs +import { of, BehaviorSubject, Subject, throwError } from 'rxjs'; + +// project test utilities +import { TestUtils } from '@testingv3/utils'; +import { MockRouter, FastFeedbackServiceMock } from '@testingv3/mocked.service'; +``` + +--- + +*Last updated: December 2025* +*Based on fixing 120 failing tests down to 0 failures* diff --git a/package-lock.json b/package-lock.json index 5fd76e181..c95bb168c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,6 @@ "core-js": "^3.21.1", "dayjs": "^1.11.10", "exif-js": "^2.3.0", - "filestack-js": "^3.30.0", "franc-min": "^6.2.0", "graphql": "^16.8.1", "ics": "^3.7.2", @@ -116,11 +115,13 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1902.22", + "version": "0.1902.23", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1902.23.tgz", + "integrity": "sha512-P1qFNzIYR0Yr5FGbG961rdYcKh9A2HRUw7WzZlXWqZ7N6wHi2f+QkW/lwZcbYGceWrJuQrOsx61lNA0A56Kyyw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.22", + "@angular-devkit/core": "19.2.23", "rxjs": "7.8.1" }, "engines": { @@ -138,15 +139,17 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "19.2.22", + "version": "19.2.23", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-19.2.23.tgz", + "integrity": "sha512-LM2VmANAOCVeTavhLjdAMMx7oZnqozE4GqWSxxaXAO6XkcW/2F5kCZa7WrM5aYM0ciopz0SV+o82t2ns3hkcmA==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1902.22", - "@angular-devkit/build-webpack": "0.1902.22", - "@angular-devkit/core": "19.2.22", - "@angular/build": "19.2.22", + "@angular-devkit/architect": "0.1902.23", + "@angular-devkit/build-webpack": "0.1902.23", + "@angular-devkit/core": "19.2.23", + "@angular/build": "19.2.23", "@babel/core": "7.26.10", "@babel/generator": "7.26.10", "@babel/helper-annotate-as-pure": "7.25.9", @@ -157,7 +160,7 @@ "@babel/preset-env": "7.26.9", "@babel/runtime": "7.26.10", "@discoveryjs/json-ext": "0.6.3", - "@ngtools/webpack": "19.2.22", + "@ngtools/webpack": "19.2.23", "@vitejs/plugin-basic-ssl": "1.2.0", "ansi-colors": "4.1.3", "autoprefixer": "10.4.20", @@ -178,7 +181,7 @@ "mini-css-extract-plugin": "2.9.2", "open": "10.1.0", "ora": "5.4.1", - "picomatch": "4.0.2", + "picomatch": "4.0.4", "piscina": "4.8.0", "postcss": "8.5.2", "postcss-loader": "8.1.1", @@ -211,7 +214,7 @@ "@angular/localize": "^19.0.0 || ^19.2.0-next.0", "@angular/platform-server": "^19.0.0 || ^19.2.0-next.0", "@angular/service-worker": "^19.0.0 || ^19.2.0-next.0", - "@angular/ssr": "^19.2.22", + "@angular/ssr": "^19.2.23", "@web/test-runner": "^0.20.0", "browser-sync": "^3.0.2", "jest": "^29.5.0", @@ -270,11 +273,13 @@ } }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1902.22", + "version": "0.1902.23", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1902.23.tgz", + "integrity": "sha512-EGZQcgnAg4JCsYhi9HNkoTMS46V6IabN4e2s0lsO53E8qcrp17KueGW5L24LdId6Fkp5JfzofMHxAQGcuIGheA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1902.22", + "@angular-devkit/architect": "0.1902.23", "rxjs": "7.8.1" }, "engines": { @@ -289,6 +294,8 @@ }, "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": { "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -296,14 +303,16 @@ } }, "node_modules/@angular-devkit/core": { - "version": "19.2.22", + "version": "19.2.23", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.23.tgz", + "integrity": "sha512-RazHPQkUEsNU/OZ75w9UeHxGFMthRiuAW2B/uA7eXExBj/1meHrrBfoCA56ujW2GUxVjRtSrMjylKh4R4meiYA==", "dev": true, "license": "MIT", "dependencies": { "ajv": "8.18.0", "ajv-formats": "3.0.1", "jsonc-parser": "3.3.1", - "picomatch": "4.0.2", + "picomatch": "4.0.4", "rxjs": "7.8.1", "source-map": "0.7.4" }, @@ -330,11 +339,13 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "19.2.22", + "version": "19.2.23", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.23.tgz", + "integrity": "sha512-Jzs7YM4X6azmHU7Mw5tQSPMuvaqYS8SLnZOJbtiXCy1JyuW9bm/WBBecNHMiuZ8LHXKhvQ6AVX1tKrzF6uiDmw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.22", + "@angular-devkit/core": "19.2.23", "jsonc-parser": "3.3.1", "magic-string": "0.30.17", "ora": "5.4.1", @@ -458,12 +469,14 @@ } }, "node_modules/@angular/build": { - "version": "19.2.22", + "version": "19.2.23", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-19.2.23.tgz", + "integrity": "sha512-AyE1MHHiDYpvCzRRRwrJwInyWa4eKu3hH0f2lOe0YfAcOIGwARBnc4vyLjR35DEpL/dZMciGPvaFXTxkaKVYQg==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1902.22", + "@angular-devkit/architect": "0.1902.23", "@babel/core": "7.26.10", "@babel/helper-annotate-as-pure": "7.25.9", "@babel/helper-split-export-declaration": "7.24.7", @@ -480,7 +493,7 @@ "magic-string": "0.30.17", "mrmime": "2.0.1", "parse5-html-rewriting-stream": "7.0.0", - "picomatch": "4.0.2", + "picomatch": "4.0.4", "piscina": "4.8.0", "rollup": "4.59.0", "sass": "1.85.0", @@ -503,7 +516,7 @@ "@angular/localize": "^19.0.0 || ^19.2.0-next.0", "@angular/platform-server": "^19.0.0 || ^19.2.0-next.0", "@angular/service-worker": "^19.0.0 || ^19.2.0-next.0", - "@angular/ssr": "^19.2.22", + "@angular/ssr": "^19.2.23", "karma": "^6.4.0", "less": "^4.2.0", "ng-packagr": "^19.0.0 || ^19.2.0-next.0", @@ -542,16 +555,18 @@ } }, "node_modules/@angular/cli": { - "version": "19.2.22", + "version": "19.2.23", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-19.2.23.tgz", + "integrity": "sha512-EZcayMLMmDF1IAfny8YIqvuAJJhyFdTVVsWea+4MNpkyVs2Sc7tsl7yFyckjJU7RVhdqYuSZSVqn5PHeSe5Pxw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1902.22", - "@angular-devkit/core": "19.2.22", - "@angular-devkit/schematics": "19.2.22", + "@angular-devkit/architect": "0.1902.23", + "@angular-devkit/core": "19.2.23", + "@angular-devkit/schematics": "19.2.23", "@inquirer/prompts": "7.3.2", "@listr2/prompt-adapter-inquirer": "2.0.18", - "@schematics/angular": "19.2.22", + "@schematics/angular": "19.2.23", "@yarnpkg/lockfile": "1.1.0", "ini": "5.0.0", "jsonc-parser": "3.3.1", @@ -2137,6 +2152,8 @@ }, "node_modules/@babel/helper-annotate-as-pure": { "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", "dev": true, "license": "MIT", "dependencies": { @@ -2401,6 +2418,8 @@ }, "node_modules/@babel/helper-split-export-declaration": { "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", "dev": true, "license": "MIT", "dependencies": { @@ -3509,6 +3528,7 @@ }, "node_modules/@babel/runtime": { "version": "7.26.10", + "dev": true, "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -4260,6 +4280,7 @@ }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" @@ -4270,6 +4291,7 @@ }, "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { "version": "0.3.9", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", @@ -4795,7 +4817,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.12", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -4866,10 +4890,6 @@ "node": ">=18.x" } }, - "node_modules/@filestack/loader": { - "version": "1.0.9", - "license": "SEE LICENSE IN LICENSE" - }, "node_modules/@graphql-typed-document-node/core": { "version": "3.2.0", "license": "MIT", @@ -4891,7 +4911,9 @@ } }, "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.12", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -5273,14 +5295,16 @@ } }, "node_modules/@ionic/angular-toolkit/node_modules/@angular-devkit/core": { - "version": "20.3.21", + "version": "20.3.22", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-20.3.22.tgz", + "integrity": "sha512-1vZnZTAjGcCM+86v2al+2eiROiSw0uAWeVllfHSQe0KsKOP1FE8UUUiWChhxVn7vIxypphlfGunkeeIn1C/ZFw==", "dev": true, "license": "MIT", "dependencies": { "ajv": "8.18.0", "ajv-formats": "3.0.1", "jsonc-parser": "3.3.1", - "picomatch": "4.0.3", + "picomatch": "4.0.4", "rxjs": "7.8.2", "source-map": "0.7.6" }, @@ -5299,11 +5323,13 @@ } }, "node_modules/@ionic/angular-toolkit/node_modules/@angular-devkit/schematics": { - "version": "20.3.21", + "version": "20.3.22", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-20.3.22.tgz", + "integrity": "sha512-gN2XSXRn3eErGEJlH0iSfQZZ7NdxVZNdjSxuVEGBEFhe3cVeC21LzM3GTWW6xwtBb4pxHglFyc7BUFiYtZiYtg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "20.3.21", + "@angular-devkit/core": "20.3.22", "jsonc-parser": "3.3.1", "magic-string": "0.30.17", "ora": "8.2.0", @@ -5316,12 +5342,14 @@ } }, "node_modules/@ionic/angular-toolkit/node_modules/@schematics/angular": { - "version": "20.3.21", + "version": "20.3.22", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-20.3.22.tgz", + "integrity": "sha512-wXTdFaPIBnSSNj/m0kclvPCYQOc2EGTQN1+Q3j9RIghS9gKgPxI1unSfgieJldZWKzcl8+WdB2zUuDzE7tEshQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "20.3.21", - "@angular-devkit/schematics": "20.3.21", + "@angular-devkit/core": "20.3.22", + "@angular-devkit/schematics": "20.3.22", "jsonc-parser": "3.3.1" }, "engines": { @@ -5422,17 +5450,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@ionic/angular-toolkit/node_modules/picomatch": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/@ionic/angular-toolkit/node_modules/source-map": { "version": "0.7.6", "dev": true, @@ -5611,6 +5628,7 @@ }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -5627,6 +5645,7 @@ }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", + "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { @@ -5640,6 +5659,8 @@ }, "node_modules/@jsonjoy.com/base64": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5655,6 +5676,8 @@ }, "node_modules/@jsonjoy.com/buffers": { "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-17.67.0.tgz", + "integrity": "sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5670,6 +5693,8 @@ }, "node_modules/@jsonjoy.com/codegen": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz", + "integrity": "sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5685,6 +5710,8 @@ }, "node_modules/@jsonjoy.com/fs-core": { "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.57.1.tgz", + "integrity": "sha512-YrEi/ZPmgc+GfdO0esBF04qv8boK9Dg9WpRQw/+vM8Qt3nnVIJWIa8HwZ/LXVZ0DB11XUROM8El/7yYTJX+WtA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5705,6 +5732,8 @@ }, "node_modules/@jsonjoy.com/fs-fsa": { "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.57.1.tgz", + "integrity": "sha512-ooEPvSW/HQDivPDPZMibHGKZf/QS4WRir1czGZmXmp3MsQqLECZEpN0JobrD8iV9BzsuwdIv+PxtWX9WpPLsIA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5726,6 +5755,8 @@ }, "node_modules/@jsonjoy.com/fs-node": { "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.57.1.tgz", + "integrity": "sha512-3YaKhP8gXEKN+2O49GLNfNb5l2gbnCFHyAaybbA2JkkbQP3dpdef7WcUaHAulg/c5Dg4VncHsA3NWAUSZMR5KQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5750,6 +5781,8 @@ }, "node_modules/@jsonjoy.com/fs-node-builtins": { "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.57.1.tgz", + "integrity": "sha512-XHkFKQ5GSH3uxm8c3ZYXVrexGdscpWKIcMWKFQpMpMJc8gA3AwOMBJXJlgpdJqmrhPyQXxaY9nbkNeYpacC0Og==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5765,6 +5798,8 @@ }, "node_modules/@jsonjoy.com/fs-node-to-fsa": { "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.57.1.tgz", + "integrity": "sha512-pqGHyWWzNck4jRfaGV39hkqpY5QjRUQ/nRbNT7FYbBa0xf4bDG+TE1Gt2KWZrSkrkZZDE3qZUjYMbjwSliX6pg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5785,6 +5820,8 @@ }, "node_modules/@jsonjoy.com/fs-node-utils": { "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.57.1.tgz", + "integrity": "sha512-vp+7ZzIB8v43G+GLXTS4oDUSQmhAsRz532QmmWBbdYA20s465JvwhkSFvX9cVTqRRAQg+vZ7zWDaIEh0lFe2gw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5803,6 +5840,8 @@ }, "node_modules/@jsonjoy.com/fs-print": { "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.57.1.tgz", + "integrity": "sha512-Ynct7ZJmfk6qoXDOKfpovNA36ITUx8rChLmRQtW08J73VOiuNsU8PB6d/Xs7fxJC2ohWR3a5AqyjmLojfrw5yw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5822,6 +5861,8 @@ }, "node_modules/@jsonjoy.com/fs-snapshot": { "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.57.1.tgz", + "integrity": "sha512-/oG8xBNFMbDXTq9J7vepSA1kerS5vpgd3p5QZSPd+nX59uwodGJftI51gDYyHRpP57P3WCQf7LHtBYPqwUg2Bg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5843,6 +5884,8 @@ }, "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/base64": { "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-17.67.0.tgz", + "integrity": "sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5858,6 +5901,8 @@ }, "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/codegen": { "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-17.67.0.tgz", + "integrity": "sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5873,6 +5918,8 @@ }, "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pack": { "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-17.67.0.tgz", + "integrity": "sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5898,6 +5945,8 @@ }, "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pointer": { "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-17.67.0.tgz", + "integrity": "sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5916,6 +5965,8 @@ }, "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/util": { "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-17.67.0.tgz", + "integrity": "sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5935,6 +5986,8 @@ }, "node_modules/@jsonjoy.com/json-pack": { "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz", + "integrity": "sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5960,6 +6013,8 @@ }, "node_modules/@jsonjoy.com/json-pack/node_modules/@jsonjoy.com/buffers": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5975,6 +6030,8 @@ }, "node_modules/@jsonjoy.com/json-pointer": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz", + "integrity": "sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5994,6 +6051,8 @@ }, "node_modules/@jsonjoy.com/util": { "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.9.0.tgz", + "integrity": "sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -6013,6 +6072,8 @@ }, "node_modules/@jsonjoy.com/util/node_modules/@jsonjoy.com/buffers": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -6041,6 +6102,8 @@ }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", "dev": true, "license": "MIT" }, @@ -6079,6 +6142,8 @@ }, "node_modules/@lmdb/lmdb-darwin-arm64": { "version": "3.2.6", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.2.6.tgz", + "integrity": "sha512-yF/ih9EJJZc72psFQbwnn8mExIWfTnzWJg+N02hnpXtDPETYLmQswIMBn7+V88lfCaFrMozJsUvcEQIkEPU0Gg==", "cpu": [ "arm64" ], @@ -6161,6 +6226,8 @@ }, "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", "cpu": [ "arm64" ], @@ -6561,7 +6628,9 @@ } }, "node_modules/@ngtools/webpack": { - "version": "19.2.22", + "version": "19.2.23", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-19.2.23.tgz", + "integrity": "sha512-RpA0ukuAcpvblXDEo702JWjblLXNrfr6BHjf/aVwujSM2yJSRQ5hZyauX9spCXMb8h3p+0dor62o6VDAQ5rAcQ==", "dev": true, "license": "MIT", "engines": { @@ -7172,18 +7241,6 @@ "license": "MIT", "optional": true }, - "node_modules/@parcel/watcher/node_modules/picomatch": { - "version": "4.0.4", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "dev": true, @@ -7602,12 +7659,14 @@ } }, "node_modules/@schematics/angular": { - "version": "19.2.22", + "version": "19.2.23", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-19.2.23.tgz", + "integrity": "sha512-ansmlDO94+d5kkDhBSvtS4XEKpNpmjOyBO3O6ChVqkE6MUrzJ5YI6jAm7p/zxQOMKrS3DhRUS3SUh/Xm/M3VWw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.22", - "@angular-devkit/schematics": "19.2.22", + "@angular-devkit/core": "19.2.23", + "@angular-devkit/schematics": "19.2.23", "jsonc-parser": "3.3.1" }, "engines": { @@ -7616,69 +7675,6 @@ "yarn": ">= 1.13.0" } }, - "node_modules/@sentry-internal/browser-utils": { - "version": "8.55.0", - "license": "MIT", - "dependencies": { - "@sentry/core": "8.55.0" - }, - "engines": { - "node": ">=14.18" - } - }, - "node_modules/@sentry-internal/feedback": { - "version": "8.55.0", - "license": "MIT", - "dependencies": { - "@sentry/core": "8.55.0" - }, - "engines": { - "node": ">=14.18" - } - }, - "node_modules/@sentry-internal/replay": { - "version": "8.55.0", - "license": "MIT", - "dependencies": { - "@sentry-internal/browser-utils": "8.55.0", - "@sentry/core": "8.55.0" - }, - "engines": { - "node": ">=14.18" - } - }, - "node_modules/@sentry-internal/replay-canvas": { - "version": "8.55.0", - "license": "MIT", - "dependencies": { - "@sentry-internal/replay": "8.55.0", - "@sentry/core": "8.55.0" - }, - "engines": { - "node": ">=14.18" - } - }, - "node_modules/@sentry/browser": { - "version": "8.55.0", - "license": "MIT", - "dependencies": { - "@sentry-internal/browser-utils": "8.55.0", - "@sentry-internal/feedback": "8.55.0", - "@sentry-internal/replay": "8.55.0", - "@sentry-internal/replay-canvas": "8.55.0", - "@sentry/core": "8.55.0" - }, - "engines": { - "node": ">=14.18" - } - }, - "node_modules/@sentry/core": { - "version": "8.55.0", - "license": "MIT", - "engines": { - "node": ">=14.18" - } - }, "node_modules/@serverless/dashboard-plugin": { "version": "7.2.3", "dev": true, @@ -7899,7 +7895,9 @@ } }, "node_modules/@serverless/platform-client/node_modules/brace-expansion": { - "version": "1.1.12", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -8910,6 +8908,7 @@ }, "node_modules/@tokenizer/token": { "version": "0.3.0", + "dev": true, "license": "MIT" }, "node_modules/@transloadit/prettier-bytes": { @@ -8965,18 +8964,22 @@ }, "node_modules/@tsconfig/node10": { "version": "1.0.12", + "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node12": { "version": "1.0.11", + "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node14": { "version": "1.0.3", + "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node16": { "version": "1.0.4", + "dev": true, "license": "MIT" }, "node_modules/@tufjs/canonical-json": { @@ -9038,6 +9041,8 @@ }, "node_modules/@types/body-parser": { "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "dev": true, "license": "MIT", "dependencies": { @@ -9047,6 +9052,8 @@ }, "node_modules/@types/bonjour": { "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9066,6 +9073,8 @@ }, "node_modules/@types/connect": { "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dev": true, "license": "MIT", "dependencies": { @@ -9074,6 +9083,8 @@ }, "node_modules/@types/connect-history-api-fallback": { "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", "dev": true, "license": "MIT", "dependencies": { @@ -9114,6 +9125,8 @@ }, "node_modules/@types/express": { "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", "dev": true, "license": "MIT", "dependencies": { @@ -9125,6 +9138,8 @@ }, "node_modules/@types/express-serve-static-core": { "version": "4.19.8", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz", + "integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==", "dev": true, "license": "MIT", "dependencies": { @@ -9147,6 +9162,8 @@ }, "node_modules/@types/http-errors": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", "dev": true, "license": "MIT" }, @@ -9196,6 +9213,8 @@ }, "node_modules/@types/mime": { "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true, "license": "MIT" }, @@ -9206,6 +9225,7 @@ }, "node_modules/@types/node": { "version": "20.19.37", + "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -9213,6 +9233,8 @@ }, "node_modules/@types/node-forge": { "version": "1.3.14", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.14.tgz", + "integrity": "sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==", "dev": true, "license": "MIT", "dependencies": { @@ -9226,6 +9248,8 @@ }, "node_modules/@types/qs": { "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", "dev": true, "license": "MIT" }, @@ -9243,6 +9267,8 @@ }, "node_modules/@types/range-parser": { "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true, "license": "MIT" }, @@ -9265,6 +9291,8 @@ }, "node_modules/@types/send": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9273,6 +9301,8 @@ }, "node_modules/@types/serve-index": { "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", "dev": true, "license": "MIT", "dependencies": { @@ -9281,6 +9311,8 @@ }, "node_modules/@types/serve-static": { "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", "dev": true, "license": "MIT", "dependencies": { @@ -9291,6 +9323,8 @@ }, "node_modules/@types/serve-static/node_modules/@types/send": { "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", "dev": true, "license": "MIT", "dependencies": { @@ -9300,6 +9334,8 @@ }, "node_modules/@types/sockjs": { "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", "dev": true, "license": "MIT", "dependencies": { @@ -10393,6 +10429,8 @@ }, "node_modules/@vitejs/plugin-basic-ssl": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.2.0.tgz", + "integrity": "sha512-mkQnxTkcldAzIsomk1UuLfAu9n+kpQ3JbHcpCp7d2Oo6ITtji8pHS3QToOWjhPFvNQSnhlkAjmGbhv2QvwO/7Q==", "dev": true, "license": "MIT", "engines": { @@ -10597,10 +10635,6 @@ "es5-ext": "^0.10.47" } }, - "node_modules/abab": { - "version": "2.0.6", - "license": "BSD-3-Clause" - }, "node_modules/abbrev": { "version": "3.0.1", "dev": true, @@ -10611,6 +10645,7 @@ }, "node_modules/abort-controller": { "version": "3.0.0", + "dev": true, "license": "MIT", "dependencies": { "event-target-shim": "^5.0.0" @@ -10660,6 +10695,7 @@ }, "node_modules/acorn": { "version": "8.16.0", + "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -10689,6 +10725,7 @@ }, "node_modules/acorn-walk": { "version": "8.3.5", + "dev": true, "license": "MIT", "dependencies": { "acorn": "^8.11.0" @@ -10804,6 +10841,8 @@ }, "node_modules/ansi-html-community": { "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", "dev": true, "engines": [ "node >= 0.8.0" @@ -10955,7 +10994,9 @@ } }, "node_modules/archiver-utils/node_modules/brace-expansion": { - "version": "1.1.12", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -11022,6 +11063,7 @@ }, "node_modules/arg": { "version": "4.1.3", + "dev": true, "license": "MIT" }, "node_modules/argparse": { @@ -11054,6 +11096,8 @@ }, "node_modules/array-flatten": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true, "license": "MIT" }, @@ -11376,6 +11420,7 @@ }, "node_modules/base64-js": { "version": "1.5.1", + "dev": true, "funding": [ { "type": "github", @@ -11434,6 +11479,8 @@ }, "node_modules/beasties": { "version": "0.3.2", + "resolved": "https://registry.npmjs.org/beasties/-/beasties-0.3.2.tgz", + "integrity": "sha512-p4AF8uYzm9Fwu8m/hSVTCPXrRBPmB34hQpHsec2KOaR9CZmgoU8IOv4Cvwq4hgz2p4hLMNbsdNl5XeA6XbAQwA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -11528,6 +11575,8 @@ }, "node_modules/bonjour-service": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz", + "integrity": "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==", "dev": true, "license": "MIT", "dependencies": { @@ -11560,7 +11609,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "2.0.2", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", "dev": true, "license": "MIT", "dependencies": { @@ -11682,6 +11733,8 @@ }, "node_modules/bundle-name": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", "dev": true, "license": "MIT", "dependencies": { @@ -12555,6 +12608,8 @@ }, "node_modules/compressible": { "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", "dev": true, "license": "MIT", "dependencies": { @@ -12566,6 +12621,8 @@ }, "node_modules/compression": { "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "dev": true, "license": "MIT", "dependencies": { @@ -12583,6 +12640,8 @@ }, "node_modules/compression/node_modules/debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "license": "MIT", "dependencies": { @@ -12591,11 +12650,15 @@ }, "node_modules/compression/node_modules/ms": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true, "license": "MIT" }, "node_modules/compression/node_modules/negotiator": { "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", "dev": true, "license": "MIT", "engines": { @@ -12604,6 +12667,8 @@ }, "node_modules/compression/node_modules/safe-buffer": { "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, "funding": [ { @@ -12650,6 +12715,8 @@ }, "node_modules/connect-history-api-fallback": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", "dev": true, "license": "MIT", "engines": { @@ -12722,6 +12789,8 @@ }, "node_modules/cookie-signature": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", "dev": true, "license": "MIT" }, @@ -12909,6 +12978,7 @@ }, "node_modules/create-require": { "version": "1.1.1", + "dev": true, "license": "MIT" }, "node_modules/cropperjs": { @@ -13054,7 +13124,9 @@ "license": "MIT" }, "node_modules/cucumber/node_modules/brace-expansion": { - "version": "1.1.12", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -13184,6 +13256,7 @@ }, "node_modules/debug": { "version": "4.4.3", + "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -13490,6 +13563,8 @@ }, "node_modules/default-browser": { "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", "dev": true, "license": "MIT", "dependencies": { @@ -13505,6 +13580,8 @@ }, "node_modules/default-browser-id": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", "dev": true, "license": "MIT", "engines": { @@ -13562,6 +13639,8 @@ }, "node_modules/define-lazy-prop": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "dev": true, "license": "MIT", "engines": { @@ -13641,6 +13720,8 @@ }, "node_modules/detect-node": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true, "license": "MIT" }, @@ -13660,6 +13741,7 @@ }, "node_modules/diff": { "version": "4.0.4", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -13678,6 +13760,8 @@ }, "node_modules/dns-packet": { "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "dev": true, "license": "MIT", "dependencies": { @@ -14449,7 +14533,9 @@ } }, "node_modules/eslint-plugin-import/node_modules/brace-expansion": { - "version": "1.1.12", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -14593,7 +14679,9 @@ } }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.12", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -14777,6 +14865,7 @@ }, "node_modules/event-target-shim": { "version": "5.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -14864,6 +14953,8 @@ }, "node_modules/express": { "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", "dev": true, "license": "MIT", "dependencies": { @@ -14909,6 +15000,8 @@ }, "node_modules/express/node_modules/body-parser": { "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", "dev": true, "license": "MIT", "dependencies": { @@ -14932,6 +15025,8 @@ }, "node_modules/express/node_modules/debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "license": "MIT", "dependencies": { @@ -14940,11 +15035,15 @@ }, "node_modules/express/node_modules/debug/node_modules/ms": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true, "license": "MIT" }, "node_modules/express/node_modules/encodeurl": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "dev": true, "license": "MIT", "engines": { @@ -14953,6 +15052,8 @@ }, "node_modules/express/node_modules/finalhandler": { "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", "dev": true, "license": "MIT", "dependencies": { @@ -14970,6 +15071,8 @@ }, "node_modules/express/node_modules/fresh": { "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true, "license": "MIT", "engines": { @@ -14978,6 +15081,8 @@ }, "node_modules/express/node_modules/iconv-lite": { "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "license": "MIT", "dependencies": { @@ -14989,6 +15094,8 @@ }, "node_modules/express/node_modules/media-typer": { "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true, "license": "MIT", "engines": { @@ -14997,6 +15104,8 @@ }, "node_modules/express/node_modules/mime": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, "license": "MIT", "bin": { @@ -15008,6 +15117,8 @@ }, "node_modules/express/node_modules/mime-db": { "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, "license": "MIT", "engines": { @@ -15016,6 +15127,8 @@ }, "node_modules/express/node_modules/mime-types": { "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "license": "MIT", "dependencies": { @@ -15027,6 +15140,8 @@ }, "node_modules/express/node_modules/qs": { "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -15041,6 +15156,8 @@ }, "node_modules/express/node_modules/raw-body": { "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "dev": true, "license": "MIT", "dependencies": { @@ -15055,6 +15172,8 @@ }, "node_modules/express/node_modules/safe-buffer": { "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, "funding": [ { @@ -15074,6 +15193,8 @@ }, "node_modules/express/node_modules/send": { "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", "dev": true, "license": "MIT", "dependencies": { @@ -15097,6 +15218,8 @@ }, "node_modules/express/node_modules/statuses": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "dev": true, "license": "MIT", "engines": { @@ -15105,6 +15228,8 @@ }, "node_modules/express/node_modules/type-is": { "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dev": true, "license": "MIT", "dependencies": { @@ -15236,6 +15361,7 @@ }, "node_modules/fast-xml-builder": { "version": "1.1.4", + "dev": true, "funding": [ { "type": "github", @@ -15247,24 +15373,6 @@ "path-expression-matcher": "^1.1.3" } }, - "node_modules/fast-xml-parser": { - "version": "5.5.9", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "fast-xml-builder": "^1.1.4", - "path-expression-matcher": "^1.2.0", - "strnum": "^2.2.2" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, "node_modules/fastest-levenshtein": { "version": "1.0.16", "dev": true, @@ -15351,6 +15459,7 @@ }, "node_modules/file-type": { "version": "16.5.4", + "dev": true, "license": "MIT", "dependencies": { "readable-web-to-node-stream": "^3.0.0", @@ -15396,58 +15505,6 @@ "node": ">= 10.4.0" } }, - "node_modules/filestack-js": { - "version": "3.46.4", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.4", - "@filestack/loader": "^1.0.9", - "@sentry/browser": "8.x.x", - "abab": "^2.0.6", - "debug": "^4.3.4", - "eventemitter3": "^5.0.0", - "fast-xml-parser": "^5.3.6", - "file-type": "^16.5.4", - "follow-redirects": "^1.15.2", - "isutf8": "^4.0.0", - "jsonschema": "^1.4.1", - "lodash.clonedeep": "^4.5.0", - "p-queue": "^6.6.2", - "spark-md5": "^3.0.2", - "ts-node": "^10.9.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/filestack-js/node_modules/p-queue": { - "version": "6.6.2", - "license": "MIT", - "dependencies": { - "eventemitter3": "^4.0.4", - "p-timeout": "^3.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/filestack-js/node_modules/p-queue/node_modules/eventemitter3": { - "version": "4.0.7", - "license": "MIT" - }, - "node_modules/filestack-js/node_modules/p-timeout": { - "version": "3.2.0", - "license": "MIT", - "dependencies": { - "p-finally": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/fill-range": { "version": "7.1.1", "dev": true, @@ -15582,6 +15639,7 @@ }, "node_modules/follow-redirects": { "version": "1.15.11", + "dev": true, "funding": [ { "type": "individual", @@ -15677,6 +15735,8 @@ }, "node_modules/forwarded": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "dev": true, "license": "MIT", "engines": { @@ -15982,6 +16042,8 @@ }, "node_modules/glob-to-regex.js": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz", + "integrity": "sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -16162,11 +16224,15 @@ }, "node_modules/handle-thing": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true, "license": "MIT" }, "node_modules/handlebars": { - "version": "4.7.8", + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", "dev": true, "license": "MIT", "dependencies": { @@ -16302,6 +16368,8 @@ }, "node_modules/hpack.js": { "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -16313,11 +16381,15 @@ }, "node_modules/hpack.js/node_modules/isarray": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true, "license": "MIT" }, "node_modules/hpack.js/node_modules/readable-stream": { "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "license": "MIT", "dependencies": { @@ -16332,6 +16404,8 @@ }, "node_modules/hpack.js/node_modules/string_decoder": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "license": "MIT", "dependencies": { @@ -16424,6 +16498,8 @@ }, "node_modules/http-deceiver": { "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", "dev": true, "license": "MIT" }, @@ -16538,6 +16614,8 @@ }, "node_modules/hyperdyperid": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", "dev": true, "license": "MIT", "engines": { @@ -16635,6 +16713,7 @@ }, "node_modules/ieee754": { "version": "1.2.1", + "dev": true, "funding": [ { "type": "github", @@ -16914,6 +16993,8 @@ }, "node_modules/ipaddr.js": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz", + "integrity": "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==", "dev": true, "license": "MIT", "engines": { @@ -17080,6 +17161,8 @@ }, "node_modules/is-docker": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", "dev": true, "license": "MIT", "bin": { @@ -17161,6 +17244,8 @@ }, "node_modules/is-inside-container": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", "dev": true, "license": "MIT", "dependencies": { @@ -17431,6 +17516,8 @@ }, "node_modules/is-wsl": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", "dev": true, "license": "MIT", "dependencies": { @@ -17490,6 +17577,8 @@ }, "node_modules/istanbul-lib-instrument": { "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -17549,13 +17638,6 @@ "node": ">=8" } }, - "node_modules/isutf8": { - "version": "4.0.1", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, "node_modules/jackspeak": { "version": "3.4.3", "dev": true, @@ -17862,13 +17944,6 @@ ], "license": "MIT" }, - "node_modules/jsonschema": { - "version": "1.5.0", - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/jszip": { "version": "3.10.1", "dev": true, @@ -18000,7 +18075,9 @@ } }, "node_modules/karma-coverage-istanbul-reporter/node_modules/brace-expansion": { - "version": "1.1.12", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -18109,7 +18186,9 @@ } }, "node_modules/karma-coverage/node_modules/brace-expansion": { - "version": "1.1.12", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -18268,7 +18347,9 @@ } }, "node_modules/karma/node_modules/brace-expansion": { - "version": "1.1.12", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -18578,6 +18659,8 @@ }, "node_modules/launch-editor": { "version": "2.13.2", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.13.2.tgz", + "integrity": "sha512-4VVDnbOpLXy/s8rdRCSXb+zfMeFR0WlJWpET1iA9CQdlZDfwyLjUuGQzXU4VeOoey6AicSAluWan7Etga6Kcmg==", "dev": true, "license": "MIT", "dependencies": { @@ -18852,6 +18935,8 @@ }, "node_modules/lmdb": { "version": "3.2.6", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.2.6.tgz", + "integrity": "sha512-SuHqzPl7mYStna8WRotY8XX/EUZBjjv3QyKIByeCLFfC9uXT/OIHByEcA07PzbMfQAM0KYJtLgtpMRlIe5dErQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -18914,11 +18999,15 @@ } }, "node_modules/lodash": { - "version": "4.17.23", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, "node_modules/lodash-es": { - "version": "4.17.23", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", "license": "MIT" }, "node_modules/lodash._baseiteratee": { @@ -19316,6 +19405,7 @@ }, "node_modules/make-error": { "version": "1.3.6", + "dev": true, "license": "ISC" }, "node_modules/make-fetch-happen": { @@ -19383,6 +19473,8 @@ }, "node_modules/memfs": { "version": "4.57.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.57.1.tgz", + "integrity": "sha512-WvzrWPwMQT+PtbX2Et64R4qXKK0fj/8pO85MrUCzymX3twwCiJCdvntW3HdhG1teLJcHDDLIKx5+c3HckWYZtQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -19429,6 +19521,8 @@ }, "node_modules/merge-descriptors": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", "dev": true, "license": "MIT", "funding": { @@ -19568,6 +19662,8 @@ }, "node_modules/minimalistic-assert": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "dev": true, "license": "ISC" }, @@ -19787,10 +19883,13 @@ }, "node_modules/ms": { "version": "2.1.3", + "dev": true, "license": "MIT" }, "node_modules/msgpackr": { "version": "1.11.9", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.9.tgz", + "integrity": "sha512-FkoAAyyA6HM8wL882EcEyFZ9s7hVADSwG9xrVx3dxxNQAtgADTrJoEWivID82Iv1zWDsv/OtbrrcZAzGzOMdNw==", "dev": true, "license": "MIT", "optional": true, @@ -19800,6 +19899,8 @@ }, "node_modules/msgpackr-extract": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -19821,6 +19922,8 @@ }, "node_modules/multicast-dns": { "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", "dev": true, "license": "MIT", "dependencies": { @@ -20155,6 +20258,8 @@ }, "node_modules/node-addon-api": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", "dev": true, "license": "MIT", "optional": true @@ -20171,7 +20276,9 @@ } }, "node_modules/node-dir/node_modules/brace-expansion": { - "version": "1.1.12", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -20211,6 +20318,8 @@ }, "node_modules/node-forge": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", + "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", "dev": true, "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { @@ -20242,6 +20351,8 @@ }, "node_modules/node-gyp-build-optional-packages": { "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", "dev": true, "license": "MIT", "optional": true, @@ -20570,6 +20681,8 @@ }, "node_modules/obuf": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "dev": true, "license": "MIT" }, @@ -20616,6 +20729,8 @@ }, "node_modules/open": { "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", "dev": true, "license": "MIT", "dependencies": { @@ -20731,6 +20846,8 @@ }, "node_modules/ordered-binary": { "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.6.1.tgz", + "integrity": "sha512-QkCdPooczexPLiXIrbVOPYkR3VO3T6v2OyKRkR1Xbhpy7/LAVXwahnRCgRp78Oe/Ehf0C/HATAxfSr6eA1oX+w==", "dev": true, "license": "MIT", "optional": true @@ -20801,6 +20918,7 @@ }, "node_modules/p-finally": { "version": "1.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -21008,6 +21126,8 @@ }, "node_modules/parse5-html-rewriting-stream": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-7.0.0.tgz", + "integrity": "sha512-mazCyGWkmCRWDI15Zp+UiCqMp/0dgEmkZRvhlsqqKYr4SsVm/TvnSpD9fCvqCA2zoWJcfRym846ejWBBHRiYEg==", "dev": true, "license": "MIT", "dependencies": { @@ -21044,6 +21164,8 @@ }, "node_modules/parse5-sax-parser": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-7.0.0.tgz", + "integrity": "sha512-5A+v2SNsq8T6/mG3ahcz8ZtQ0OUFTatxPbeidoMB7tkJSGDY3tdfl4MHovtLQHkEn5CGxijNWRQHhRQ6IRpXKg==", "dev": true, "license": "MIT", "dependencies": { @@ -21087,6 +21209,7 @@ }, "node_modules/path-expression-matcher": { "version": "1.2.0", + "dev": true, "funding": [ { "type": "github", @@ -21152,7 +21275,9 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.12", + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", "dev": true, "license": "MIT" }, @@ -21190,6 +21315,7 @@ }, "node_modules/peek-readable": { "version": "4.1.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -21210,7 +21336,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.2", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -21432,6 +21560,8 @@ }, "node_modules/postcss-media-query-parser": { "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", "dev": true, "license": "MIT" }, @@ -21558,6 +21688,7 @@ }, "node_modules/process": { "version": "0.11.10", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6.0" @@ -21664,6 +21795,8 @@ }, "node_modules/proxy-addr": { "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dev": true, "license": "MIT", "dependencies": { @@ -21676,6 +21809,8 @@ }, "node_modules/proxy-addr/node_modules/ipaddr.js": { "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true, "license": "MIT", "engines": { @@ -21904,6 +22039,7 @@ }, "node_modules/readable-web-to-node-stream": { "version": "3.0.4", + "dev": true, "license": "MIT", "dependencies": { "readable-stream": "^4.7.0" @@ -21918,6 +22054,7 @@ }, "node_modules/readable-web-to-node-stream/node_modules/buffer": { "version": "6.0.3", + "dev": true, "funding": [ { "type": "github", @@ -21940,6 +22077,7 @@ }, "node_modules/readable-web-to-node-stream/node_modules/events": { "version": "3.3.0", + "dev": true, "license": "MIT", "engines": { "node": ">=0.8.x" @@ -21947,6 +22085,7 @@ }, "node_modules/readable-web-to-node-stream/node_modules/readable-stream": { "version": "4.7.0", + "dev": true, "license": "MIT", "dependencies": { "abort-controller": "^3.0.0", @@ -22034,6 +22173,7 @@ }, "node_modules/regenerator-runtime": { "version": "0.14.1", + "dev": true, "license": "MIT" }, "node_modules/regex-parser": { @@ -22266,7 +22406,9 @@ } }, "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.12", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -22459,6 +22601,8 @@ }, "node_modules/run-applescript": { "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", "dev": true, "license": "MIT", "engines": { @@ -22549,7 +22693,9 @@ } }, "node_modules/rxjs-report-usage/node_modules/brace-expansion": { - "version": "1.1.12", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -22769,11 +22915,15 @@ }, "node_modules/select-hose": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", "dev": true, "license": "MIT" }, "node_modules/selfsigned": { "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", "dev": true, "license": "MIT", "dependencies": { @@ -22943,6 +23093,8 @@ }, "node_modules/serve-static": { "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", "dev": true, "license": "MIT", "dependencies": { @@ -22957,6 +23109,8 @@ }, "node_modules/serve-static/node_modules/debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "license": "MIT", "dependencies": { @@ -22965,11 +23119,15 @@ }, "node_modules/serve-static/node_modules/debug/node_modules/ms": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true, "license": "MIT" }, "node_modules/serve-static/node_modules/encodeurl": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "dev": true, "license": "MIT", "engines": { @@ -22978,6 +23136,8 @@ }, "node_modules/serve-static/node_modules/fresh": { "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true, "license": "MIT", "engines": { @@ -22986,6 +23146,8 @@ }, "node_modules/serve-static/node_modules/mime": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, "license": "MIT", "bin": { @@ -22997,6 +23159,8 @@ }, "node_modules/serve-static/node_modules/send": { "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", "dev": true, "license": "MIT", "dependencies": { @@ -23020,6 +23184,8 @@ }, "node_modules/serve-static/node_modules/statuses": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "dev": true, "license": "MIT", "engines": { @@ -23410,6 +23576,8 @@ }, "node_modules/shell-quote": { "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", "dev": true, "license": "MIT", "engines": { @@ -23654,6 +23822,8 @@ }, "node_modules/sockjs": { "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", "dev": true, "license": "MIT", "dependencies": { @@ -23664,6 +23834,8 @@ }, "node_modules/sockjs/node_modules/uuid": { "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, "license": "MIT", "bin": { @@ -23781,10 +23953,6 @@ "node": ">=0.10.0" } }, - "node_modules/spark-md5": { - "version": "3.0.2", - "license": "(WTFPL OR MIT)" - }, "node_modules/spdx-correct": { "version": "3.2.0", "dev": true, @@ -23815,6 +23983,8 @@ }, "node_modules/spdy": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", "dev": true, "license": "MIT", "dependencies": { @@ -23830,6 +24000,8 @@ }, "node_modules/spdy-transport": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", "dev": true, "license": "MIT", "dependencies": { @@ -24042,6 +24214,7 @@ }, "node_modules/string_decoder": { "version": "1.3.0", + "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" @@ -24049,6 +24222,7 @@ }, "node_modules/string_decoder/node_modules/safe-buffer": { "version": "5.2.1", + "dev": true, "funding": [ { "type": "github", @@ -24271,6 +24445,7 @@ }, "node_modules/strnum": { "version": "2.2.2", + "dev": true, "funding": [ { "type": "github", @@ -24281,6 +24456,7 @@ }, "node_modules/strtok3": { "version": "6.3.0", + "dev": true, "license": "MIT", "dependencies": { "@tokenizer/token": "^0.3.0", @@ -24558,6 +24734,8 @@ }, "node_modules/thingies": { "version": "2.6.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-2.6.0.tgz", + "integrity": "sha512-rMHRjmlFLM1R96UYPvpmnc3LYtdFrT33JIB7L9hetGue1qAPfn1N2LJeEjxUSidu1Iku+haLZXDuEXUHNGO/lg==", "dev": true, "license": "MIT", "engines": { @@ -24583,6 +24761,8 @@ }, "node_modules/thunky": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true, "license": "MIT" }, @@ -24617,17 +24797,6 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/title-case": { "version": "2.1.1", "dev": true, @@ -24698,6 +24867,7 @@ }, "node_modules/token-types": { "version": "4.2.1", + "dev": true, "license": "MIT", "dependencies": { "@tokenizer/token": "^0.3.0", @@ -24746,6 +24916,8 @@ }, "node_modules/tree-dump": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.1.0.tgz", + "integrity": "sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -24848,6 +25020,7 @@ }, "node_modules/ts-node": { "version": "10.9.2", + "dev": true, "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", @@ -25132,6 +25305,7 @@ }, "node_modules/typescript": { "version": "5.6.3", + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -25214,6 +25388,7 @@ }, "node_modules/undici-types": { "version": "6.21.0", + "dev": true, "license": "MIT" }, "node_modules/uni-global": { @@ -25490,6 +25665,7 @@ }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", + "dev": true, "license": "MIT" }, "node_modules/validate-npm-package-license": { @@ -25580,6 +25756,8 @@ }, "node_modules/vite": { "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, "license": "MIT", "dependencies": { @@ -25653,6 +25831,8 @@ }, "node_modules/vite/node_modules/nanoid": { "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, "funding": [ { @@ -25670,6 +25850,8 @@ }, "node_modules/vite/node_modules/postcss": { "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", "dev": true, "funding": [ { @@ -25705,6 +25887,8 @@ }, "node_modules/watchpack": { "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", "dev": true, "license": "MIT", "dependencies": { @@ -25717,6 +25901,8 @@ }, "node_modules/wbuf": { "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", "dev": true, "license": "MIT", "dependencies": { @@ -25733,6 +25919,8 @@ }, "node_modules/weak-lru-cache": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", + "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", "dev": true, "license": "MIT", "optional": true @@ -25791,6 +25979,8 @@ }, "node_modules/webpack-dev-middleware": { "version": "7.4.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz", + "integrity": "sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==", "dev": true, "license": "MIT", "dependencies": { @@ -25819,6 +26009,8 @@ }, "node_modules/webpack-dev-middleware/node_modules/mime-db": { "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, "license": "MIT", "engines": { @@ -25827,6 +26019,8 @@ }, "node_modules/webpack-dev-middleware/node_modules/mime-types": { "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "license": "MIT", "dependencies": { @@ -25838,6 +26032,8 @@ }, "node_modules/webpack-dev-server": { "version": "5.2.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.2.tgz", + "integrity": "sha512-QcQ72gh8a+7JO63TAx/6XZf/CWhgMzu5m0QirvPfGvptOusAxG12w2+aua1Jkjr7hzaWDnJ2n6JFeexMHI+Zjg==", "dev": true, "license": "MIT", "dependencies": { @@ -25894,6 +26090,8 @@ }, "node_modules/webpack-dev-server/node_modules/chokidar": { "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "license": "MIT", "dependencies": { @@ -25917,6 +26115,8 @@ }, "node_modules/webpack-dev-server/node_modules/glob-parent": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "license": "ISC", "dependencies": { @@ -25928,6 +26128,8 @@ }, "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { "version": "2.0.9", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", + "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", "dev": true, "license": "MIT", "dependencies": { @@ -25951,6 +26153,8 @@ }, "node_modules/webpack-dev-server/node_modules/is-plain-obj": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", "dev": true, "license": "MIT", "engines": { @@ -25962,6 +26166,8 @@ }, "node_modules/webpack-dev-server/node_modules/picomatch": { "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -25973,6 +26179,8 @@ }, "node_modules/webpack-dev-server/node_modules/readdirp": { "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "license": "MIT", "dependencies": { @@ -25984,6 +26192,8 @@ }, "node_modules/webpack-dev-server/node_modules/ws": { "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", "dev": true, "license": "MIT", "engines": { @@ -26511,7 +26721,9 @@ } }, "node_modules/yamljs/node_modules/brace-expansion": { - "version": "1.1.12", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -26611,6 +26823,7 @@ }, "node_modules/yn": { "version": "3.1.1", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -26714,7 +26927,9 @@ } }, "node_modules/zip-stream/node_modules/brace-expansion": { - "version": "1.1.12", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 35bc57d9b..4eff2ba30 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "postbuild": "npm run generate-version", "deploy": "ng build --base-href '/practera-app-v2/' && git commit -am 'Build to Github Page' && git subtree push --prefix www origin gh-pages", "postdeploy": "npm run generate-version", - "test": "ng test v3 --no-watch", + "test": "ng test v3 --no-watch --code-coverage --browsers ChromeHeadless", "sonar-test": "ng test --no-watch --code-coverage --browsers ChromeHeadless --karmaConfig=src/karma.conf.sonarci.js", "lint": "ng lint v3", "lint-fix": "ng lint --fix", diff --git a/projects/v3/karma.conf.js b/projects/v3/karma.conf.js index a7003e29d..216effff0 100644 --- a/projects/v3/karma.conf.js +++ b/projects/v3/karma.conf.js @@ -1,6 +1,8 @@ // Karma configuration file, see link for more information // https://karma-runner.github.io/1.0/config/configuration-file.html +const { random } = require('lodash'); + module.exports = function (config) { config.set({ basePath: '', diff --git a/projects/v3/src/app/app.component.spec.ts b/projects/v3/src/app/app.component.spec.ts index e237827db..a10cde821 100644 --- a/projects/v3/src/app/app.component.spec.ts +++ b/projects/v3/src/app/app.component.spec.ts @@ -1,8 +1,7 @@ import { NgZone } from '@angular/core'; import { fakeAsync, TestBed, tick } from '@angular/core/testing'; import { DomSanitizer } from '@angular/platform-browser'; -import { Router } from '@angular/router'; -import { RouterTestingModule } from '@angular/router/testing'; +import { Router, RouterModule } from '@angular/router'; import { Platform } from '@ionic/angular'; import { TestUtils } from '@testingv3/utils'; import { of } from 'rxjs'; @@ -27,7 +26,9 @@ describe('AppComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [ - RouterTestingModule + RouterModule.forRoot([ + { path: '', component: AppComponent } + ]), ], declarations: [ AppComponent diff --git a/projects/v3/src/app/components/achievement-pop-up/achievement-pop-up.component.ts b/projects/v3/src/app/components/achievement-pop-up/achievement-pop-up.component.ts index 5396935b1..fbadb5346 100644 --- a/projects/v3/src/app/components/achievement-pop-up/achievement-pop-up.component.ts +++ b/projects/v3/src/app/components/achievement-pop-up/achievement-pop-up.component.ts @@ -1,7 +1,7 @@ import { Component, ViewChild } from '@angular/core'; import { ModalController } from '@ionic/angular'; import { Achievement } from '@v3/services/achievement.service'; -import { UtilsService } from '@v3/services/utils.service'; +// import { UtilsService } from '@v3/services/utils.service'; @Component({ standalone: false, @@ -21,11 +21,12 @@ export class AchievementPopUpComponent { constructor( private modalController: ModalController, - private utils: UtilsService, + // private utils: UtilsService, ) {} get isMobile() { - return this.utils.isMobile(); + // return this.utils.isMobile(); + return false; } ionViewDidEnter() { diff --git a/projects/v3/src/app/components/activity-complete-pop-up/activity-complete-pop-up.component.ts b/projects/v3/src/app/components/activity-complete-pop-up/activity-complete-pop-up.component.ts index b47ecfdfe..8e20d3ef5 100644 --- a/projects/v3/src/app/components/activity-complete-pop-up/activity-complete-pop-up.component.ts +++ b/projects/v3/src/app/components/activity-complete-pop-up/activity-complete-pop-up.component.ts @@ -53,7 +53,7 @@ export class ActivityCompletePopUpComponent { confirmed(continueToActivity: boolean) { this.modalController.dismiss(); if (!continueToActivity) { - const route = this.utils.isMobile() ? ['v3', 'activity-mobile', this.activityId] : ['v3', 'activity-desktop', this.activityId]; + const route = this.isMobile ? ['v3', 'activity-mobile', this.activityId] : ['v3', 'activity-desktop', this.activityId]; this.router.navigate(route); } else { if (this.activityCompleted) { diff --git a/projects/v3/src/app/components/components.module.ts b/projects/v3/src/app/components/components.module.ts index 713e6b8fe..758325fd1 100644 --- a/projects/v3/src/app/components/components.module.ts +++ b/projects/v3/src/app/components/components.module.ts @@ -14,7 +14,6 @@ import { PopUpComponent } from './pop-up/pop-up.component'; import { LockTeamAssessmentPopUpComponent } from './lock-team-assessment-pop-up/lock-team-assessment-pop-up.component'; import { MultiTeamMemberSelectorComponent } from './multi-team-member-selector/multi-team-member-selector.component'; import { ActivityCompletePopUpComponent } from './activity-complete-pop-up/activity-complete-pop-up.component'; -import { FastFeedbackComponent } from './fast-feedback/fast-feedback.component'; import { ReviewRatingComponent } from './review-rating/review-rating.component'; import { CircleProgressComponent } from './circle-progress/circle-progress.component'; import { NgCircleProgressModule } from 'ng-circle-progress'; @@ -43,6 +42,7 @@ import { UppyUploaderComponent } from './uppy-uploader/uppy-uploader.component'; import { FileUploadComponent } from './file-upload/file-upload.component'; import { UppyUploaderService } from './uppy-uploader/uppy-uploader.service'; import { FilePopupComponent } from './file-popup/file-popup.component'; +import { FastFeedbackComponent } from './fast-feedback/fast-feedback.component'; import { SliderComponent } from './slider/slider.component'; import { LanguageDetectionPipe } from '../pipes/language.pipe'; import { ProjectBriefModalComponent } from './project-brief-modal/project-brief-modal.component'; diff --git a/projects/v3/src/app/components/fast-feedback/fast-feedback.component.ts b/projects/v3/src/app/components/fast-feedback/fast-feedback.component.ts index cbae22c75..e2d91b74d 100644 --- a/projects/v3/src/app/components/fast-feedback/fast-feedback.component.ts +++ b/projects/v3/src/app/components/fast-feedback/fast-feedback.component.ts @@ -54,9 +54,9 @@ export class FastFeedbackComponent implements OnInit, OnDestroy { private storage: BrowserStorageService, private navParams: NavParams, private homeService: HomeService, - private notificationsService: NotificationsService, private request: RequestService, - private demo: DemoService + private demo: DemoService, + private notificationsService: NotificationsService, ) { this.isMobile = this.utils.isMobile(); } diff --git a/projects/v3/src/app/components/file-display/file-display.component.spec.ts b/projects/v3/src/app/components/file-display/file-display.component.spec.ts index 6b41b7e9d..98baf24da 100644 --- a/projects/v3/src/app/components/file-display/file-display.component.spec.ts +++ b/projects/v3/src/app/components/file-display/file-display.component.spec.ts @@ -37,6 +37,13 @@ describe('FileDisplayComponent', () => { dismiss: Promise.resolve() }) }, + { + provide: ModalController, + useValue: jasmine.createSpyObj('ModalController', { + create: Promise.resolve({ present: jasmine.createSpy('present').and.returnValue(Promise.resolve()) }), + dismiss: Promise.resolve() + }) + }, ], }) .compileComponents(); diff --git a/projects/v3/src/app/components/file-display/file-display.component.ts b/projects/v3/src/app/components/file-display/file-display.component.ts index 4d3a91372..3fbb3b183 100644 --- a/projects/v3/src/app/components/file-display/file-display.component.ts +++ b/projects/v3/src/app/components/file-display/file-display.component.ts @@ -63,7 +63,6 @@ export class FileDisplayComponent { return await modal.present(); } - actionBtnClick( file: TusFileResponse, index: number diff --git a/projects/v3/src/app/components/review-rating/review-rating.component.ts b/projects/v3/src/app/components/review-rating/review-rating.component.ts index d40ae7e07..80a0a63ce 100644 --- a/projects/v3/src/app/components/review-rating/review-rating.component.ts +++ b/projects/v3/src/app/components/review-rating/review-rating.component.ts @@ -1,7 +1,6 @@ import { firstValueFrom } from 'rxjs'; import { Component, Inject, Input, OnInit, forwardRef } from '@angular/core'; import { Router } from '@angular/router'; -import { AlertController, ModalController } from '@ionic/angular'; import { ReviewRatingService, ReviewRating } from '@v3/services/review-rating.service'; import { UtilsService } from '@v3/services/utils.service'; import { FastFeedbackService } from '@v3/services/fast-feedback.service'; @@ -61,7 +60,6 @@ export class ReviewRatingComponent implements OnInit { constructor( private reviewRatingService: ReviewRatingService, - private modalController: ModalController, private router: Router, private utils: UtilsService, // types are 'any' to prevent design:paramtypes metadata from triggering circular dependency TDZ error @@ -154,7 +152,7 @@ export class ReviewRatingComponent implements OnInit { } async dismissModal(): Promise { - await this.modalController.dismiss(null, 'cancel', `review-popup-${this.reviewId}`); + await this.notificationsService.dismiss(null, 'cancel', `review-popup-${this.reviewId}`); await this.fastFeedbackOrRedirect(); } } diff --git a/projects/v3/src/app/components/support-popup/support-popup.component.spec.ts b/projects/v3/src/app/components/support-popup/support-popup.component.spec.ts index 3e029d564..05c80d9b4 100644 --- a/projects/v3/src/app/components/support-popup/support-popup.component.spec.ts +++ b/projects/v3/src/app/components/support-popup/support-popup.component.spec.ts @@ -4,10 +4,30 @@ import { of, throwError } from 'rxjs'; import { SupportPopupComponent } from './support-popup.component'; import { HubspotService } from '@v3/services/hubspot.service'; -import { UppyUploaderService } from '@v3/services/uppy-uploader.service'; +import { UppyUploaderService, UppyFileData } from '@v3/components/uppy-uploader/uppy-uploader.service'; import { UtilsService } from '@v3/services/utils.service'; import { NotificationsService } from '@v3/app/services/notifications.service'; +const mockUppyFile = (overrides: Partial = {}): UppyFileData => ({ + source: 'test', + id: 'test-id', + name: 'test-file', + extension: 'jpg', + meta: { relativePath: null, name: 'test-file', type: 'image/jpeg' }, + type: 'image/jpeg', + data: {}, + progress: { uploadStarted: 0, uploadComplete: true, percentage: 100, bytesUploaded: 1000, bytesTotal: 1000 }, + size: 1000, + isGhost: false, + isRemote: false, + preview: '', + tus: { uploadUrl: '' }, + bucket: 'test-bucket', + path: 'test-path', + url: 'http://example.com/test.jpg', + ...overrides, +}); + // Mock services class HubspotServiceMock { submitDataToHubspot() { return of({}); } @@ -103,15 +123,7 @@ describe('SupportPopupComponent', () => { it('should return false when selectedFile is truthy', () => { component.problemSubject = ''; component.problemContent = ''; - component.selectedFile = { - bucket: 'test-bucket', - path: 'test-path', - name: 'test-file', - url: 'http://example.com/test.jpg', - extension: 'jpg', - type: 'image/jpeg', - size: 1000 - }; + component.selectedFile = mockUppyFile(); const result = component.isPristine(); @@ -192,15 +204,7 @@ describe('SupportPopupComponent', () => { describe('removeSelectedFile', () => { it('should clear the selected file', fakeAsync(() => { - component.selectedFile = { - bucket: 'test-bucket', - path: 'test-path', - name: 'test-file', - url: 'http://example.com/test.jpg', - extension: 'jpg', - type: 'image/jpeg', - size: 1000, - }; + component.selectedFile = mockUppyFile(); component.removeSelectedFile(); flushMicrotasks(); @@ -210,15 +214,7 @@ describe('SupportPopupComponent', () => { describe('uploadFile', () => { it('should open uppy uploader and set selectedFile on dismiss with data', fakeAsync(() => { - const mockFile = { - bucket: 'test-bucket', - path: 'test-path', - name: 'test.jpg', - url: 'http://example.com/test.jpg', - extension: 'jpg', - type: 'image/jpeg', - size: 1000, - }; + const mockFile = mockUppyFile({ name: 'test.jpg', url: 'http://example.com/test.jpg' }); uppyUploaderSpy.open.and.returnValue(Promise.resolve({ onDidDismiss: () => Promise.resolve({ data: mockFile, role: 'confirm' }), diff --git a/projects/v3/src/app/components/video-conversion/video-conversion.component.spec.ts b/projects/v3/src/app/components/video-conversion/video-conversion.component.spec.ts index 1da48ee5a..baca52219 100644 --- a/projects/v3/src/app/components/video-conversion/video-conversion.component.spec.ts +++ b/projects/v3/src/app/components/video-conversion/video-conversion.component.spec.ts @@ -33,9 +33,14 @@ describe('VideoConversionComponent', () => { expect(component).toBeTruthy(); }); - describe('ngOnInit()', () => { - it('should be a no-op after filestack removal', () => { - component.ngOnInit(); + describe('ngOnChanges()', () => { + it('should not set waitedTooLong for mp4 video', () => { + component.video = { + fileObject: { + mimetype: 'video/mp4', + }, + }; + component.ngOnChanges({} as any); expect(component.waitedTooLong).toBeFalse(); }); }); diff --git a/projects/v3/src/app/fast-feedback/fast-feedback.token.ts b/projects/v3/src/app/fast-feedback/fast-feedback.token.ts new file mode 100644 index 000000000..d9c816895 --- /dev/null +++ b/projects/v3/src/app/fast-feedback/fast-feedback.token.ts @@ -0,0 +1,9 @@ +import { InjectionToken } from '@angular/core'; + +// Define the interface for the modal function +export interface FastFeedbackModalFunction { + createModal: (props: any, options: any) => Promise; +} + +// Create an injection token for the modal function +export const FAST_FEEDBACK_MODAL_TOKEN = new InjectionToken('FastFeedbackModal'); diff --git a/projects/v3/src/app/pages/activity-desktop/activity-desktop.page.ts b/projects/v3/src/app/pages/activity-desktop/activity-desktop.page.ts index fe24bf120..3db26f71e 100644 --- a/projects/v3/src/app/pages/activity-desktop/activity-desktop.page.ts +++ b/projects/v3/src/app/pages/activity-desktop/activity-desktop.page.ts @@ -13,6 +13,7 @@ import { BehaviorSubject, Observable, firstValueFrom } from 'rxjs'; import { delay, filter, tap, distinctUntilChanged, takeUntil, debounceTime } from 'rxjs/operators'; import { TopicComponent } from '@v3/app/components/topic/topic.component'; import { ComponentCleanupService } from '@v3/app/services/component-cleanup.service'; +import { ReviewService } from '../../services/review.service'; const SAVE_PROGRESS_TIMEOUT = 10000; @@ -70,6 +71,7 @@ export class ActivityDesktopPage { private utils: UtilsService, private unlockIndicatorService: UnlockIndicatorService, private componentCleanupService: ComponentCleanupService, + private reviewService: ReviewService, @Inject(DOCUMENT) private readonly document: Document, ) { this.assessment = this.assessmentService.assessment$; diff --git a/projects/v3/src/app/pages/activity-mobile/activity-mobile.page.spec.ts b/projects/v3/src/app/pages/activity-mobile/activity-mobile.page.spec.ts index 655a2a0f6..c166766fc 100644 --- a/projects/v3/src/app/pages/activity-mobile/activity-mobile.page.spec.ts +++ b/projects/v3/src/app/pages/activity-mobile/activity-mobile.page.spec.ts @@ -2,6 +2,8 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { ActivatedRoute, Router } from '@angular/router'; import { ActivityService } from '@v3/services/activity.service'; import { AssessmentService } from '@v3/services/assessment.service'; +import { NotificationsService } from '@v3/services/notifications.service'; +import { UnlockIndicatorService } from '@v3/services/unlock-indicator.service'; import { UtilsService } from '@v3/services/utils.service'; import { IonicModule } from '@ionic/angular'; import { HttpClientTestingModule } from '@angular/common/http/testing'; @@ -18,6 +20,8 @@ describe('ActivityMobilePage', () => { let submission$: Subject; let routerSpy: jasmine.SpyObj; let activityServiceSpy: jasmine.SpyObj; + let unlockIndicatorSpy: jasmine.SpyObj; + let notificationsSpy: jasmine.SpyObj; let utilsSpy: jasmine.SpyObj; beforeEach(waitForAsync(() => { @@ -64,6 +68,19 @@ describe('ActivityMobilePage', () => { getEvent: new Subject(), }), }, + { + provide: NotificationsService, + useValue: jasmine.createSpyObj('NotificationsService', ['markTodoItemAsDone']), + }, + { + provide: UnlockIndicatorService, + useValue: jasmine.createSpyObj('UnlockIndicatorService', [ + 'getTasksByActivityId', + 'clearByActivityId', + 'findRelatedIndicators', + 'clearRelatedIndicators', + ]), + }, ], }).compileComponents(); @@ -71,8 +88,16 @@ describe('ActivityMobilePage', () => { component = fixture.componentInstance; routerSpy = TestBed.inject(Router) as jasmine.SpyObj; activityServiceSpy = TestBed.inject(ActivityService) as jasmine.SpyObj; + unlockIndicatorSpy = TestBed.inject(UnlockIndicatorService) as jasmine.SpyObj; + notificationsSpy = TestBed.inject(NotificationsService) as jasmine.SpyObj; utilsSpy = TestBed.inject(UtilsService) as jasmine.SpyObj; + notificationsSpy.markTodoItemAsDone.and.returnValue(of(true) as any); + unlockIndicatorSpy.getTasksByActivityId.and.returnValue([] as any); + unlockIndicatorSpy.clearByActivityId.and.returnValue([] as any); + unlockIndicatorSpy.findRelatedIndicators.and.returnValue([] as any); + unlockIndicatorSpy.clearRelatedIndicators.and.returnValue([] as any); + fixture.detectChanges(); })); @@ -98,6 +123,46 @@ describe('ActivityMobilePage', () => { expect(utilsSpy.setPageTitle).not.toHaveBeenCalled(); }); + it('should clear pure activity indicator via standard path', () => { + unlockIndicatorSpy.getTasksByActivityId.and.returnValue([{ taskId: undefined }] as any); + unlockIndicatorSpy.clearByActivityId.and.returnValue([{ id: 123 }] as any); + + activity$.next({ id: 1, name: 'Activity A' } as any); + + expect(unlockIndicatorSpy.clearByActivityId).toHaveBeenCalledWith(1); + expect(notificationsSpy.markTodoItemAsDone).toHaveBeenCalledWith(jasmine.objectContaining({ id: 123 })); + }); + + it('should clear pure activity indicator via related-indicator fallback', () => { + unlockIndicatorSpy.getTasksByActivityId.and.returnValue([] as any); + unlockIndicatorSpy.findRelatedIndicators.and.returnValue([ + { taskId: undefined, activityId: 1 }, + ] as any); + unlockIndicatorSpy.clearRelatedIndicators.and.returnValue([{ id: 456 }] as any); + + activity$.next({ id: 1 } as any); + + expect(unlockIndicatorSpy.clearRelatedIndicators).toHaveBeenCalledWith('activity', 1); + expect(notificationsSpy.markTodoItemAsDone).toHaveBeenCalledWith(jasmine.objectContaining({ id: 456 })); + }); + + it('should not clear related indicators when only task-level entries exist', () => { + unlockIndicatorSpy.getTasksByActivityId.and.returnValue([] as any); + unlockIndicatorSpy.findRelatedIndicators.and.returnValue([ + { taskId: 7, activityId: 1 }, + ] as any); + + activity$.next({ id: 1 } as any); + + expect(unlockIndicatorSpy.clearRelatedIndicators).not.toHaveBeenCalled(); + }); + + it('should handle unlock indicator cleanup errors gracefully', () => { + unlockIndicatorSpy.getTasksByActivityId.and.throwError('boom'); + + expect(() => activity$.next({ id: 1 } as any)).not.toThrow(); + }); + it('should navigate to assessment task route', () => { component.activity = { id: 55 } as any; diff --git a/projects/v3/src/app/pages/auth/auth-logout/auth-logout.component.spec.ts b/projects/v3/src/app/pages/auth/auth-logout/auth-logout.component.spec.ts index 41e13f4e3..e78e15896 100644 --- a/projects/v3/src/app/pages/auth/auth-logout/auth-logout.component.spec.ts +++ b/projects/v3/src/app/pages/auth/auth-logout/auth-logout.component.spec.ts @@ -5,7 +5,6 @@ import { Router, ActivatedRoute } from '@angular/router'; // import { NewRelicService } from '@v3/services/new-relic.service'; import { Observable, of } from 'rxjs'; import { RouterTestingModule } from '@angular/router/testing'; -import { doesNotReject } from 'assert'; import { ActivatedRouteStub } from '@testingv3/activated-route-stub'; describe('AuthLogoutComponent', () => { diff --git a/projects/v3/src/app/pages/chat/chat-room/chat-room.component.ts b/projects/v3/src/app/pages/chat/chat-room/chat-room.component.ts index 8d2704d34..621bfefd4 100644 --- a/projects/v3/src/app/pages/chat/chat-room/chat-room.component.ts +++ b/projects/v3/src/app/pages/chat/chat-room/chat-room.component.ts @@ -13,7 +13,8 @@ import { EditMessagePopupComponent } from '../edit-message-popup/edit-message-po import { Subject, timer } from 'rxjs'; import { debounceTime, switchMap, takeUntil, tap } from 'rxjs/operators'; import { QuillModules } from 'ngx-quill'; -import { UppyFileData, UppyUploaderResponse, UppyUploaderService } from '../../../components/uppy-uploader/uppy-uploader.service'; +import { UppyFileData, UppyUploaderResponse } from '../../../components/uppy-uploader/uppy-uploader.service'; +import { ModalService } from '../../../services/modal.service'; enum ScrollPosition { Top = 'top', @@ -158,7 +159,7 @@ export class ChatRoomComponent implements OnInit, OnDestroy, AfterViewInit { public element: ElementRef, private route: ActivatedRoute, public popoverController: PopoverController, - private uppyUploaderService: UppyUploaderService, + private modalService: ModalService, private notificationsService: NotificationsService, @Inject(DOCUMENT) private readonly document: Document ) { @@ -1063,7 +1064,7 @@ export class ChatRoomComponent implements OnInit, OnDestroy, AfterViewInit { } async attachmentSelectPopover(ev: any) { - const modal = await this.uppyUploaderService.open('chat'); + const modal = await this.modalService.openUppyUploaderModal('chat'); const res = await modal.onDidDismiss(); const data: UppyFileData = res.data; diff --git a/projects/v3/src/app/pages/devtool/devtool.page.ts b/projects/v3/src/app/pages/devtool/devtool.page.ts index e6fe857a2..e85d732f0 100644 --- a/projects/v3/src/app/pages/devtool/devtool.page.ts +++ b/projects/v3/src/app/pages/devtool/devtool.page.ts @@ -1,15 +1,13 @@ /* eslint-disable no-console */ import { Component, HostListener, OnInit } from '@angular/core'; import { AuthService } from '@v3/app/services/auth.service'; -import { ExperienceService } from '@v3/app/services/experience.service'; import { FastFeedbackService } from '@v3/app/services/fast-feedback.service'; import { NotificationsService } from '@v3/app/services/notifications.service'; import { BrowserStorageService } from '@v3/app/services/storage.service'; -import { SharedService } from '@v3/app/services/shared.service'; import { UnlockIndicatorService } from '@v3/app/services/unlock-indicator.service'; -import { Achievement, AchievementService } from '@v3/app/services/achievement.service'; -import { environment } from '../../../environments/environment'; +import { AchievementService } from '@v3/app/services/achievement.service'; import { FfmpegService } from '../../services/ffmpeg.service'; +import { ReviewService } from '../../services/review.service'; @Component({ standalone: false, @@ -47,8 +45,7 @@ export class DevtoolPage implements OnInit { private storageService: BrowserStorageService, private fastFeedbackService: FastFeedbackService, private notificationsService: NotificationsService, - private experienceService: ExperienceService, - private sharedService: SharedService, + private reviewService: ReviewService, private unlockIndicatorService: UnlockIndicatorService, private achievementService: AchievementService, private ffmpegService: FfmpegService @@ -202,7 +199,7 @@ export class DevtoolPage implements OnInit { } async reviewrating() { - this.notificationsService.popUpReviewRating(1, false); + this.reviewService.popUpReviewRating(1, false); } async testAuth(withAPIkey?: boolean) { diff --git a/projects/v3/src/app/pages/home/home.page.spec.ts b/projects/v3/src/app/pages/home/home.page.spec.ts index 0716be3f0..c20aa3f28 100644 --- a/projects/v3/src/app/pages/home/home.page.spec.ts +++ b/projects/v3/src/app/pages/home/home.page.spec.ts @@ -1,9 +1,10 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { ActivityService } from '@v3/services/activity.service'; import { AssessmentService } from '@v3/services/assessment.service'; import { UtilsService } from '@v3/services/utils.service'; -import { IonicModule } from '@ionic/angular'; +import { AlertController, IonicModule, ModalController } from '@ionic/angular'; import { AchievementService } from '@v3/app/services/achievement.service'; import { HomeService } from '@v3/app/services/home.service'; import { NotificationsService } from '@v3/app/services/notifications.service'; @@ -11,6 +12,8 @@ import { SharedService } from '@v3/app/services/shared.service'; import { BrowserStorageService } from '@v3/app/services/storage.service'; import { FastFeedbackService } from '@v3/app/services/fast-feedback.service'; import { UnlockIndicatorService } from '@v3/app/services/unlock-indicator.service'; +import { NavigationStateService } from '@v3/app/services/navigation-state.service'; +import { PulsecheckService } from '@v3/app/services/pulsecheck.service'; import { HomePage } from './home.page'; import { of } from 'rxjs'; @@ -52,7 +55,9 @@ describe('HomePage', () => { 'achievements$': of(), }); - const sharedServiceSpy = jasmine.createSpyObj('SharedService', ['refreshJWT']); + const sharedServiceSpy = jasmine.createSpyObj('SharedService', ['refreshJWT'], { + 'team$': of(null) + }); const storageServiceSpy = jasmine.createSpyObj('BrowserStorageService', [ 'get', 'lastVisited', @@ -65,6 +70,7 @@ describe('HomePage', () => { TestBed.configureTestingModule({ declarations: [ HomePage ], imports: [IonicModule.forRoot()], + schemas: [CUSTOM_ELEMENTS_SCHEMA], providers: [ { provide: ActivatedRoute, @@ -112,6 +118,26 @@ describe('HomePage', () => { 'unlockedTasks$': of([]) }) }, + { + provide: NotificationsService, + useValue: jasmine.createSpyObj('NotificationsService', ['getCurrentTodoItems', 'achievementPopUp', 'popUp']) + }, + { + provide: NavigationStateService, + useValue: jasmine.createSpyObj('NavigationStateService', ['setNavigationSource']) + }, + { + provide: PulsecheckService, + useValue: jasmine.createSpyObj('PulsecheckService', ['getSkillChangeDisplayFromValue']) + }, + { + provide: AlertController, + useValue: jasmine.createSpyObj('AlertController', ['create']) + }, + { + provide: ModalController, + useValue: jasmine.createSpyObj('ModalController', ['create', 'dismiss']) + }, ] }).compileComponents(); @@ -125,6 +151,15 @@ describe('HomePage', () => { fastFeedbackService = TestBed.inject(FastFeedbackService) as jasmine.SpyObj; utilsService = TestBed.inject(UtilsService) as jasmine.SpyObj; + // default return values needed by ngOnInit -> updateDashboard + sharedService.refreshJWT.and.returnValue(Promise.resolve()); + storageService.getUser.and.returnValue({ role: 'participant', apikey: 'test-key', projectId: 1, teamId: 1 }); + storageService.get.and.returnValue({ name: 'Default Experience', leadImage: '', description: '', locale: 'en' }); + storageService.getFeature.and.returnValue(false); + storageService.lastVisited.and.returnValue([]); + fastFeedbackService.pullFastFeedback.and.returnValue(of({})); + homeService.getPulseCheckSkills.and.returnValue(of({ success: true, status: 'success', cache: false, data: { pulseCheckSkills: [] } })); + fixture.detectChanges(); })); @@ -135,7 +170,8 @@ describe('HomePage', () => { describe('updateDashboard', () => { beforeEach(() => { sharedService.refreshJWT.and.returnValue(Promise.resolve()); - storageService.get.and.returnValue({ name: 'Test Experience', cardUrl: 'test-url' }); + storageService.get.and.returnValue({ name: 'Test Experience', cardUrl: 'test-url', leadImage: '', description: '', locale: 'en' }); + storageService.getUser.and.returnValue({ role: 'participant', apikey: 'test-key', projectId: 1, teamId: 1 }); storageService.getFeature.and.returnValue(true); achievementService.getIsPointsConfigured.and.returnValue(true); achievementService.getEarnedPoints.and.returnValue(100); @@ -143,7 +179,7 @@ describe('HomePage', () => { success: true, status: 'success', cache: false, - data: { pulseCheckStatus: { red: 1, orange: 2, green: 3 } } + data: { pulseCheckStatus: { self: 1, expert: 2, team: 3, teams: [] } } })); homeService.getPulseCheckSkills.and.returnValue(of({ success: true, @@ -163,7 +199,7 @@ describe('HomePage', () => { it('should get experience from storage', async () => { await component.updateDashboard(); expect(storageService.get).toHaveBeenCalledWith('experience'); - expect(component.experience).toEqual({ name: 'Test Experience', cardUrl: 'test-url' }); + expect(component.experience).toEqual({ name: 'Test Experience', cardUrl: 'test-url', leadImage: '', description: '', locale: 'en' }); }); it('should set project hub visibility from feature toggle', async () => { @@ -211,7 +247,7 @@ describe('HomePage', () => { component.pulseCheckIndicatorEnabled = true; await component.updateDashboard(); expect(homeService.getPulseCheckStatuses).toHaveBeenCalled(); - expect(component.pulseCheckStatus).toEqual({ red: 1, orange: 2, green: 3 }); + expect(component.pulseCheckStatus).toEqual({ self: 1, expert: 2, team: 3, teams: [] }); }); it('should not get pulse check statuses when pulse check indicator is disabled', async () => { @@ -237,7 +273,7 @@ describe('HomePage', () => { }); it('should set empty default lead image when experience has no card URL', async () => { - storageService.get.and.returnValue({ name: 'Test Experience' }); + storageService.get.and.returnValue({ name: 'Test Experience', leadImage: '', description: '', locale: 'en' }); await component.updateDashboard(); expect(component.defaultLeadImage).toBe(''); }); @@ -283,7 +319,7 @@ describe('HomePage', () => { data: { pulseCheckSkills: null } })); await component.updateDashboard(); - expect(component.pulseCheckSkills).toBeNull(); + expect(component.pulseCheckSkills).toEqual([]); }); it('should handle empty pulse check skills response', async () => { diff --git a/projects/v3/src/app/services/achievement.service.ts b/projects/v3/src/app/services/achievement.service.ts index 6b44f9407..0f843a9c9 100644 --- a/projects/v3/src/app/services/achievement.service.ts +++ b/projects/v3/src/app/services/achievement.service.ts @@ -3,9 +3,9 @@ import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable, Subscription, of } from 'rxjs'; import { first, map, shareReplay, takeUntil } from 'rxjs/operators'; import { RequestService } from 'request'; -import { UtilsService } from '@v3/services/utils.service'; import { DemoService } from './demo.service'; import { environment } from '@v3/environments/environment'; +import has from 'lodash-es/has'; /** * @name api @@ -47,7 +47,6 @@ export class AchievementService { constructor( private apolloService: ApolloService, private request: RequestService, - private utils: UtilsService, private demo: DemoService ) {} @@ -115,13 +114,13 @@ export class AchievementService { const achievements: Achievement[] = []; data.forEach((achievement) => { if ( - !this.utils.has(achievement, "id") || - !this.utils.has(achievement, "name") || - !this.utils.has(achievement, "description") || - !this.utils.has(achievement, "badge") || - !this.utils.has(achievement, "points") || - !this.utils.has(achievement, "isEarned") || - !this.utils.has(achievement, "earnedDate") + !has(achievement, "id") || + !has(achievement, "name") || + !has(achievement, "description") || + !has(achievement, "badge") || + !has(achievement, "points") || + !has(achievement, "isEarned") || + !has(achievement, "earnedDate") ) { return this.request.apiResponseFormatError( "Achievement object format error" diff --git a/projects/v3/src/app/services/chat.service.ts b/projects/v3/src/app/services/chat.service.ts index 1fc06a81a..4ada4749e 100644 --- a/projects/v3/src/app/services/chat.service.ts +++ b/projects/v3/src/app/services/chat.service.ts @@ -72,6 +72,7 @@ export interface Message { isSender: boolean; message: string; file: FileResponse; + fileObject?: any; // Adding the missing fileObject property created: string; scheduled: string; sentAt?: string; diff --git a/projects/v3/src/app/services/feedback-modal.service.ts b/projects/v3/src/app/services/feedback-modal.service.ts new file mode 100644 index 000000000..681256597 --- /dev/null +++ b/projects/v3/src/app/services/feedback-modal.service.ts @@ -0,0 +1,58 @@ +import { Injectable } from '@angular/core'; +import { ModalController } from '@ionic/angular'; +import { ModalOptions } from '@ionic/core'; +import { FastFeedbackComponent } from '../components/fast-feedback/fast-feedback.component'; + +export interface Meta { + context_id?: number; + team_id?: number; + target_user_id?: number; + team_name?: string; + assessment_name?: string; +} + +export interface Question { + id?: number; + title?: string; + description?: string; + choices?: Array; +} + +@Injectable({ + providedIn: 'root' +}) +export class FeedbackModalService { + + constructor( + private modalController: ModalController + ) {} + + /** + * Creates a modal for fast feedback + */ + async createFastFeedbackModal( + props: { + questions?: Array; + meta?: Meta | Object; + }, + options: { + closable?: boolean; + modalOnly?: boolean; + backdropDismiss?: boolean; + showBackdrop?: boolean; + [key: string]: any; + } = {} + ): Promise { + const modalConfig: ModalOptions = { + component: FastFeedbackComponent, + componentProps: props, + backdropDismiss: options?.closable === true || options?.backdropDismiss === true, + showBackdrop: options?.showBackdrop !== false, + ...options + }; + + const modal = await this.modalController.create(modalConfig); + await modal.present(); + return modal; + } +} diff --git a/projects/v3/src/app/services/hubspot.service.ts b/projects/v3/src/app/services/hubspot.service.ts index 3291a43f0..318f0dfad 100644 --- a/projects/v3/src/app/services/hubspot.service.ts +++ b/projects/v3/src/app/services/hubspot.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@angular/core'; import { RequestService } from 'request'; import { Observable, map } from 'rxjs'; -import { UtilsService } from '@v3/services/utils.service'; import { BrowserStorageService } from '@v3/services/storage.service'; import { environment } from '@v3/environments/environment'; import { DemoService } from './demo.service'; @@ -57,7 +56,7 @@ export class HubspotService { } generateParams(params: HubspotFormParams) { - if (this.isNotEmptyObject(this.storage.getUser())) { + if (!this.storage.getUser()?.uuid) { // legalConsentOptions is a required param for the hubspot API const submitParam = { fields: [], diff --git a/projects/v3/src/app/services/modal.service.ts b/projects/v3/src/app/services/modal.service.ts index eb1c89721..b79a8e48d 100644 --- a/projects/v3/src/app/services/modal.service.ts +++ b/projects/v3/src/app/services/modal.service.ts @@ -1,6 +1,9 @@ import { Injectable } from '@angular/core'; import { ModalController } from '@ionic/angular'; +import { UppyUploaderComponent } from '../components/uppy-uploader/uppy-uploader.component'; +// type for the source parameter, matching the original method signature +export type UppyModalSource = 'chat' | 'user-profile' | 'assessment' | 'media-manager' | 'static' | null; @Injectable({ providedIn: 'root' }) @@ -60,4 +63,24 @@ export class ModalService { return await modal.present(); } + + /** + * opens a modal with the uppyuploadercomponent. + * @param source the context or type of upload. + * @return a promise that resolves with the modal element. + */ + async openUppyUploaderModal(source: UppyModalSource): Promise { + const modal = await this.modalController.create({ + component: UppyUploaderComponent, + componentProps: { + // 'source' will be passed to uppyuploadercomponent's @input() source. + // there's an existing type mismatch between uppymodalsource and the component's expected source type. + // using 'as any' to bypass this for now, as in the previous implementation. + source: source as any + }, + cssClass: 'uppy-uploader-modal', + }); + await modal.present(); + return modal; + } } diff --git a/projects/v3/src/app/services/notifications.service.ts b/projects/v3/src/app/services/notifications.service.ts index 325c64d0a..f6ec793ca 100644 --- a/projects/v3/src/app/services/notifications.service.ts +++ b/projects/v3/src/app/services/notifications.service.ts @@ -4,9 +4,8 @@ import { AlertOptions, ToastOptions, ModalOptions, LoadingOptions } from '@ionic import { PopUpComponent } from '../components/pop-up/pop-up.component'; import { AchievementPopUpComponent } from '../components/achievement-pop-up/achievement-pop-up.component'; import { ActivityCompletePopUpComponent } from '../components/activity-complete-pop-up/activity-complete-pop-up.component'; -import { Achievement, AchievementService } from './achievement.service'; +import { Achievement } from './achievement.service'; import { UtilsService } from '@v3/services/utils.service'; -import { ReviewRatingComponent } from '../components/review-rating/review-rating.component'; import { LockTeamAssessmentPopUpComponent } from '../components/lock-team-assessment-pop-up/lock-team-assessment-pop-up.component'; import { firstValueFrom, Observable, of, Subject } from 'rxjs'; import { RequestService } from 'request'; @@ -148,8 +147,7 @@ export class NotificationsService { private alertController: AlertController, private toastController: ToastController, private loadingController: LoadingController, - readonly achievementService: AchievementService, - readonly utils: UtilsService, + private utils: UtilsService, private request: RequestService, private storage: BrowserStorageService, private apolloService: ApolloService, @@ -180,8 +178,8 @@ export class NotificationsService { }); } - dismiss() { - return this.modalController.dismiss(); + dismiss(...args: any[]) { + return this.modalController.dismiss(...args); } get notificationsCount(): number { @@ -459,6 +457,9 @@ export class NotificationsService { return; } + // use dynamic import to avoid circular dependency + const { ReviewRatingComponent } = await import('../components/review-rating/review-rating.component'); + return this.modalOnly( ReviewRatingComponent, { @@ -489,13 +490,13 @@ export class NotificationsService { modalOnly: false, } ): Promise { + // use dynamic import to avoid circular dependency + const { FastFeedbackComponent } = await import('../components/fast-feedback/fast-feedback.component'); + const cssClass = this.utils.isMobile() ? 'modal-fullscreen' : ''; - // lazy import to break circular dependency with FastFeedbackService - const { FastFeedbackComponent } = await import('../components/fast-feedback/fast-feedback.component'); - const modalConfig = { cssClass, backdropDismiss: options?.closable === true, diff --git a/projects/v3/src/app/services/review.service.spec.ts b/projects/v3/src/app/services/review.service.spec.ts new file mode 100644 index 000000000..7d45f8b36 --- /dev/null +++ b/projects/v3/src/app/services/review.service.spec.ts @@ -0,0 +1,130 @@ +import { TestBed } from '@angular/core/testing'; +import { ReviewService } from './review.service'; +import { of } from 'rxjs'; +import { RequestService } from 'request'; +import { UtilsService } from '@v3/services/utils.service'; +import { TestUtils } from '@testingv3/utils'; +import { DemoService } from './demo.service'; +import { NotificationsService } from './notifications.service'; + +describe('ReviewService', () => { + let service: ReviewService; + let requestSpy: jasmine.SpyObj; + let notificationSpy: jasmine.SpyObj; + let utils: UtilsService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + ReviewService, + { + provide: UtilsService, + useClass: TestUtils, + }, + { + provide: DemoService, + useValue: jasmine.createSpyObj('DemoService', ['getReviews']), + }, + { + provide: NotificationsService, + useValue: jasmine.createSpyObj('NotificationsService', ['modalOnly']), + }, + { + provide: RequestService, + useValue: jasmine.createSpyObj('RequestService', [ + 'get', 'apiResponseFormatError' + ]), + }, + ] + }); + service = TestBed.inject(ReviewService); + requestSpy = TestBed.inject(RequestService) as jasmine.SpyObj; + utils = TestBed.inject(UtilsService); + notificationSpy = TestBed.inject(NotificationsService) as jasmine.SpyObj; + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + describe('getReviews()', () => { + it('should return empty array if no response data.', () => { + requestSpy.get.and.returnValue(of({})); + expect(service.getReviews).toThrowError(); + }); + + it('should return error if response data format error #1.', () => { + requestSpy.get.and.returnValue(of({ + success: true, + data: {} + })); + expect(service.getReviews).toThrowError(); + }); + + it('should return error if response data format error #2.', () => { + requestSpy.get.and.returnValue(of({ + success: true, + data: [{ + Assessment: {} + }] + })); + service.getReviews(); + service.reviews$.subscribe(); + expect(requestSpy.apiResponseFormatError.calls.count()).toBe(1); + }); + + it('should get correct data.', () => { + requestSpy.get.and.returnValue(of({ + success: true, + data: [{ + Assessment: { + id: 1, + name: 'assessment' + }, + AssessmentSubmission: { + id: 2, + context_id: 3, + Submitter: { name: 'submitter' }, + Team: { name: 'team' } + }, + AssessmentReview: { + is_done: true, + created: '2019-01-01 18:00:00', + modified: '2019-01-01 19:00:00' + } + }] + })); + service.getReviews(); + service.reviews$.subscribe(res => { + expect(res).toEqual([{ + assessmentId: 1, + submissionId: 2, + isDone: true, + name: 'assessment', + submitterName: 'submitter', + date: utils.timeFormatter('2019-01-01 19:00:00'), + teamName: 'team', + contextId: 3, + }]); + }); + }); + }); + + describe('when testing popUpReviewRating()', () => { + it('should pass the correct data to notification modal', () => { + service.popUpReviewRating(1, ['home']); + expect(notificationSpy.modalOnly).toHaveBeenCalledTimes(1); + expect(notificationSpy.modalOnly).toHaveBeenCalledWith( + jasmine.any(Function), // ReviewRatingComponent + { + reviewId: 1, + redirect: ['home'] + }, + { + id: 'review-popup-1', + backdropDismiss: false, + } + ); + }); + }); +}); diff --git a/projects/v3/src/app/services/review.service.ts b/projects/v3/src/app/services/review.service.ts index 504a6b710..da3d8c97f 100644 --- a/projects/v3/src/app/services/review.service.ts +++ b/projects/v3/src/app/services/review.service.ts @@ -5,6 +5,8 @@ import { UtilsService } from '@v3/services/utils.service'; import { DemoService } from './demo.service'; import { environment } from '@v3/environments/environment'; import { shareReplay } from 'rxjs/operators'; +import { ReviewRatingComponent } from '../components/review-rating/review-rating.component'; +import { NotificationsService } from './notifications.service'; const api = { reviews: 'api/reviews.json', @@ -36,6 +38,7 @@ export class ReviewService { private request: RequestService, private utils: UtilsService, private demoService: DemoService, + private notificationService: NotificationsService, ) { } getReviews() { @@ -77,4 +80,29 @@ export class ReviewService { return reviews; } + /** + * trigger reviewer rating modal + * + * @param {number} reviewId submission review record id + * @param {string[]} redirect array: routeUrl, boolean: disable + * routing (stay at same component) + * + * @return {Promise} deferred ionic modal + */ + async popUpReviewRating( + reviewId, + redirect: string[] | boolean + ): Promise { + return this.notificationService.modalOnly( + ReviewRatingComponent, + { + reviewId, + redirect, + }, + { + id: `review-popup-${reviewId}`, + backdropDismiss: false, + } + ); + } } diff --git a/projects/v3/src/test.ts b/projects/v3/src/test.ts index b0a23e9b9..f7b426772 100644 --- a/projects/v3/src/test.ts +++ b/projects/v3/src/test.ts @@ -11,14 +11,6 @@ import { import { ModalController, PopoverController, AngularDelegate } from '@ionic/angular'; import { Apollo } from 'apollo-angular'; -declare const require: { - context(path: string, deep?: boolean, filter?: RegExp): { - (id: string): T; - keys(): string[]; - }; -}; - -// First, initialize the Angular testing environment. getTestBed().initTestEnvironment( BrowserDynamicTestingModule, platformBrowserDynamicTesting(), diff --git a/projects/v3/tsconfig.spec.json b/projects/v3/tsconfig.spec.json index 5c3db3df5..42b1ce88d 100644 --- a/projects/v3/tsconfig.spec.json +++ b/projects/v3/tsconfig.spec.json @@ -1,12 +1,9 @@ -/* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "../../tsconfig.json", "compilerOptions": { "emitDecoratorMetadata": true, "outDir": "../../out-tsc/spec", - "types": [ - "jasmine" - ] + "types": ["jasmine"] }, "files": [ "src/test.ts", @@ -15,11 +12,10 @@ "include": [ "src/testing", "src/**/*.spec.ts", - "src/**/*.d.ts", + "src/**/*.d.ts" ], "exclude": [ "../../node_modules", - "../../dist", - "../../src" + "../../dist" ] }