@transcommerce/cwm-shared 1.1.88 → 1.1.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/transcommerce-cwm-shared.mjs +814 -143
- package/fesm2022/transcommerce-cwm-shared.mjs.map +1 -1
- package/lib/components/external-navigation/external-navigation.component.d.ts +1 -1
- package/lib/components/navigate-to-route/navigate-to-route.component.d.ts +1 -1
- package/lib/components/page-not-found/page-not-found.component.d.ts +1 -1
- package/lib/cwm-shared-standalone.d.ts +19 -0
- package/lib/helpers/async-utils.d.ts +31 -3
- package/lib/helpers/jwt.d.ts +35 -0
- package/lib/helpers/time-span.d.ts +344 -0
- package/lib/helpers/utilities.d.ts +301 -0
- package/lib/modules/cwm-shared.module.d.ts +25 -10
- package/lib/pipes/safe-html.pipe.d.ts +1 -1
- package/lib/services/local-storage.service.d.ts +2 -0
- package/package.json +1 -1
- package/public-api.d.ts +9 -9
- package/lib/helpers/db-keys.d.ts +0 -18
- package/lib/helpers/time-span-overflow-error.d.ts +0 -2
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Component, Injectable, InjectionToken, Pipe,
|
|
3
|
-
import * as i1 from '@angular/router';
|
|
4
|
-
import { RouterModule } from '@angular/router';
|
|
2
|
+
import { Component, Injectable, InjectionToken, Pipe, Inject, NgModule } from '@angular/core';
|
|
5
3
|
import * as i2 from '@angular/forms';
|
|
6
4
|
import { FormsModule } from '@angular/forms';
|
|
5
|
+
import * as i1 from '@angular/router';
|
|
6
|
+
import { RouterModule } from '@angular/router';
|
|
7
7
|
import { Subject, firstValueFrom } from 'rxjs';
|
|
8
|
-
import {
|
|
8
|
+
import { PublicClientApplication, LogLevel, InteractionType } from '@azure/msal-browser';
|
|
9
9
|
import * as i2$1 from '@angular/common/http';
|
|
10
10
|
import { HttpResponseBase, HttpResponse, HttpErrorResponse, HttpHeaders, HttpStatusCode } from '@angular/common/http';
|
|
11
|
-
import { CommonModule, DOCUMENT } from '@angular/common';
|
|
12
|
-
export * from '@angular/common';
|
|
13
|
-
import { MsalModule } from '@azure/msal-angular';
|
|
14
|
-
export * from '@azure/msal-angular';
|
|
15
11
|
import * as i1$1 from '@angular/platform-browser';
|
|
16
12
|
import { BrowserModule } from '@angular/platform-browser';
|
|
13
|
+
import { DOCUMENT, CommonModule } from '@angular/common';
|
|
14
|
+
export * from '@angular/common';
|
|
17
15
|
import { AppConfigurationClient } from '@azure/app-configuration';
|
|
18
16
|
import { keys, countBy } from 'lodash';
|
|
17
|
+
import { MsalModule } from '@azure/msal-angular';
|
|
18
|
+
export * from '@azure/msal-angular';
|
|
19
19
|
|
|
20
20
|
class ExternalNavigationComponent {
|
|
21
21
|
router;
|
|
@@ -49,7 +49,7 @@ class ExternalNavigationComponent {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: ExternalNavigationComponent, deps: [{ token: i1.Router }], target: i0.ɵɵFactoryTarget.Component });
|
|
52
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.19", type: ExternalNavigationComponent, isStandalone:
|
|
52
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.19", type: ExternalNavigationComponent, isStandalone: true, selector: "cwm-external-navigation", ngImport: i0, template: `<div class="container">
|
|
53
53
|
<h3>Enter the destination route, URL, or Href you want and press the button that uses the navigation method of your choosing:</h3>
|
|
54
54
|
<input id="txtRoute" type="text" [(ngModel)]="route" placeholder="Enter the destination route, URL, or Href..."
|
|
55
55
|
(keydown)="onKeyPress($event)"/>
|
|
@@ -58,13 +58,14 @@ class ExternalNavigationComponent {
|
|
|
58
58
|
<button (click)="navigateByUrl()">Navigate by URL</button>
|
|
59
59
|
<button (click)="navigateToExternalUrl()">Navigate to external URL</button>
|
|
60
60
|
<button (click)="navigateToExternalHref()">Navigate to external Href</button>
|
|
61
|
-
</div>`, isInline: true, dependencies: [{ kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
61
|
+
</div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
62
62
|
}
|
|
63
63
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: ExternalNavigationComponent, decorators: [{
|
|
64
64
|
type: Component,
|
|
65
65
|
args: [{
|
|
66
66
|
selector: 'cwm-external-navigation',
|
|
67
|
-
standalone:
|
|
67
|
+
standalone: true,
|
|
68
|
+
imports: [FormsModule],
|
|
68
69
|
template: `<div class="container">
|
|
69
70
|
<h3>Enter the destination route, URL, or Href you want and press the button that uses the navigation method of your choosing:</h3>
|
|
70
71
|
<input id="txtRoute" type="text" [(ngModel)]="route" placeholder="Enter the destination route, URL, or Href..."
|
|
@@ -98,11 +99,11 @@ class NavigateToRouteComponent {
|
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
101
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: NavigateToRouteComponent, deps: [{ token: i1.Router }], target: i0.ɵɵFactoryTarget.Component });
|
|
101
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.19", type: NavigateToRouteComponent, isStandalone:
|
|
102
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.19", type: NavigateToRouteComponent, isStandalone: true, selector: "cwm-navigate-to-route", ngImport: i0, template: "<div class=\"container\">\n <h2>Enter the route you want to navigate to:</h2>\n <input id=\"txtRoute\" type=\"text\" [(ngModel)]=\"route\" placeholder=\"Enter Route...\"\n (keydown)=\"onKeyPress($event)\"/>\n <button (click)=\"navigateToRoute()\">Navigate</button>\n</div>\n``\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
102
103
|
}
|
|
103
104
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: NavigateToRouteComponent, decorators: [{
|
|
104
105
|
type: Component,
|
|
105
|
-
args: [{ selector: 'cwm-navigate-to-route', standalone:
|
|
106
|
+
args: [{ selector: 'cwm-navigate-to-route', standalone: true, imports: [FormsModule], template: "<div class=\"container\">\n <h2>Enter the route you want to navigate to:</h2>\n <input id=\"txtRoute\" type=\"text\" [(ngModel)]=\"route\" placeholder=\"Enter Route...\"\n (keydown)=\"onKeyPress($event)\"/>\n <button (click)=\"navigateToRoute()\">Navigate</button>\n</div>\n``\n", styles: [":host{display:block}\n"] }]
|
|
106
107
|
}], ctorParameters: () => [{ type: i1.Router }] });
|
|
107
108
|
|
|
108
109
|
class PageNotFoundComponent {
|
|
@@ -135,11 +136,11 @@ class PageNotFoundComponent {
|
|
|
135
136
|
}, 1000);
|
|
136
137
|
}
|
|
137
138
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: PageNotFoundComponent, deps: [{ token: i1.Router }], target: i0.ɵɵFactoryTarget.Component });
|
|
138
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.19", type: PageNotFoundComponent, isStandalone:
|
|
139
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.19", type: PageNotFoundComponent, isStandalone: true, selector: "cwm-page-not-found", ngImport: i0, template: "<div class=\"not-found-container\">\n <div class=\"not-found-card\">\n <div class=\"emoji-icon\">\uD83D\uDD0E</div>\n <div class=\"error-code\">404</div>\n <h1 class=\"error-title\">Page Not Found</h1>\n <p class=\"error-description\">\n The page you're looking for doesn't exist or has been moved. Let's get you back on track!\n </p>\n\n <div class=\"button-group\">\n <a routerLink=\"/\" class=\"btn btn-primary\">\uD83C\uDFE0 Go to Home</a>\n <a routerLink=\"/dashboard\" class=\"btn btn-secondary\">\uD83D\uDCCA Dashboard</a>\n <a href=\"javascript:history.back()\" class=\"btn btn-outline\">\u2190 Go Back</a>\n <a href=\"mailto:support@cheapweedmenu.com?subject=Broken%20link%20-%20404&body=I%20found%20a%20broken%20link\" class=\"btn btn-ghost\">\uD83D\uDCE7 Report Issue</a>\n </div>\n\n <div class=\"redirect-info\">\n <small class=\"redirect-timer\">You will be redirected to home in <span class=\"timer-badge\">{{ redirectCountdown }}</span> seconds</small>\n <div class=\"breadcrumb\">\n Requested URL: <code>{{ requestedUrl }}</code>\n </div>\n </div>\n </div>\n</div>\n", styles: [".menu{color:#fff}.menu-bg{background-color:#333}.hybrid{color:#375b2b}.hybrid-bg{background-color:#375b2b}.shake{color:#4e544c}.shake-bg{background-color:#4e544c}.indica{color:#1c2e16}.indica-bg{background-color:#1c2e16}.sativa{color:#d7d0c4}.sativa-bg{background-color:#d7d0c4}@keyframes float{0%,to{transform:translateY(0)}50%{transform:translateY(-20px)}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}.not-found-container{min-height:70vh;display:flex;align-items:center;justify-content:center;padding:24px;background:linear-gradient(135deg,#1c2e16,#375b2b)}.not-found-card{max-width:720px;width:100%;text-align:center;border-radius:12px;box-shadow:0 8px 30px #0000004d;padding:36px;background:linear-gradient(180deg,#d7d0c4,#e8e2d8);animation:fadeInUp .6s ease-out}.emoji-icon{font-size:80px;line-height:1;margin-bottom:16px;animation:float 3s ease-in-out infinite;display:inline-block}.error-code{margin:0;font-size:48px;font-weight:700;color:#1c2e16;letter-spacing:-1px}.error-title{margin:8px 0;font-size:28px;font-weight:600;color:#1c2e16}.error-description{color:#4e544c;margin:12px 0 28px;line-height:1.6;font-size:16px}.button-group{display:flex;gap:12px;justify-content:center;flex-wrap:wrap;margin:32px 0}.btn{padding:12px 24px;border-radius:8px;text-decoration:none;font-weight:600;font-size:14px;transition:all .5s ease;cursor:pointer;border:none;display:inline-block}.btn-primary{background-color:#1c2e16;color:#d7d0c4;transition-property:all;transition-duration:.5s}.btn-primary:hover{background-color:#160816;color:#d7d0c4;transform:translateY(-2px);box-shadow:0 4px 12px #1c2e1666}.btn-secondary{background-color:#375b2b;color:#d7d0c4;transition-property:all;transition-duration:.5s}.btn-secondary:hover{background-color:#1c2e16;color:#d7d0c4;transform:translateY(-2px)}.btn-outline{background-color:#d7d0c4;color:#1c2e16;transition-property:all;transition-duration:.5s}.btn-outline:hover{background-color:#1c2e16;color:#d7d0c4;transform:translateY(-2px)}.btn-ghost{background:transparent;border:2px solid #1c2e16;color:#1c2e16;transition-property:all;transition-duration:.5s}.btn-ghost:hover{border-color:#375b2b;background-color:#375b2b;color:#d7d0c4;transform:translateY(-2px)}.redirect-info{margin-top:24px;padding-top:24px;border-top:2px solid #1c2e16}.redirect-timer{font-size:14px;color:#4e544c;margin-top:12px}.timer-badge{display:inline-block;background-color:#1c2e16;color:#d7d0c4;padding:4px 10px;border-radius:4px;font-weight:600;margin-left:4px}.breadcrumb{margin-top:20px;font-size:12px;color:#4e544c}.breadcrumb code{background-color:#1c2e16;color:#d7d0c4;padding:2px 6px;border-radius:4px}\n"] });
|
|
139
140
|
}
|
|
140
141
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: PageNotFoundComponent, decorators: [{
|
|
141
142
|
type: Component,
|
|
142
|
-
args: [{ selector: 'cwm-page-not-found', standalone:
|
|
143
|
+
args: [{ selector: 'cwm-page-not-found', standalone: true, imports: [], template: "<div class=\"not-found-container\">\n <div class=\"not-found-card\">\n <div class=\"emoji-icon\">\uD83D\uDD0E</div>\n <div class=\"error-code\">404</div>\n <h1 class=\"error-title\">Page Not Found</h1>\n <p class=\"error-description\">\n The page you're looking for doesn't exist or has been moved. Let's get you back on track!\n </p>\n\n <div class=\"button-group\">\n <a routerLink=\"/\" class=\"btn btn-primary\">\uD83C\uDFE0 Go to Home</a>\n <a routerLink=\"/dashboard\" class=\"btn btn-secondary\">\uD83D\uDCCA Dashboard</a>\n <a href=\"javascript:history.back()\" class=\"btn btn-outline\">\u2190 Go Back</a>\n <a href=\"mailto:support@cheapweedmenu.com?subject=Broken%20link%20-%20404&body=I%20found%20a%20broken%20link\" class=\"btn btn-ghost\">\uD83D\uDCE7 Report Issue</a>\n </div>\n\n <div class=\"redirect-info\">\n <small class=\"redirect-timer\">You will be redirected to home in <span class=\"timer-badge\">{{ redirectCountdown }}</span> seconds</small>\n <div class=\"breadcrumb\">\n Requested URL: <code>{{ requestedUrl }}</code>\n </div>\n </div>\n </div>\n</div>\n", styles: [".menu{color:#fff}.menu-bg{background-color:#333}.hybrid{color:#375b2b}.hybrid-bg{background-color:#375b2b}.shake{color:#4e544c}.shake-bg{background-color:#4e544c}.indica{color:#1c2e16}.indica-bg{background-color:#1c2e16}.sativa{color:#d7d0c4}.sativa-bg{background-color:#d7d0c4}@keyframes float{0%,to{transform:translateY(0)}50%{transform:translateY(-20px)}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}.not-found-container{min-height:70vh;display:flex;align-items:center;justify-content:center;padding:24px;background:linear-gradient(135deg,#1c2e16,#375b2b)}.not-found-card{max-width:720px;width:100%;text-align:center;border-radius:12px;box-shadow:0 8px 30px #0000004d;padding:36px;background:linear-gradient(180deg,#d7d0c4,#e8e2d8);animation:fadeInUp .6s ease-out}.emoji-icon{font-size:80px;line-height:1;margin-bottom:16px;animation:float 3s ease-in-out infinite;display:inline-block}.error-code{margin:0;font-size:48px;font-weight:700;color:#1c2e16;letter-spacing:-1px}.error-title{margin:8px 0;font-size:28px;font-weight:600;color:#1c2e16}.error-description{color:#4e544c;margin:12px 0 28px;line-height:1.6;font-size:16px}.button-group{display:flex;gap:12px;justify-content:center;flex-wrap:wrap;margin:32px 0}.btn{padding:12px 24px;border-radius:8px;text-decoration:none;font-weight:600;font-size:14px;transition:all .5s ease;cursor:pointer;border:none;display:inline-block}.btn-primary{background-color:#1c2e16;color:#d7d0c4;transition-property:all;transition-duration:.5s}.btn-primary:hover{background-color:#160816;color:#d7d0c4;transform:translateY(-2px);box-shadow:0 4px 12px #1c2e1666}.btn-secondary{background-color:#375b2b;color:#d7d0c4;transition-property:all;transition-duration:.5s}.btn-secondary:hover{background-color:#1c2e16;color:#d7d0c4;transform:translateY(-2px)}.btn-outline{background-color:#d7d0c4;color:#1c2e16;transition-property:all;transition-duration:.5s}.btn-outline:hover{background-color:#1c2e16;color:#d7d0c4;transform:translateY(-2px)}.btn-ghost{background:transparent;border:2px solid #1c2e16;color:#1c2e16;transition-property:all;transition-duration:.5s}.btn-ghost:hover{border-color:#375b2b;background-color:#375b2b;color:#d7d0c4;transform:translateY(-2px)}.redirect-info{margin-top:24px;padding-top:24px;border-top:2px solid #1c2e16}.redirect-timer{font-size:14px;color:#4e544c;margin-top:12px}.timer-badge{display:inline-block;background-color:#1c2e16;color:#d7d0c4;padding:4px 10px;border-radius:4px;font-weight:600;margin-left:4px}.breadcrumb{margin-top:20px;font-size:12px;color:#4e544c}.breadcrumb code{background-color:#1c2e16;color:#d7d0c4;padding:2px 6px;border-radius:4px}\n"] }]
|
|
143
144
|
}], ctorParameters: () => [{ type: i1.Router }] });
|
|
144
145
|
|
|
145
146
|
/*
|
|
@@ -346,29 +347,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.19", ngImpo
|
|
|
346
347
|
type: Injectable
|
|
347
348
|
}], ctorParameters: () => [] });
|
|
348
349
|
|
|
349
|
-
function msalGuardConfigFactory(authConfig, injector) {
|
|
350
|
-
const logService = injector ? injector.get(ClassLoggerService, null) : null;
|
|
351
|
-
if (logService) {
|
|
352
|
-
//logService.className = 'AppModule';
|
|
353
|
-
logService.methodName = 'msalGuardConfigFactory';
|
|
354
|
-
logService.info(`msalGuardConfigFactory: using clientId=${authConfig.clientId}`);
|
|
355
|
-
}
|
|
356
|
-
else {
|
|
357
|
-
console.info('msalGuardConfigFactory: auth clientId=', authConfig.clientId);
|
|
358
|
-
}
|
|
359
|
-
const scopes = authConfig && authConfig.scopes ? (Array.isArray(authConfig.scopes) ? authConfig.scopes : authConfig.scopes.split(',').map(s => s.trim())) : ['user.read'];
|
|
360
|
-
const msalGuardConfiguration = {
|
|
361
|
-
interactionType: InteractionType.Redirect,
|
|
362
|
-
authRequest: {
|
|
363
|
-
scopes: scopes,
|
|
364
|
-
prompt: authConfig.prompt
|
|
365
|
-
},
|
|
366
|
-
};
|
|
367
|
-
if (logService)
|
|
368
|
-
logService.methodName = "";
|
|
369
|
-
return msalGuardConfiguration;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
350
|
function msalInstanceFactory(authConfig, injector) {
|
|
373
351
|
const logService = injector ? injector.get(ClassLoggerService, null) : null;
|
|
374
352
|
if (logService) {
|
|
@@ -467,6 +445,29 @@ function msalInstanceFactory(authConfig, injector) {
|
|
|
467
445
|
return publicClientApplication;
|
|
468
446
|
}
|
|
469
447
|
|
|
448
|
+
function msalGuardConfigFactory(authConfig, injector) {
|
|
449
|
+
const logService = injector ? injector.get(ClassLoggerService, null) : null;
|
|
450
|
+
if (logService) {
|
|
451
|
+
//logService.className = 'AppModule';
|
|
452
|
+
logService.methodName = 'msalGuardConfigFactory';
|
|
453
|
+
logService.info(`msalGuardConfigFactory: using clientId=${authConfig.clientId}`);
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
console.info('msalGuardConfigFactory: auth clientId=', authConfig.clientId);
|
|
457
|
+
}
|
|
458
|
+
const scopes = authConfig && authConfig.scopes ? (Array.isArray(authConfig.scopes) ? authConfig.scopes : authConfig.scopes.split(',').map(s => s.trim())) : ['user.read'];
|
|
459
|
+
const msalGuardConfiguration = {
|
|
460
|
+
interactionType: InteractionType.Redirect,
|
|
461
|
+
authRequest: {
|
|
462
|
+
scopes: scopes,
|
|
463
|
+
prompt: authConfig.prompt
|
|
464
|
+
},
|
|
465
|
+
};
|
|
466
|
+
if (logService)
|
|
467
|
+
logService.methodName = "";
|
|
468
|
+
return msalGuardConfiguration;
|
|
469
|
+
}
|
|
470
|
+
|
|
470
471
|
function msalInterceptorConfigFactory(authConfig, injector) {
|
|
471
472
|
const logService = injector ? injector.get(ClassLoggerService, null) : null;
|
|
472
473
|
if (logService) {
|
|
@@ -495,12 +496,23 @@ function msalInterceptorConfigFactory(authConfig, injector) {
|
|
|
495
496
|
|
|
496
497
|
/* eslint-disable no-useless-catch */
|
|
497
498
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
499
|
+
/**
|
|
500
|
+
* @module async-utils
|
|
501
|
+
* @description Utility functions for working with asynchronous code and managing concurrent task execution.
|
|
502
|
+
* Provides utilities for delays, promise management, and synchronization primitives for controlling
|
|
503
|
+
* the execution of async tasks to ensure only one task executes at a time for a given lock name.
|
|
504
|
+
*/
|
|
498
505
|
/**
|
|
499
506
|
* A wrapper around setTimeout which returns a promise. Useful for waiting for an amount of
|
|
500
507
|
* time from an async function. e.g. await waitFor(1000);
|
|
501
508
|
*
|
|
502
509
|
* @param milliseconds The amount of time to wait.
|
|
503
510
|
* @returns A promise that resolves once the given number of milliseconds has ellapsed.
|
|
511
|
+
*
|
|
512
|
+
* @example
|
|
513
|
+
* // Wait for 2 seconds before continuing
|
|
514
|
+
* await waitFor(2000);
|
|
515
|
+
* console.log('2 seconds have passed');
|
|
504
516
|
*/
|
|
505
517
|
function waitFor(milliseconds) {
|
|
506
518
|
return new Promise((resolve) => {
|
|
@@ -519,9 +531,26 @@ const locksByName = {};
|
|
|
519
531
|
* lock name for a group of tasks you can ensure the only one task will ever be in progress
|
|
520
532
|
* at a given time.
|
|
521
533
|
*
|
|
522
|
-
*
|
|
523
|
-
*
|
|
524
|
-
*
|
|
534
|
+
* Uses a queue-based locking mechanism where tasks are executed in FIFO order. Each task must
|
|
535
|
+
* complete (or throw an error) before the next task in the queue begins execution.
|
|
536
|
+
*
|
|
537
|
+
* @param lockName The name of the lock to be obtained. Tasks with the same lock name will be serialized.
|
|
538
|
+
* @param task The task to execute. Should return a Promise.
|
|
539
|
+
* @returns The value returned by the task. Rejects if the task throws an error.
|
|
540
|
+
*
|
|
541
|
+
* @example
|
|
542
|
+
* // Execute multiple API calls sequentially to prevent race conditions
|
|
543
|
+
* const result1 = await doWithLock('api-calls', async () => {
|
|
544
|
+
* return await fetchUser(userId);
|
|
545
|
+
* });
|
|
546
|
+
*
|
|
547
|
+
* @example
|
|
548
|
+
* // Multiple locks can run in parallel, but tasks with the same lock name are serialized
|
|
549
|
+
* await Promise.all([
|
|
550
|
+
* doWithLock('lock-a', () => slowTask1()),
|
|
551
|
+
* doWithLock('lock-a', () => slowTask2()), // Waits for slowTask1 to complete
|
|
552
|
+
* doWithLock('lock-b', () => otherTask()) // Runs in parallel with the lock-a tasks
|
|
553
|
+
* ]);
|
|
525
554
|
*/
|
|
526
555
|
async function doWithLock(lockName, task) {
|
|
527
556
|
// Ensure array present for the given lock name.
|
|
@@ -562,27 +591,20 @@ async function doWithLock(lockName, task) {
|
|
|
562
591
|
}
|
|
563
592
|
}
|
|
564
593
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: DbKeys, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
580
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: DbKeys });
|
|
581
|
-
}
|
|
582
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: DbKeys, decorators: [{
|
|
583
|
-
type: Injectable
|
|
584
|
-
}] });
|
|
585
|
-
|
|
594
|
+
/**
|
|
595
|
+
* Decodes a JWT (JSON Web Token) and returns the decoded claims as an object.
|
|
596
|
+
*
|
|
597
|
+
* JWT tokens consist of three Base64-encoded parts separated by dots: header.payload.signature.
|
|
598
|
+
* This function decodes all parts and merges them into a single object, returning the combined result.
|
|
599
|
+
*
|
|
600
|
+
* @param token The JWT token string to decode. Can be a complete token or any of its parts.
|
|
601
|
+
* @returns An object containing the decoded claims from the token parts, or undefined if the token is empty or invalid.
|
|
602
|
+
*
|
|
603
|
+
* @example
|
|
604
|
+
* const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
|
|
605
|
+
* const decoded = decodeToken(token);
|
|
606
|
+
* // Returns: { alg: 'HS256', typ: 'JWT', sub: '1234567890', name: 'John Doe', iat: 1516239022 }
|
|
607
|
+
*/
|
|
586
608
|
function decodeToken(token) {
|
|
587
609
|
if (!token) {
|
|
588
610
|
return;
|
|
@@ -604,6 +626,27 @@ function decodeToken(token) {
|
|
|
604
626
|
return acc;
|
|
605
627
|
}, Object.create(null));
|
|
606
628
|
}
|
|
629
|
+
/**
|
|
630
|
+
* Validates whether a JWT token is still valid based on its expiration time.
|
|
631
|
+
*
|
|
632
|
+
* This function checks the 'exp' (expiration) claim of a JWT token. The expiration time is
|
|
633
|
+
* expected to be a Unix timestamp (seconds since epoch). If the current time is less than the
|
|
634
|
+
* expiration time, the token is considered valid.
|
|
635
|
+
*
|
|
636
|
+
* @param input Either a JWT token string (from which the expiration will be extracted) or
|
|
637
|
+
* a Unix timestamp (expiration time in seconds). If invalid or undefined, returns false.
|
|
638
|
+
* @returns true if the token/timestamp has not yet expired, false otherwise.
|
|
639
|
+
*
|
|
640
|
+
* @example
|
|
641
|
+
* // Check if a token string is valid
|
|
642
|
+
* const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjk5OTk5OTk5OTl9.xxx';
|
|
643
|
+
* const isValid = isTokenValid(token); // true if not expired
|
|
644
|
+
*
|
|
645
|
+
* @example
|
|
646
|
+
* // Check if an expiration timestamp is valid
|
|
647
|
+
* const futureTimestamp = Math.floor(Date.now() / 1000) + 3600; // 1 hour from now
|
|
648
|
+
* const isValid = isTokenValid(futureTimestamp); // true
|
|
649
|
+
*/
|
|
607
650
|
function isTokenValid(input) {
|
|
608
651
|
if (!input) {
|
|
609
652
|
return false;
|
|
@@ -612,15 +655,68 @@ function isTokenValid(input) {
|
|
|
612
655
|
return exp ? Math.floor(Date.now() / 1000) < exp : false;
|
|
613
656
|
}
|
|
614
657
|
|
|
615
|
-
class TimeSpanOverflowError extends Error {
|
|
616
|
-
}
|
|
617
|
-
|
|
618
658
|
const MILLIS_PER_SECOND = 1000;
|
|
619
659
|
const MILLIS_PER_MINUTE = MILLIS_PER_SECOND * 60; // 60,000
|
|
620
660
|
const MILLIS_PER_HOUR = MILLIS_PER_MINUTE * 60; // 3,600,000
|
|
621
661
|
const MILLIS_PER_DAY = MILLIS_PER_HOUR * 24; // 86,400,000
|
|
662
|
+
/**
|
|
663
|
+
* Represents an error that occurs when a TimeSpan operation would result in a value
|
|
664
|
+
* that exceeds the safe bounds of a number in JavaScript.
|
|
665
|
+
*
|
|
666
|
+
* This error is thrown when:
|
|
667
|
+
* - Creating a TimeSpan from a value that, when multiplied by a time scale (e.g., days, hours),
|
|
668
|
+
* would exceed {@link TimeSpan.maxValue} or fall below {@link TimeSpan.minValue}
|
|
669
|
+
* - Adding or subtracting TimeSpan instances would result in overflow
|
|
670
|
+
* - Converting time components that would exceed safe integer bounds
|
|
671
|
+
*
|
|
672
|
+
* The safe bounds are defined by JavaScript's {@link Number.MAX_SAFE_INTEGER} and
|
|
673
|
+
* {@link Number.MIN_SAFE_INTEGER}.
|
|
674
|
+
*
|
|
675
|
+
* @example
|
|
676
|
+
* try {
|
|
677
|
+
* // This will throw TimeSpanOverflowError
|
|
678
|
+
* const hugeSpan = TimeSpan.fromDays(Number.MAX_SAFE_INTEGER);
|
|
679
|
+
* } catch (error) {
|
|
680
|
+
* if (error instanceof TimeSpanOverflowError) {
|
|
681
|
+
* console.log('TimeSpan duration exceeded safe bounds');
|
|
682
|
+
* }
|
|
683
|
+
* }
|
|
684
|
+
*/
|
|
685
|
+
class TimeSpanOverflowError extends Error {
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Represents a time interval with support for days, hours, minutes, seconds, and milliseconds.
|
|
689
|
+
*
|
|
690
|
+
* The TimeSpan class provides a way to represent and work with time durations, similar to
|
|
691
|
+
* .NET's TimeSpan class. It stores time internally as milliseconds and provides various
|
|
692
|
+
* static factory methods to create instances from different time units.
|
|
693
|
+
*
|
|
694
|
+
* @example
|
|
695
|
+
* // Create a TimeSpan of 5 days
|
|
696
|
+
* const fiveDays = TimeSpan.fromDays(5);
|
|
697
|
+
*
|
|
698
|
+
* @example
|
|
699
|
+
* // Create a TimeSpan from specific time components
|
|
700
|
+
* const timespan = TimeSpan.fromTime(2, 30, 45); // 2 hours, 30 minutes, 45 seconds
|
|
701
|
+
*
|
|
702
|
+
* @example
|
|
703
|
+
* // Create a TimeSpan from a string representation
|
|
704
|
+
* const parsed = TimeSpan.fromString("5:30:45"); // 5 hours, 30 minutes, 45 seconds
|
|
705
|
+
*/
|
|
622
706
|
class TimeSpan {
|
|
707
|
+
/** @private The number of milliseconds represented by this TimeSpan */
|
|
623
708
|
_millis;
|
|
709
|
+
/**
|
|
710
|
+
* @private
|
|
711
|
+
* Creates a TimeSpan from a value and time scale.
|
|
712
|
+
* Converts the value using the provided scale factor and rounds the result.
|
|
713
|
+
*
|
|
714
|
+
* @param value - The numeric value to convert
|
|
715
|
+
* @param scale - The scale factor (e.g., MILLIS_PER_DAY) to multiply by
|
|
716
|
+
* @returns A new TimeSpan instance
|
|
717
|
+
* @throws {Error} If value is NaN
|
|
718
|
+
* @throws {TimeSpanOverflowError} If the resulting milliseconds exceed safe integer bounds
|
|
719
|
+
*/
|
|
624
720
|
static interval(value, scale) {
|
|
625
721
|
if (Number.isNaN(value)) {
|
|
626
722
|
throw new Error("value can't be NaN");
|
|
@@ -632,6 +728,13 @@ class TimeSpan {
|
|
|
632
728
|
}
|
|
633
729
|
return new TimeSpan(millis);
|
|
634
730
|
}
|
|
731
|
+
/**
|
|
732
|
+
* @private
|
|
733
|
+
* Rounds a number towards zero (floor for positive, ceil for negative).
|
|
734
|
+
*
|
|
735
|
+
* @param n - The number to round
|
|
736
|
+
* @returns The rounded number
|
|
737
|
+
*/
|
|
635
738
|
static round(n) {
|
|
636
739
|
if (n < 0) {
|
|
637
740
|
return Math.ceil(n);
|
|
@@ -641,6 +744,17 @@ class TimeSpan {
|
|
|
641
744
|
}
|
|
642
745
|
return 0;
|
|
643
746
|
}
|
|
747
|
+
/**
|
|
748
|
+
* @private
|
|
749
|
+
* Converts time components (hours, minutes, seconds) to milliseconds.
|
|
750
|
+
* Validates that the total does not exceed TimeSpan bounds.
|
|
751
|
+
*
|
|
752
|
+
* @param hour - The number of hours (0-23 for typical usage)
|
|
753
|
+
* @param minute - The number of minutes (0-59)
|
|
754
|
+
* @param second - The number of seconds (0-59)
|
|
755
|
+
* @returns The total milliseconds
|
|
756
|
+
* @throws {TimeSpanOverflowError} If the total seconds exceed safe bounds
|
|
757
|
+
*/
|
|
644
758
|
static timeToMilliseconds(hour, minute, second) {
|
|
645
759
|
const totalSeconds = (hour * 3600) + (minute * 60) + second;
|
|
646
760
|
if (totalSeconds > TimeSpan.maxValue.totalSeconds || totalSeconds < TimeSpan.minValue.totalSeconds) {
|
|
@@ -648,27 +762,99 @@ class TimeSpan {
|
|
|
648
762
|
}
|
|
649
763
|
return totalSeconds * MILLIS_PER_SECOND;
|
|
650
764
|
}
|
|
765
|
+
/**
|
|
766
|
+
* Gets a TimeSpan instance representing zero duration.
|
|
767
|
+
*
|
|
768
|
+
* @returns A TimeSpan with 0 milliseconds
|
|
769
|
+
*/
|
|
651
770
|
static get zero() {
|
|
652
771
|
return new TimeSpan(0);
|
|
653
772
|
}
|
|
773
|
+
/**
|
|
774
|
+
* Gets the maximum possible TimeSpan value.
|
|
775
|
+
* Uses JavaScript's MAX_SAFE_INTEGER as the underlying millisecond limit.
|
|
776
|
+
*
|
|
777
|
+
* @returns A TimeSpan with the maximum safe integer milliseconds
|
|
778
|
+
*/
|
|
654
779
|
static get maxValue() {
|
|
655
780
|
return new TimeSpan(Number.MAX_SAFE_INTEGER);
|
|
656
781
|
}
|
|
782
|
+
/**
|
|
783
|
+
* Gets the minimum possible TimeSpan value.
|
|
784
|
+
* Uses JavaScript's MIN_SAFE_INTEGER as the underlying millisecond limit.
|
|
785
|
+
*
|
|
786
|
+
* @returns A TimeSpan with the minimum safe integer milliseconds
|
|
787
|
+
*/
|
|
657
788
|
static get minValue() {
|
|
658
789
|
return new TimeSpan(Number.MIN_SAFE_INTEGER);
|
|
659
790
|
}
|
|
791
|
+
/**
|
|
792
|
+
* Creates a TimeSpan from a number of days.
|
|
793
|
+
*
|
|
794
|
+
* @param value - The number of days
|
|
795
|
+
* @returns A new TimeSpan instance representing the specified duration
|
|
796
|
+
* @throws {Error} If value is NaN
|
|
797
|
+
* @throws {TimeSpanOverflowError} If the result exceeds safe bounds
|
|
798
|
+
*
|
|
799
|
+
* @example
|
|
800
|
+
* const span = TimeSpan.fromDays(7); // 7 days
|
|
801
|
+
*/
|
|
660
802
|
static fromDays(value) {
|
|
661
803
|
return TimeSpan.interval(value, MILLIS_PER_DAY);
|
|
662
804
|
}
|
|
805
|
+
/**
|
|
806
|
+
* Creates a TimeSpan from a number of hours.
|
|
807
|
+
*
|
|
808
|
+
* @param value - The number of hours
|
|
809
|
+
* @returns A new TimeSpan instance representing the specified duration
|
|
810
|
+
* @throws {Error} If value is NaN
|
|
811
|
+
* @throws {TimeSpanOverflowError} If the result exceeds safe bounds
|
|
812
|
+
*
|
|
813
|
+
* @example
|
|
814
|
+
* const span = TimeSpan.fromHours(24); // 24 hours = 1 day
|
|
815
|
+
*/
|
|
663
816
|
static fromHours(value) {
|
|
664
817
|
return TimeSpan.interval(value, MILLIS_PER_HOUR);
|
|
665
818
|
}
|
|
819
|
+
/**
|
|
820
|
+
* Creates a TimeSpan from a number of milliseconds.
|
|
821
|
+
*
|
|
822
|
+
* @param value - The number of milliseconds
|
|
823
|
+
* @returns A new TimeSpan instance representing the specified duration
|
|
824
|
+
* @throws {Error} If value is NaN
|
|
825
|
+
* @throws {TimeSpanOverflowError} If the result exceeds safe bounds
|
|
826
|
+
*
|
|
827
|
+
* @example
|
|
828
|
+
* const span = TimeSpan.fromMilliseconds(5000); // 5 seconds
|
|
829
|
+
*/
|
|
666
830
|
static fromMilliseconds(value) {
|
|
667
831
|
return TimeSpan.interval(value, 1);
|
|
668
832
|
}
|
|
833
|
+
/**
|
|
834
|
+
* Creates a TimeSpan from a number of minutes.
|
|
835
|
+
*
|
|
836
|
+
* @param value - The number of minutes
|
|
837
|
+
* @returns A new TimeSpan instance representing the specified duration
|
|
838
|
+
* @throws {Error} If value is NaN
|
|
839
|
+
* @throws {TimeSpanOverflowError} If the result exceeds safe bounds
|
|
840
|
+
*
|
|
841
|
+
* @example
|
|
842
|
+
* const span = TimeSpan.fromMinutes(60); // 60 minutes = 1 hour
|
|
843
|
+
*/
|
|
669
844
|
static fromMinutes(value) {
|
|
670
845
|
return TimeSpan.interval(value, MILLIS_PER_MINUTE);
|
|
671
846
|
}
|
|
847
|
+
/**
|
|
848
|
+
* Creates a TimeSpan from a number of seconds.
|
|
849
|
+
*
|
|
850
|
+
* @param value - The number of seconds
|
|
851
|
+
* @returns A new TimeSpan instance representing the specified duration
|
|
852
|
+
* @throws {Error} If value is NaN
|
|
853
|
+
* @throws {TimeSpanOverflowError} If the result exceeds safe bounds
|
|
854
|
+
*
|
|
855
|
+
* @example
|
|
856
|
+
* const span = TimeSpan.fromSeconds(300); // 300 seconds = 5 minutes
|
|
857
|
+
*/
|
|
672
858
|
static fromSeconds(value) {
|
|
673
859
|
return TimeSpan.interval(value, MILLIS_PER_SECOND);
|
|
674
860
|
}
|
|
@@ -680,10 +866,32 @@ class TimeSpan {
|
|
|
680
866
|
return this.fromTimeStartingFromHours(daysOrHours, hoursOrMinutes, minutesOrSeconds);
|
|
681
867
|
}
|
|
682
868
|
}
|
|
869
|
+
/**
|
|
870
|
+
* @private
|
|
871
|
+
* Creates a TimeSpan from hours, minutes, and seconds components.
|
|
872
|
+
*
|
|
873
|
+
* @param hours - The number of hours
|
|
874
|
+
* @param minutes - The number of minutes
|
|
875
|
+
* @param seconds - The number of seconds
|
|
876
|
+
* @returns A new TimeSpan instance
|
|
877
|
+
* @throws {TimeSpanOverflowError} If the total exceeds safe bounds
|
|
878
|
+
*/
|
|
683
879
|
static fromTimeStartingFromHours(hours, minutes, seconds) {
|
|
684
880
|
const millis = TimeSpan.timeToMilliseconds(hours, minutes, seconds);
|
|
685
881
|
return new TimeSpan(millis);
|
|
686
882
|
}
|
|
883
|
+
/**
|
|
884
|
+
* @private
|
|
885
|
+
* Creates a TimeSpan from all time components (days through milliseconds).
|
|
886
|
+
*
|
|
887
|
+
* @param days - The number of days
|
|
888
|
+
* @param hours - The number of hours
|
|
889
|
+
* @param minutes - The number of minutes
|
|
890
|
+
* @param seconds - The number of seconds
|
|
891
|
+
* @param milliseconds - The number of milliseconds
|
|
892
|
+
* @returns A new TimeSpan instance
|
|
893
|
+
* @throws {TimeSpanOverflowError} If the total exceeds safe bounds
|
|
894
|
+
*/
|
|
687
895
|
static fromTimeStartingFromDays(days, hours, minutes, seconds, milliseconds) {
|
|
688
896
|
const totalMilliSeconds = (days * MILLIS_PER_DAY) +
|
|
689
897
|
(hours * MILLIS_PER_HOUR) +
|
|
@@ -695,47 +903,192 @@ class TimeSpan {
|
|
|
695
903
|
}
|
|
696
904
|
return new TimeSpan(totalMilliSeconds);
|
|
697
905
|
}
|
|
906
|
+
/**
|
|
907
|
+
* Creates a new TimeSpan instance with the specified number of milliseconds.
|
|
908
|
+
*
|
|
909
|
+
* @param millis - The total number of milliseconds
|
|
910
|
+
*
|
|
911
|
+
* @example
|
|
912
|
+
* const span = new TimeSpan(5000); // 5 seconds
|
|
913
|
+
*/
|
|
698
914
|
constructor(millis) {
|
|
699
915
|
this._millis = millis;
|
|
700
916
|
}
|
|
917
|
+
/**
|
|
918
|
+
* Gets the day component of this TimeSpan (0-31+).
|
|
919
|
+
* This returns only the day portion, not the total days.
|
|
920
|
+
*
|
|
921
|
+
* @returns The number of whole days
|
|
922
|
+
* @example
|
|
923
|
+
* const span = TimeSpan.fromTime(1, 5, 30, 0); // 1 day, 5 hours, 30 minutes
|
|
924
|
+
* console.log(span.days); // 1
|
|
925
|
+
*/
|
|
701
926
|
get days() {
|
|
702
927
|
return TimeSpan.round(this._millis / MILLIS_PER_DAY);
|
|
703
928
|
}
|
|
929
|
+
/**
|
|
930
|
+
* Gets the hours component of this TimeSpan (0-23).
|
|
931
|
+
* This returns only the hours portion within a day, not the total hours.
|
|
932
|
+
*
|
|
933
|
+
* @returns The number of hours (0-23)
|
|
934
|
+
* @example
|
|
935
|
+
* const span = TimeSpan.fromTime(25, 30, 0); // 25 hours, 30 minutes
|
|
936
|
+
* console.log(span.hours); // 1 (25 % 24)
|
|
937
|
+
*/
|
|
704
938
|
get hours() {
|
|
705
939
|
return TimeSpan.round((this._millis / MILLIS_PER_HOUR) % 24);
|
|
706
940
|
}
|
|
941
|
+
/**
|
|
942
|
+
* Gets the minutes component of this TimeSpan (0-59).
|
|
943
|
+
* This returns only the minutes portion within an hour, not the total minutes.
|
|
944
|
+
*
|
|
945
|
+
* @returns The number of minutes (0-59)
|
|
946
|
+
* @example
|
|
947
|
+
* const span = TimeSpan.fromTime(0, 90, 0); // 90 minutes
|
|
948
|
+
* console.log(span.minutes); // 30 (90 % 60)
|
|
949
|
+
*/
|
|
707
950
|
get minutes() {
|
|
708
951
|
return TimeSpan.round((this._millis / MILLIS_PER_MINUTE) % 60);
|
|
709
952
|
}
|
|
953
|
+
/**
|
|
954
|
+
* Gets the seconds component of this TimeSpan (0-59).
|
|
955
|
+
* This returns only the seconds portion within a minute, not the total seconds.
|
|
956
|
+
*
|
|
957
|
+
* @returns The number of seconds (0-59)
|
|
958
|
+
* @example
|
|
959
|
+
* const span = TimeSpan.fromTime(0, 0, 90); // 90 seconds
|
|
960
|
+
* console.log(span.seconds); // 30 (90 % 60)
|
|
961
|
+
*/
|
|
710
962
|
get seconds() {
|
|
711
963
|
return TimeSpan.round((this._millis / MILLIS_PER_SECOND) % 60);
|
|
712
964
|
}
|
|
965
|
+
/**
|
|
966
|
+
* Gets the milliseconds component of this TimeSpan (0-999).
|
|
967
|
+
* This returns only the milliseconds portion within a second, not the total milliseconds.
|
|
968
|
+
*
|
|
969
|
+
* @returns The number of milliseconds (0-999)
|
|
970
|
+
* @example
|
|
971
|
+
* const span = TimeSpan.fromTime(0, 0, 0, 1, 500); // 1 second, 500 milliseconds
|
|
972
|
+
* console.log(span.milliseconds); // 500
|
|
973
|
+
*/
|
|
713
974
|
get milliseconds() {
|
|
714
975
|
return TimeSpan.round(this._millis % 1000);
|
|
715
976
|
}
|
|
977
|
+
/**
|
|
978
|
+
* Gets the total number of days in this TimeSpan.
|
|
979
|
+
* Unlike the {@link days} property, this returns the complete duration in days as a decimal.
|
|
980
|
+
*
|
|
981
|
+
* @returns The total duration in days
|
|
982
|
+
* @example
|
|
983
|
+
* const span = TimeSpan.fromHours(30); // 30 hours
|
|
984
|
+
* console.log(span.totalDays); // 1.25
|
|
985
|
+
*/
|
|
716
986
|
get totalDays() {
|
|
717
987
|
return this._millis / MILLIS_PER_DAY;
|
|
718
988
|
}
|
|
989
|
+
/**
|
|
990
|
+
* Gets the total number of hours in this TimeSpan.
|
|
991
|
+
* Unlike the {@link hours} property, this returns the complete duration in hours as a decimal.
|
|
992
|
+
*
|
|
993
|
+
* @returns The total duration in hours
|
|
994
|
+
* @example
|
|
995
|
+
* const span = TimeSpan.fromMinutes(90); // 90 minutes
|
|
996
|
+
* console.log(span.totalHours); // 1.5
|
|
997
|
+
*/
|
|
719
998
|
get totalHours() {
|
|
720
999
|
return this._millis / MILLIS_PER_HOUR;
|
|
721
1000
|
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Gets the total number of minutes in this TimeSpan.
|
|
1003
|
+
* Unlike the {@link minutes} property, this returns the complete duration in minutes as a decimal.
|
|
1004
|
+
*
|
|
1005
|
+
* @returns The total duration in minutes
|
|
1006
|
+
* @example
|
|
1007
|
+
* const span = TimeSpan.fromSeconds(150); // 150 seconds
|
|
1008
|
+
* console.log(span.totalMinutes); // 2.5
|
|
1009
|
+
*/
|
|
722
1010
|
get totalMinutes() {
|
|
723
1011
|
return this._millis / MILLIS_PER_MINUTE;
|
|
724
1012
|
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Gets the total number of seconds in this TimeSpan.
|
|
1015
|
+
* Unlike the {@link seconds} property, this returns the complete duration in seconds as a decimal.
|
|
1016
|
+
*
|
|
1017
|
+
* @returns The total duration in seconds
|
|
1018
|
+
* @example
|
|
1019
|
+
* const span = TimeSpan.fromMilliseconds(5500); // 5500 milliseconds
|
|
1020
|
+
* console.log(span.totalSeconds); // 5.5
|
|
1021
|
+
*/
|
|
725
1022
|
get totalSeconds() {
|
|
726
1023
|
return this._millis / MILLIS_PER_SECOND;
|
|
727
1024
|
}
|
|
1025
|
+
/**
|
|
1026
|
+
* Gets the total number of milliseconds in this TimeSpan.
|
|
1027
|
+
*
|
|
1028
|
+
* @returns The total duration in milliseconds
|
|
1029
|
+
* @example
|
|
1030
|
+
* const span = TimeSpan.fromSeconds(2); // 2 seconds
|
|
1031
|
+
* console.log(span.totalMilliseconds); // 2000
|
|
1032
|
+
*/
|
|
728
1033
|
get totalMilliseconds() {
|
|
729
1034
|
return this._millis;
|
|
730
1035
|
}
|
|
1036
|
+
/**
|
|
1037
|
+
* Returns a new TimeSpan that represents the sum of this instance and the specified TimeSpan.
|
|
1038
|
+
*
|
|
1039
|
+
* @param ts - The TimeSpan to add
|
|
1040
|
+
* @returns A new TimeSpan representing the sum
|
|
1041
|
+
* @throws {TimeSpanOverflowError} If the result exceeds safe integer bounds
|
|
1042
|
+
*
|
|
1043
|
+
* @example
|
|
1044
|
+
* const span1 = TimeSpan.fromHours(2);
|
|
1045
|
+
* const span2 = TimeSpan.fromHours(3);
|
|
1046
|
+
* const span3 = span1.add(span2);
|
|
1047
|
+
* console.log(span3.totalHours); // 5
|
|
1048
|
+
*/
|
|
731
1049
|
add(ts) {
|
|
732
1050
|
const result = this._millis + ts.totalMilliseconds;
|
|
733
1051
|
return new TimeSpan(result);
|
|
734
1052
|
}
|
|
1053
|
+
/**
|
|
1054
|
+
* Returns a new TimeSpan that represents the difference between this instance and the specified TimeSpan.
|
|
1055
|
+
*
|
|
1056
|
+
* @param ts - The TimeSpan to subtract
|
|
1057
|
+
* @returns A new TimeSpan representing the difference
|
|
1058
|
+
* @throws {TimeSpanOverflowError} If the result exceeds safe integer bounds
|
|
1059
|
+
*
|
|
1060
|
+
* @example
|
|
1061
|
+
* const span1 = TimeSpan.fromHours(5);
|
|
1062
|
+
* const span2 = TimeSpan.fromHours(2);
|
|
1063
|
+
* const span3 = span1.subtract(span2);
|
|
1064
|
+
* console.log(span3.totalHours); // 3
|
|
1065
|
+
*/
|
|
735
1066
|
subtract(ts) {
|
|
736
1067
|
const result = this._millis - ts.totalMilliseconds;
|
|
737
1068
|
return new TimeSpan(result);
|
|
738
1069
|
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Creates a TimeSpan from a string representation.
|
|
1072
|
+
*
|
|
1073
|
+
* Supports two string formats:
|
|
1074
|
+
* 1. "HH:mm:ss" - Hours, minutes, seconds (e.g., "5:30:45")
|
|
1075
|
+
* 2. "d.HH:mm:ss.fff" - Days, hours, minutes, seconds, milliseconds (e.g., "5.02:30:45.500")
|
|
1076
|
+
*
|
|
1077
|
+
* @param value - The string representation of a TimeSpan
|
|
1078
|
+
* @returns A new TimeSpan instance parsed from the string
|
|
1079
|
+
* @throws {Error} If the string format is invalid or values cannot be parsed as integers
|
|
1080
|
+
*
|
|
1081
|
+
* @example
|
|
1082
|
+
* // Parse hours:minutes:seconds
|
|
1083
|
+
* const span1 = TimeSpan.fromString("5:30:45");
|
|
1084
|
+
* console.log(span1.totalSeconds); // 19845
|
|
1085
|
+
*
|
|
1086
|
+
* @example
|
|
1087
|
+
* // Parse days.hours:minutes:seconds.milliseconds
|
|
1088
|
+
* const span2 = TimeSpan.fromString("5.02:30:45.500");
|
|
1089
|
+
* console.log(span2.days); // 5
|
|
1090
|
+
* console.log(span2.milliseconds); // 500
|
|
1091
|
+
*/
|
|
739
1092
|
static fromString(value) {
|
|
740
1093
|
const splitPoint = value.split('.');
|
|
741
1094
|
if (splitPoint.length == 3) {
|
|
@@ -751,6 +1104,26 @@ class TimeSpan {
|
|
|
751
1104
|
|
|
752
1105
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
753
1106
|
/* eslint-disable no-useless-escape */
|
|
1107
|
+
/**
|
|
1108
|
+
* A comprehensive utility service providing static helper methods for common operations across the application.
|
|
1109
|
+
*
|
|
1110
|
+
* This service includes utilities for:
|
|
1111
|
+
* - **Cookie Management**: Getting, setting, removing, and checking cookies with proper encoding
|
|
1112
|
+
* - **Security**: Scrubbing sensitive data from logs to prevent accidental exposure of PII and credentials
|
|
1113
|
+
* - **HTTP Response Handling**: Parsing and extracting meaningful messages from HTTP responses and errors
|
|
1114
|
+
* - **Date/Time Formatting**: Converting dates to various human-readable formats and calculating durations
|
|
1115
|
+
* - **String Manipulation**: Case conversion, text expansion, trimming, and parsing operations
|
|
1116
|
+
* - **Data Processing**: Array operations, deep cloning, searching, and JSON parsing with fallback handling
|
|
1117
|
+
* - **Validation**: Type checking, URL validation, and network connectivity checks
|
|
1118
|
+
*
|
|
1119
|
+
* @example
|
|
1120
|
+
* // Using static utility methods
|
|
1121
|
+
* const scrubbedLog = Utilities.scrubForLog(sensitiveData);
|
|
1122
|
+
* const formattedDate = Utilities.printFriendlyDate(new Date());
|
|
1123
|
+
* const errorMessage = Utilities.getHttpResponseMessage(httpError);
|
|
1124
|
+
*
|
|
1125
|
+
* @injectable
|
|
1126
|
+
*/
|
|
754
1127
|
class Utilities {
|
|
755
1128
|
static captionAndMessageSeparator = ':';
|
|
756
1129
|
static noNetworkMessageCaption = 'No Network';
|
|
@@ -759,10 +1132,36 @@ class Utilities {
|
|
|
759
1132
|
static accessDeniedMessageDetail = '';
|
|
760
1133
|
static notFoundMessageCaption = 'Not Found';
|
|
761
1134
|
static notFoundMessageDetail = 'The target resource cannot be found';
|
|
1135
|
+
/**
|
|
1136
|
+
* Cookie management utilities providing cross-browser compatible methods for cookie operations.
|
|
1137
|
+
* All cookie keys and values are properly URL-encoded/decoded to handle special characters safely.
|
|
1138
|
+
*
|
|
1139
|
+
* Methods:
|
|
1140
|
+
* - getItem: Retrieves a cookie value by name, returns null if not found
|
|
1141
|
+
* - setItem: Sets a cookie with optional expiration, path, domain, and secure flags
|
|
1142
|
+
* - removeItem: Removes a cookie by setting its expiration to the past
|
|
1143
|
+
* - hasItem: Checks if a cookie exists
|
|
1144
|
+
* - keys: Returns an array of all cookie names
|
|
1145
|
+
*/
|
|
762
1146
|
static cookies = {
|
|
1147
|
+
/**
|
|
1148
|
+
* Retrieves the value of a cookie by its name with proper URL decoding.
|
|
1149
|
+
* @param sKey The cookie name to retrieve
|
|
1150
|
+
* @returns The decoded cookie value or null if not found
|
|
1151
|
+
*/
|
|
763
1152
|
getItem: (sKey) => {
|
|
764
1153
|
return decodeURIComponent(document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, '\\$&') + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1')) || null;
|
|
765
1154
|
},
|
|
1155
|
+
/**
|
|
1156
|
+
* Sets a cookie with optional expiration, domain, path, and secure flag.
|
|
1157
|
+
* @param sKey The cookie name
|
|
1158
|
+
* @param sValue The cookie value
|
|
1159
|
+
* @param vEnd The expiration (Number for max-age in seconds, String for expires date, Date object, or null for session cookie)
|
|
1160
|
+
* @param sPath The path scope for the cookie (defaults to all paths if empty)
|
|
1161
|
+
* @param sDomain The domain scope for the cookie (defaults to current domain if empty)
|
|
1162
|
+
* @param bSecure Whether to set the Secure flag (cookie only sent over HTTPS)
|
|
1163
|
+
* @returns true if the cookie was set successfully, false otherwise
|
|
1164
|
+
*/
|
|
766
1165
|
setItem: (sKey, sValue, vEnd, sPath, sDomain, bSecure) => {
|
|
767
1166
|
if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) {
|
|
768
1167
|
return false;
|
|
@@ -784,6 +1183,13 @@ class Utilities {
|
|
|
784
1183
|
document.cookie = encodeURIComponent(sKey) + '=' + encodeURIComponent(sValue) + sExpires + (sDomain ? '; domain=' + sDomain : '') + (sPath ? '; path=' + sPath : '') + (bSecure ? '; secure' : '');
|
|
785
1184
|
return true;
|
|
786
1185
|
},
|
|
1186
|
+
/**
|
|
1187
|
+
* Removes a cookie by setting its expiration to a past date.
|
|
1188
|
+
* @param sKey The cookie name to remove
|
|
1189
|
+
* @param sPath The path scope (must match the path used when setting the cookie)
|
|
1190
|
+
* @param sDomain The domain scope (must match the domain used when setting the cookie)
|
|
1191
|
+
* @returns true if the removal was attempted, false if key was invalid
|
|
1192
|
+
*/
|
|
787
1193
|
removeItem: (sKey, sPath, sDomain) => {
|
|
788
1194
|
if (!sKey) {
|
|
789
1195
|
return false;
|
|
@@ -791,9 +1197,18 @@ class Utilities {
|
|
|
791
1197
|
document.cookie = encodeURIComponent(sKey) + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT' + (sDomain ? '; domain=' + sDomain : '') + (sPath ? '; path=' + sPath : '');
|
|
792
1198
|
return true;
|
|
793
1199
|
},
|
|
1200
|
+
/**
|
|
1201
|
+
* Checks if a cookie with the specified name exists.
|
|
1202
|
+
* @param sKey The cookie name to check
|
|
1203
|
+
* @returns true if the cookie exists, false otherwise
|
|
1204
|
+
*/
|
|
794
1205
|
hasItem: (sKey) => {
|
|
795
1206
|
return (new RegExp('(?:^|;\\s*)' + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, '\\$&') + '\\s*\\=')).test(document.cookie);
|
|
796
1207
|
},
|
|
1208
|
+
/**
|
|
1209
|
+
* Retrieves all cookie names as an array.
|
|
1210
|
+
* @returns An array of all cookie names with proper URL decoding applied
|
|
1211
|
+
*/
|
|
797
1212
|
keys: () => {
|
|
798
1213
|
// eslint-disable-next-line no-useless-backreference
|
|
799
1214
|
const aKeys = document.cookie.replace(/((?:^|\s*;)[^\=]+)(?=;|$)|^\s*|\s*(?:\=[^;]*)?(?:\1|$)/g, '').split(/\s*(?:\=[^;]*)?;\s*/);
|
|
@@ -848,6 +1263,13 @@ class Utilities {
|
|
|
848
1263
|
return '[UNABLE_TO_SCRUB]';
|
|
849
1264
|
}
|
|
850
1265
|
}
|
|
1266
|
+
/**
|
|
1267
|
+
* Extracts all messages from an HTTP response or error, formatting them as key-value pairs.
|
|
1268
|
+
* Automatically detects network errors, access denied, and not found responses with special formatting.
|
|
1269
|
+
*
|
|
1270
|
+
* @param data The HTTP response or error object
|
|
1271
|
+
* @returns An array of formatted message strings in the format "Key: Value"
|
|
1272
|
+
*/
|
|
851
1273
|
static getHttpResponseMessages(data) {
|
|
852
1274
|
const responses = [];
|
|
853
1275
|
if (data instanceof HttpResponseBase) {
|
|
@@ -897,6 +1319,13 @@ class Utilities {
|
|
|
897
1319
|
}
|
|
898
1320
|
return responses;
|
|
899
1321
|
}
|
|
1322
|
+
/**
|
|
1323
|
+
* Extracts a single consolidated message from an HTTP response or error.
|
|
1324
|
+
* Searches in priority order: network message, not found message, error_description, error, then general response messages.
|
|
1325
|
+
*
|
|
1326
|
+
* @param data The HTTP response or error object
|
|
1327
|
+
* @returns A single formatted message string
|
|
1328
|
+
*/
|
|
900
1329
|
static getHttpResponseMessage(data) {
|
|
901
1330
|
const httpMessage = Utilities.findHttpResponseMessage(Utilities.noNetworkMessageCaption, data) ||
|
|
902
1331
|
Utilities.findHttpResponseMessage(Utilities.notFoundMessageCaption, data) ||
|
|
@@ -905,6 +1334,16 @@ class Utilities {
|
|
|
905
1334
|
Utilities.getHttpResponseMessages(data).join();
|
|
906
1335
|
return httpMessage;
|
|
907
1336
|
}
|
|
1337
|
+
/**
|
|
1338
|
+
* Searches for a specific message within HTTP response messages.
|
|
1339
|
+
* Can search in caption only or in full message text, and can optionally include the caption in the result.
|
|
1340
|
+
*
|
|
1341
|
+
* @param messageToFind The message or caption text to search for (case-insensitive)
|
|
1342
|
+
* @param data The HTTP response or error object
|
|
1343
|
+
* @param searchInCaptionOnly If true, only searches in the caption portion (before separator), defaults to true
|
|
1344
|
+
* @param includeCaptionInResult If true, includes the caption in the returned result, defaults to false
|
|
1345
|
+
* @returns The matching message or null if not found
|
|
1346
|
+
*/
|
|
908
1347
|
static findHttpResponseMessage(messageToFind, data, searchInCaptionOnly = true, includeCaptionInResult = false) {
|
|
909
1348
|
const searchString = messageToFind.toLowerCase();
|
|
910
1349
|
const httpMessages = this.getHttpResponseMessages(data);
|
|
@@ -929,6 +1368,13 @@ class Utilities {
|
|
|
929
1368
|
}
|
|
930
1369
|
return null;
|
|
931
1370
|
}
|
|
1371
|
+
/**
|
|
1372
|
+
* Extracts the response body from an HTTP response or error.
|
|
1373
|
+
* For successful responses, returns the body property. For error responses, prioritizes error over message and statusText.
|
|
1374
|
+
*
|
|
1375
|
+
* @param response The HTTP response or error response
|
|
1376
|
+
* @returns The response body, error, or null if none available
|
|
1377
|
+
*/
|
|
932
1378
|
static getResponseBody(response) {
|
|
933
1379
|
if (response instanceof HttpResponse) {
|
|
934
1380
|
return response.body;
|
|
@@ -937,24 +1383,45 @@ class Utilities {
|
|
|
937
1383
|
return response.error || response.message || response.statusText;
|
|
938
1384
|
}
|
|
939
1385
|
}
|
|
1386
|
+
/**
|
|
1387
|
+
* Checks if the response indicates a network error (status 0).
|
|
1388
|
+
* @param response The HTTP response to check
|
|
1389
|
+
* @returns true if status is 0 (no network connection), false otherwise
|
|
1390
|
+
*/
|
|
940
1391
|
static checkNoNetwork(response) {
|
|
941
1392
|
if (response instanceof HttpResponseBase) {
|
|
942
1393
|
return response.status == 0;
|
|
943
1394
|
}
|
|
944
1395
|
return false;
|
|
945
1396
|
}
|
|
1397
|
+
/**
|
|
1398
|
+
* Checks if the response indicates an access denied error (status 403).
|
|
1399
|
+
* @param response The HTTP response to check
|
|
1400
|
+
* @returns true if status is 403 (Forbidden), false otherwise
|
|
1401
|
+
*/
|
|
946
1402
|
static checkAccessDenied(response) {
|
|
947
1403
|
if (response instanceof HttpResponseBase) {
|
|
948
1404
|
return response.status == 403;
|
|
949
1405
|
}
|
|
950
1406
|
return false;
|
|
951
1407
|
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Checks if the response indicates a not found error (status 404).
|
|
1410
|
+
* @param response The HTTP response to check
|
|
1411
|
+
* @returns true if status is 404 (Not Found), false otherwise
|
|
1412
|
+
*/
|
|
952
1413
|
static checkNotFound(response) {
|
|
953
1414
|
if (response instanceof HttpResponseBase) {
|
|
954
1415
|
return response.status == 404;
|
|
955
1416
|
}
|
|
956
1417
|
return false;
|
|
957
1418
|
}
|
|
1419
|
+
/**
|
|
1420
|
+
* Validates if a URL points to localhost or 127.0.0.1.
|
|
1421
|
+
* @param url The URL to check
|
|
1422
|
+
* @param base Optional base URL for resolving relative URLs
|
|
1423
|
+
* @returns true if the URL hostname is localhost or 127.0.0.1, false otherwise
|
|
1424
|
+
*/
|
|
958
1425
|
static checkIsLocalHost(url, base) {
|
|
959
1426
|
if (url) {
|
|
960
1427
|
const location = new URL(url, base);
|
|
@@ -962,6 +1429,13 @@ class Utilities {
|
|
|
962
1429
|
}
|
|
963
1430
|
return false;
|
|
964
1431
|
}
|
|
1432
|
+
/**
|
|
1433
|
+
* Parses a query parameter string into a key-value object.
|
|
1434
|
+
* Handles standard URL query string format (key=value&key=value).
|
|
1435
|
+
*
|
|
1436
|
+
* @param paramString The query parameter string (e.g., "id=123&name=test")
|
|
1437
|
+
* @returns An object with key-value pairs, or null if paramString is empty
|
|
1438
|
+
*/
|
|
965
1439
|
static getQueryParamsFromString(paramString) {
|
|
966
1440
|
if (!paramString) {
|
|
967
1441
|
return null;
|
|
@@ -973,6 +1447,14 @@ class Utilities {
|
|
|
973
1447
|
}
|
|
974
1448
|
return params;
|
|
975
1449
|
}
|
|
1450
|
+
/**
|
|
1451
|
+
* Splits a string into two parts based on the first occurrence of a separator.
|
|
1452
|
+
* Both parts are trimmed of whitespace.
|
|
1453
|
+
*
|
|
1454
|
+
* @param text The text to split
|
|
1455
|
+
* @param separator The separator string to split on
|
|
1456
|
+
* @returns An object with firstPart and secondPart (secondPart is null if separator not found)
|
|
1457
|
+
*/
|
|
976
1458
|
static splitInTwo(text, separator) {
|
|
977
1459
|
const separatorIndex = text.indexOf(separator);
|
|
978
1460
|
if (separatorIndex == -1) {
|
|
@@ -982,6 +1464,13 @@ class Utilities {
|
|
|
982
1464
|
const part2 = text.substr(separatorIndex + 1).trim();
|
|
983
1465
|
return { firstPart: part1, secondPart: part2 };
|
|
984
1466
|
}
|
|
1467
|
+
/**
|
|
1468
|
+
* Safely stringifies an object, filtering out complex types and handling circular references.
|
|
1469
|
+
* If direct JSON.stringify fails, creates a sanitized object containing only primitive properties.
|
|
1470
|
+
*
|
|
1471
|
+
* @param object The object to stringify
|
|
1472
|
+
* @returns A JSON string representation of the object, or a sanitized version if stringification fails
|
|
1473
|
+
*/
|
|
985
1474
|
static safeStringify(object) {
|
|
986
1475
|
let result;
|
|
987
1476
|
try {
|
|
@@ -1007,6 +1496,13 @@ class Utilities {
|
|
|
1007
1496
|
result = '[***Sanitized Object***]: ' + JSON.stringify(simpleObject);
|
|
1008
1497
|
return result;
|
|
1009
1498
|
}
|
|
1499
|
+
/**
|
|
1500
|
+
* Safely parses a JSON string with fallback handling.
|
|
1501
|
+
* Returns the parsed object if valid JSON, or returns undefined/value if parsing fails.
|
|
1502
|
+
*
|
|
1503
|
+
* @param value The JSON string to parse
|
|
1504
|
+
* @returns The parsed object, 'undefined' if the string is 'undefined', or the original value if parsing fails
|
|
1505
|
+
*/
|
|
1010
1506
|
static JsonTryParse(value) {
|
|
1011
1507
|
try {
|
|
1012
1508
|
return JSON.parse(value);
|
|
@@ -1018,6 +1514,11 @@ class Utilities {
|
|
|
1018
1514
|
return value;
|
|
1019
1515
|
}
|
|
1020
1516
|
}
|
|
1517
|
+
/**
|
|
1518
|
+
* Checks if an object has no properties.
|
|
1519
|
+
* @param obj The object to check
|
|
1520
|
+
* @returns true if the object is empty (no own properties), false otherwise
|
|
1521
|
+
*/
|
|
1021
1522
|
static TestIsObjectEmpty(obj) {
|
|
1022
1523
|
for (const prop in obj) {
|
|
1023
1524
|
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
|
|
@@ -1026,13 +1527,28 @@ class Utilities {
|
|
|
1026
1527
|
}
|
|
1027
1528
|
return true;
|
|
1028
1529
|
}
|
|
1530
|
+
/**
|
|
1531
|
+
* Checks if a value is undefined.
|
|
1532
|
+
* @param value The value to check
|
|
1533
|
+
* @returns true if the value is undefined, false otherwise
|
|
1534
|
+
*/
|
|
1029
1535
|
static TestIsUndefined(value) {
|
|
1030
1536
|
return typeof value === 'undefined';
|
|
1031
1537
|
// return value === undefined;
|
|
1032
1538
|
}
|
|
1539
|
+
/**
|
|
1540
|
+
* Checks if a value is a string.
|
|
1541
|
+
* @param value The value to check
|
|
1542
|
+
* @returns true if the value is a string primitive or String object, false otherwise
|
|
1543
|
+
*/
|
|
1033
1544
|
static TestIsString(value) {
|
|
1034
1545
|
return typeof value === 'string' || value instanceof String;
|
|
1035
1546
|
}
|
|
1547
|
+
/**
|
|
1548
|
+
* Capitalizes the first letter of a string while leaving the rest unchanged.
|
|
1549
|
+
* @param text The text to capitalize
|
|
1550
|
+
* @returns The text with the first character uppercased, or the original text if empty
|
|
1551
|
+
*/
|
|
1036
1552
|
static capitalizeFirstLetter(text) {
|
|
1037
1553
|
if (text) {
|
|
1038
1554
|
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
@@ -1041,6 +1557,11 @@ class Utilities {
|
|
|
1041
1557
|
return text;
|
|
1042
1558
|
}
|
|
1043
1559
|
}
|
|
1560
|
+
/**
|
|
1561
|
+
* Converts a string to title case, capitalizing the first letter of each word.
|
|
1562
|
+
* @param text The text to convert to title case
|
|
1563
|
+
* @returns The text in title case format
|
|
1564
|
+
*/
|
|
1044
1565
|
static toTitleCase(text) {
|
|
1045
1566
|
return text.replace(/\w\S*/g, (subString) => {
|
|
1046
1567
|
return subString.charAt(0).toUpperCase() + subString.substr(1).toLowerCase();
|
|
@@ -1058,12 +1579,28 @@ class Utilities {
|
|
|
1058
1579
|
return items.toLowerCase();
|
|
1059
1580
|
}
|
|
1060
1581
|
}
|
|
1582
|
+
/**
|
|
1583
|
+
* Generates a unique random ID string.
|
|
1584
|
+
* @returns A string representation of a random number between 1,000,000 and 9,000,000
|
|
1585
|
+
*/
|
|
1061
1586
|
static uniqueId() {
|
|
1062
1587
|
return this.randomNumber(1000000, 9000000).toString();
|
|
1063
1588
|
}
|
|
1589
|
+
/**
|
|
1590
|
+
* Generates a random integer within a specified range (inclusive).
|
|
1591
|
+
* @param min The minimum value (inclusive)
|
|
1592
|
+
* @param max The maximum value (inclusive)
|
|
1593
|
+
* @returns A random integer between min and max
|
|
1594
|
+
*/
|
|
1064
1595
|
static randomNumber(min, max) {
|
|
1065
1596
|
return Math.floor(Math.random() * (max - min + 1) + min);
|
|
1066
1597
|
}
|
|
1598
|
+
/**
|
|
1599
|
+
* Gets the base URL of the current application.
|
|
1600
|
+
* Handles cases where window.location.origin is not available by constructing it from components.
|
|
1601
|
+
*
|
|
1602
|
+
* @returns The base URL without trailing slash (e.g., "http://localhost:4200")
|
|
1603
|
+
*/
|
|
1067
1604
|
static baseUrl() {
|
|
1068
1605
|
let base = '';
|
|
1069
1606
|
if (window.location.origin) {
|
|
@@ -1074,6 +1611,13 @@ class Utilities {
|
|
|
1074
1611
|
}
|
|
1075
1612
|
return base.replace(/\/$/, '');
|
|
1076
1613
|
}
|
|
1614
|
+
/**
|
|
1615
|
+
* Formats a date to display only the date portion in human-readable format.
|
|
1616
|
+
* Example output: "Monday, 1st January 2024"
|
|
1617
|
+
*
|
|
1618
|
+
* @param date The date to format
|
|
1619
|
+
* @returns A formatted date string with day of week, day of month with ordinal suffix, month name, and year
|
|
1620
|
+
*/
|
|
1077
1621
|
static printDateOnly(date) {
|
|
1078
1622
|
date = new Date(date);
|
|
1079
1623
|
const dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
|
@@ -1098,6 +1642,13 @@ class Utilities {
|
|
|
1098
1642
|
const dateString = dayNames[dayOfWeek] + ', ' + dayOfMonth + sup + ' ' + monthNames[month] + ' ' + year;
|
|
1099
1643
|
return dateString;
|
|
1100
1644
|
}
|
|
1645
|
+
/**
|
|
1646
|
+
* Formats a date to display only the time portion in 12-hour format with AM/PM.
|
|
1647
|
+
* Example output: "2:30 PM"
|
|
1648
|
+
*
|
|
1649
|
+
* @param date The date to format
|
|
1650
|
+
* @returns A formatted time string in HH:MM AM/PM format
|
|
1651
|
+
*/
|
|
1101
1652
|
static printTimeOnly(date) {
|
|
1102
1653
|
date = new Date(date);
|
|
1103
1654
|
let period = '';
|
|
@@ -1116,9 +1667,25 @@ class Utilities {
|
|
|
1116
1667
|
const timeString = hour + ':' + minute + ' ' + period;
|
|
1117
1668
|
return timeString;
|
|
1118
1669
|
}
|
|
1670
|
+
/**
|
|
1671
|
+
* Combines date and time formatting into a single readable string.
|
|
1672
|
+
* Example output: "Monday, 1st January 2024 at 2:30 PM"
|
|
1673
|
+
*
|
|
1674
|
+
* @param date The date to format
|
|
1675
|
+
* @param separator The text to insert between date and time (defaults to 'at')
|
|
1676
|
+
* @returns A combined date and time string
|
|
1677
|
+
*/
|
|
1119
1678
|
static printDate(date, separator = 'at') {
|
|
1120
1679
|
return `${Utilities.printDateOnly(date)} ${separator} ${Utilities.printTimeOnly(date)}`;
|
|
1121
1680
|
}
|
|
1681
|
+
/**
|
|
1682
|
+
* Formats a date using friendly relative terminology for recent dates.
|
|
1683
|
+
* Returns "Today", "Yesterday", or full date for older dates.
|
|
1684
|
+
*
|
|
1685
|
+
* @param date The date to format
|
|
1686
|
+
* @param separator The text to insert between "Today"/"Yesterday" and time (defaults to '-')
|
|
1687
|
+
* @returns A friendly date string (e.g., "Today - 2:30 PM" or full date for older dates)
|
|
1688
|
+
*/
|
|
1122
1689
|
static printFriendlyDate(date, separator = '-') {
|
|
1123
1690
|
const today = new Date();
|
|
1124
1691
|
today.setHours(0, 0, 0, 0);
|
|
@@ -1135,6 +1702,15 @@ class Utilities {
|
|
|
1135
1702
|
return Utilities.printDate(date, separator);
|
|
1136
1703
|
}
|
|
1137
1704
|
}
|
|
1705
|
+
/**
|
|
1706
|
+
* Formats a date to a short format with time.
|
|
1707
|
+
* Example output: "03/01/2024 - 2:30 PM"
|
|
1708
|
+
*
|
|
1709
|
+
* @param date The date to format
|
|
1710
|
+
* @param separator The separator between month, day, and year (defaults to '/')
|
|
1711
|
+
* @param dateTimeSeparator The separator between date and time (defaults to '-')
|
|
1712
|
+
* @returns A formatted short date string with time
|
|
1713
|
+
*/
|
|
1138
1714
|
static printShortDate(date, separator = '/', dateTimeSeparator = '-') {
|
|
1139
1715
|
let day = date.getDate().toString();
|
|
1140
1716
|
let month = (date.getMonth() + 1).toString();
|
|
@@ -1147,6 +1723,14 @@ class Utilities {
|
|
|
1147
1723
|
}
|
|
1148
1724
|
return `${month}${separator}${day}${separator}${year} ${dateTimeSeparator} ${Utilities.printTimeOnly(date)}`;
|
|
1149
1725
|
}
|
|
1726
|
+
/**
|
|
1727
|
+
* Parses various date formats into a Date object.
|
|
1728
|
+
* Supports Date objects, ISO strings, and timestamps (milliseconds).
|
|
1729
|
+
* Automatically appends 'Z' to date strings without timezone information.
|
|
1730
|
+
*
|
|
1731
|
+
* @param date The date in various formats (Date, string, or number/timestamp)
|
|
1732
|
+
* @returns A Date object, or an empty string if input is null/undefined
|
|
1733
|
+
*/
|
|
1150
1734
|
static parseDate(date) {
|
|
1151
1735
|
if (date) {
|
|
1152
1736
|
if (date instanceof Date) {
|
|
@@ -1164,6 +1748,15 @@ class Utilities {
|
|
|
1164
1748
|
}
|
|
1165
1749
|
return "";
|
|
1166
1750
|
}
|
|
1751
|
+
/**
|
|
1752
|
+
* Calculates and formats the duration between two dates.
|
|
1753
|
+
* Breaks down the duration into days, hours, minutes, and seconds.
|
|
1754
|
+
* Example output: "2 days, 3 hours, 15 minutes and 30 seconds"
|
|
1755
|
+
*
|
|
1756
|
+
* @param start The start date
|
|
1757
|
+
* @param end The end date
|
|
1758
|
+
* @returns A human-readable duration string
|
|
1759
|
+
*/
|
|
1167
1760
|
static printDuration(start, end) {
|
|
1168
1761
|
start = new Date(start);
|
|
1169
1762
|
end = new Date(end);
|
|
@@ -1198,6 +1791,14 @@ class Utilities {
|
|
|
1198
1791
|
}
|
|
1199
1792
|
return printedDays;
|
|
1200
1793
|
}
|
|
1794
|
+
/**
|
|
1795
|
+
* Calculates the age in years between two dates.
|
|
1796
|
+
* Properly accounts for whether the birthday has occurred in the current year.
|
|
1797
|
+
*
|
|
1798
|
+
* @param birthDate The birth date (can be string, number timestamp, or Date object)
|
|
1799
|
+
* @param otherDate The reference date to calculate age from (can be string, number timestamp, or Date object)
|
|
1800
|
+
* @returns The age in years as an integer
|
|
1801
|
+
*/
|
|
1201
1802
|
static getAge(birthDate, otherDate) {
|
|
1202
1803
|
birthDate = new Date(birthDate);
|
|
1203
1804
|
otherDate = new Date(otherDate);
|
|
@@ -1208,6 +1809,13 @@ class Utilities {
|
|
|
1208
1809
|
}
|
|
1209
1810
|
return years;
|
|
1210
1811
|
}
|
|
1812
|
+
/**
|
|
1813
|
+
* Performs a deep clone of an array, recursively copying nested arrays and objects.
|
|
1814
|
+
* Primitive types are returned unchanged, and handles circular references safely.
|
|
1815
|
+
*
|
|
1816
|
+
* @param items The array to clone
|
|
1817
|
+
* @returns A deep copy of the array with all nested structures cloned
|
|
1818
|
+
*/
|
|
1211
1819
|
static deepCloneArray(items) {
|
|
1212
1820
|
return items.map(item => {
|
|
1213
1821
|
if (Array.isArray(item)) {
|
|
@@ -1224,6 +1832,15 @@ class Utilities {
|
|
|
1224
1832
|
return item; // Return primitive types unchanged
|
|
1225
1833
|
});
|
|
1226
1834
|
}
|
|
1835
|
+
/**
|
|
1836
|
+
* Searches for a term within an array of values using case-sensitive or case-insensitive matching.
|
|
1837
|
+
* Joins all values into a single string and searches for the term within it.
|
|
1838
|
+
*
|
|
1839
|
+
* @param searchTerm The term to search for
|
|
1840
|
+
* @param caseSensitive Whether the search should be case-sensitive (defaults to false)
|
|
1841
|
+
* @param values Variable number of values to search within
|
|
1842
|
+
* @returns true if the search term is found, false otherwise
|
|
1843
|
+
*/
|
|
1227
1844
|
static searchArray(searchTerm, caseSensitive, ...values) {
|
|
1228
1845
|
if (!searchTerm) {
|
|
1229
1846
|
return true;
|
|
@@ -1236,6 +1853,14 @@ class Utilities {
|
|
|
1236
1853
|
}
|
|
1237
1854
|
return data.indexOf(filter) != -1;
|
|
1238
1855
|
}
|
|
1856
|
+
/**
|
|
1857
|
+
* Moves an item within an array from one index to another.
|
|
1858
|
+
* Handles negative indices (counts from the end) and automatically extends the array if needed.
|
|
1859
|
+
*
|
|
1860
|
+
* @param array The array to modify
|
|
1861
|
+
* @param oldIndex The current index of the item to move
|
|
1862
|
+
* @param newIndex The target index to move the item to
|
|
1863
|
+
*/
|
|
1239
1864
|
static moveArrayItem(array, oldIndex, newIndex) {
|
|
1240
1865
|
if (oldIndex < 0) {
|
|
1241
1866
|
return;
|
|
@@ -1251,6 +1876,14 @@ class Utilities {
|
|
|
1251
1876
|
}
|
|
1252
1877
|
array.splice(newIndex, 0, array.splice(oldIndex, 1)[0]);
|
|
1253
1878
|
}
|
|
1879
|
+
/**
|
|
1880
|
+
* Expands camelCase text into space-separated words.
|
|
1881
|
+
* Handles both camelCase and PascalCase patterns as well as non-alphanumeric characters.
|
|
1882
|
+
* Example: "myPropertyName" becomes "my Property Name"
|
|
1883
|
+
*
|
|
1884
|
+
* @param text The camelCase text to expand
|
|
1885
|
+
* @returns The expanded text with spaces between words
|
|
1886
|
+
*/
|
|
1254
1887
|
static expandCamelCase(text) {
|
|
1255
1888
|
if (!text) {
|
|
1256
1889
|
return text;
|
|
@@ -1259,10 +1892,24 @@ class Utilities {
|
|
|
1259
1892
|
.replace(/([A-Z][A-Z]+)/g, ' $1')
|
|
1260
1893
|
.replace(/([^A-Za-z ]+)/g, ' $1');
|
|
1261
1894
|
}
|
|
1895
|
+
/**
|
|
1896
|
+
* Tests whether a URL is absolute (starts with protocol or //).
|
|
1897
|
+
* Matches URLs like "http://example.com" or "//example.com"
|
|
1898
|
+
*
|
|
1899
|
+
* @param url The URL to test
|
|
1900
|
+
* @returns true if the URL is absolute, false if it's relative
|
|
1901
|
+
*/
|
|
1262
1902
|
static testIsAbsoluteUrl(url) {
|
|
1263
1903
|
const r = new RegExp('^(?:[a-z]+:)?//', 'i');
|
|
1264
1904
|
return r.test(url);
|
|
1265
1905
|
}
|
|
1906
|
+
/**
|
|
1907
|
+
* Converts a relative URL to an absolute URL by prepending //.
|
|
1908
|
+
* If the URL is already absolute, it's returned unchanged.
|
|
1909
|
+
*
|
|
1910
|
+
* @param url The URL to convert
|
|
1911
|
+
* @returns The absolute URL (e.g., "//example.com" for "example.com")
|
|
1912
|
+
*/
|
|
1266
1913
|
static convertToAbsoluteUrl(url) {
|
|
1267
1914
|
return Utilities.testIsAbsoluteUrl(url) ? url : '//' + url;
|
|
1268
1915
|
}
|
|
@@ -29091,76 +29738,16 @@ class SafeHtmlPipe {
|
|
|
29091
29738
|
return this.sanitizer.bypassSecurityTrustHtml(value);
|
|
29092
29739
|
}
|
|
29093
29740
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: SafeHtmlPipe, deps: [{ token: i1$1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe });
|
|
29094
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.19", ngImport: i0, type: SafeHtmlPipe, isStandalone:
|
|
29741
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.19", ngImport: i0, type: SafeHtmlPipe, isStandalone: true, name: "safeHtml" });
|
|
29095
29742
|
}
|
|
29096
29743
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: SafeHtmlPipe, decorators: [{
|
|
29097
29744
|
type: Pipe,
|
|
29098
29745
|
args: [{
|
|
29099
29746
|
name: 'safeHtml',
|
|
29100
|
-
standalone:
|
|
29747
|
+
standalone: true
|
|
29101
29748
|
}]
|
|
29102
29749
|
}], ctorParameters: () => [{ type: i1$1.DomSanitizer }] });
|
|
29103
29750
|
|
|
29104
|
-
class CwmSharedModule {
|
|
29105
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: CwmSharedModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
29106
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.19", ngImport: i0, type: CwmSharedModule, declarations: [ExternalNavigationComponent,
|
|
29107
|
-
NavigateToRouteComponent,
|
|
29108
|
-
PageNotFoundComponent,
|
|
29109
|
-
SafeHtmlPipe], imports: [CommonModule,
|
|
29110
|
-
RouterModule,
|
|
29111
|
-
MsalModule,
|
|
29112
|
-
BrowserModule,
|
|
29113
|
-
FormsModule], exports: [CommonModule,
|
|
29114
|
-
RouterModule,
|
|
29115
|
-
MsalModule,
|
|
29116
|
-
ExternalNavigationComponent,
|
|
29117
|
-
NavigateToRouteComponent,
|
|
29118
|
-
PageNotFoundComponent,
|
|
29119
|
-
SafeHtmlPipe] });
|
|
29120
|
-
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: CwmSharedModule, imports: [CommonModule,
|
|
29121
|
-
RouterModule,
|
|
29122
|
-
MsalModule,
|
|
29123
|
-
BrowserModule,
|
|
29124
|
-
FormsModule, CommonModule,
|
|
29125
|
-
RouterModule,
|
|
29126
|
-
MsalModule] });
|
|
29127
|
-
}
|
|
29128
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: CwmSharedModule, decorators: [{
|
|
29129
|
-
type: NgModule,
|
|
29130
|
-
args: [{
|
|
29131
|
-
declarations: [
|
|
29132
|
-
ExternalNavigationComponent,
|
|
29133
|
-
NavigateToRouteComponent,
|
|
29134
|
-
PageNotFoundComponent,
|
|
29135
|
-
SafeHtmlPipe
|
|
29136
|
-
],
|
|
29137
|
-
imports: [
|
|
29138
|
-
CommonModule,
|
|
29139
|
-
RouterModule,
|
|
29140
|
-
MsalModule,
|
|
29141
|
-
BrowserModule,
|
|
29142
|
-
FormsModule
|
|
29143
|
-
],
|
|
29144
|
-
exports: [
|
|
29145
|
-
CommonModule,
|
|
29146
|
-
RouterModule,
|
|
29147
|
-
MsalModule,
|
|
29148
|
-
ExternalNavigationComponent,
|
|
29149
|
-
NavigateToRouteComponent,
|
|
29150
|
-
PageNotFoundComponent,
|
|
29151
|
-
SafeHtmlPipe
|
|
29152
|
-
]
|
|
29153
|
-
}]
|
|
29154
|
-
}] });
|
|
29155
|
-
|
|
29156
|
-
const MockInventoryApiResponse = DEFAULT_INVENTORY_API_RESPONSE;
|
|
29157
|
-
|
|
29158
|
-
const MockConfig = DEFAULT_CONFIGURATION;
|
|
29159
|
-
|
|
29160
|
-
const MockProfile = DEFAULT_PROFILE;
|
|
29161
|
-
|
|
29162
|
-
const MockCustomer = DEFAULT_CUSTOMER;
|
|
29163
|
-
|
|
29164
29751
|
/**********************************************************************************************************************/
|
|
29165
29752
|
class ConfigService {
|
|
29166
29753
|
authConfig;
|
|
@@ -29354,6 +29941,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.19", ngImpo
|
|
|
29354
29941
|
args: [DOCUMENT]
|
|
29355
29942
|
}] }, { type: ConfigService }, { type: i2$1.HttpClient }, { type: ClassLoggerService }] });
|
|
29356
29943
|
|
|
29944
|
+
const MockInventoryApiResponse = DEFAULT_INVENTORY_API_RESPONSE;
|
|
29945
|
+
|
|
29357
29946
|
class InventoryApiService extends BaseApiService {
|
|
29358
29947
|
useMockData = false;
|
|
29359
29948
|
constructor(document, configService, httpClient, logger) {
|
|
@@ -29427,6 +30016,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.19", ngImpo
|
|
|
29427
30016
|
* Provides a wrapper for accessing the web storage API and synchronizing session storage across tabs/windows.
|
|
29428
30017
|
*/
|
|
29429
30018
|
class LocalStorageService {
|
|
30019
|
+
static USER_DATA = 'user_data';
|
|
30020
|
+
static SYNC_KEYS = 'sync_keys';
|
|
29430
30021
|
static syncListenerInitialized = false;
|
|
29431
30022
|
syncKeys = [];
|
|
29432
30023
|
initEvent = new Subject();
|
|
@@ -29458,7 +30049,7 @@ class LocalStorageService {
|
|
|
29458
30049
|
}
|
|
29459
30050
|
clearAllSessionsStorage() {
|
|
29460
30051
|
this.clearInstanceSessionStorage();
|
|
29461
|
-
localStorage.removeItem(
|
|
30052
|
+
localStorage.removeItem(LocalStorageService.SYNC_KEYS);
|
|
29462
30053
|
localStorage.setItem('clearAllSessionsStorage', '_dummy');
|
|
29463
30054
|
localStorage.removeItem('clearAllSessionsStorage');
|
|
29464
30055
|
}
|
|
@@ -29469,23 +30060,23 @@ class LocalStorageService {
|
|
|
29469
30060
|
clearLocalStorage() {
|
|
29470
30061
|
localStorage.clear();
|
|
29471
30062
|
}
|
|
29472
|
-
saveSessionData(data, key =
|
|
30063
|
+
saveSessionData(data, key = LocalStorageService.USER_DATA) {
|
|
29473
30064
|
this.testForInvalidKeys(key);
|
|
29474
30065
|
this.removeFromSyncKeys(key);
|
|
29475
30066
|
localStorage.removeItem(key);
|
|
29476
30067
|
this.sessionStorageSetItem(key, data);
|
|
29477
30068
|
}
|
|
29478
|
-
saveSyncedSessionData(data, key =
|
|
30069
|
+
saveSyncedSessionData(data, key = LocalStorageService.USER_DATA) {
|
|
29479
30070
|
this.testForInvalidKeys(key);
|
|
29480
30071
|
localStorage.removeItem(key);
|
|
29481
30072
|
this.addToSessionStorage(data, key);
|
|
29482
30073
|
}
|
|
29483
|
-
savePermanentData(data, key =
|
|
30074
|
+
savePermanentData(data, key = LocalStorageService.USER_DATA) {
|
|
29484
30075
|
this.testForInvalidKeys(key);
|
|
29485
30076
|
this.removeFromSessionStorage(key);
|
|
29486
30077
|
this.localStorageSetItem(key, data);
|
|
29487
30078
|
}
|
|
29488
|
-
moveDataToSessionStorage(key =
|
|
30079
|
+
moveDataToSessionStorage(key = LocalStorageService.USER_DATA) {
|
|
29489
30080
|
this.testForInvalidKeys(key);
|
|
29490
30081
|
const data = this.getData(key);
|
|
29491
30082
|
if (data == null) {
|
|
@@ -29493,7 +30084,7 @@ class LocalStorageService {
|
|
|
29493
30084
|
}
|
|
29494
30085
|
this.saveSessionData(data, key);
|
|
29495
30086
|
}
|
|
29496
|
-
moveDataToSyncedSessionStorage(key =
|
|
30087
|
+
moveDataToSyncedSessionStorage(key = LocalStorageService.USER_DATA) {
|
|
29497
30088
|
this.testForInvalidKeys(key);
|
|
29498
30089
|
const data = this.getData(key);
|
|
29499
30090
|
if (data == null) {
|
|
@@ -29501,7 +30092,7 @@ class LocalStorageService {
|
|
|
29501
30092
|
}
|
|
29502
30093
|
this.saveSyncedSessionData(data, key);
|
|
29503
30094
|
}
|
|
29504
|
-
moveDataToPermanentStorage(key =
|
|
30095
|
+
moveDataToPermanentStorage(key = LocalStorageService.USER_DATA) {
|
|
29505
30096
|
this.testForInvalidKeys(key);
|
|
29506
30097
|
const data = this.getData(key);
|
|
29507
30098
|
if (data == null) {
|
|
@@ -29509,14 +30100,14 @@ class LocalStorageService {
|
|
|
29509
30100
|
}
|
|
29510
30101
|
this.savePermanentData(data, key);
|
|
29511
30102
|
}
|
|
29512
|
-
exists(key =
|
|
30103
|
+
exists(key = LocalStorageService.USER_DATA) {
|
|
29513
30104
|
let data = sessionStorage.getItem(key);
|
|
29514
30105
|
if (data == null) {
|
|
29515
30106
|
data = localStorage.getItem(key);
|
|
29516
30107
|
}
|
|
29517
30108
|
return data != null;
|
|
29518
30109
|
}
|
|
29519
|
-
getData(key =
|
|
30110
|
+
getData(key = LocalStorageService.USER_DATA) {
|
|
29520
30111
|
this.testForInvalidKeys(key);
|
|
29521
30112
|
let data = this.sessionStorageGetItem(key);
|
|
29522
30113
|
if (data == '') {
|
|
@@ -29524,7 +30115,7 @@ class LocalStorageService {
|
|
|
29524
30115
|
}
|
|
29525
30116
|
return data;
|
|
29526
30117
|
}
|
|
29527
|
-
getDataObject(key =
|
|
30118
|
+
getDataObject(key = LocalStorageService.USER_DATA, isDateType = false) {
|
|
29528
30119
|
let data = this.getData(key);
|
|
29529
30120
|
if (data != null) {
|
|
29530
30121
|
if (isDateType) {
|
|
@@ -29536,7 +30127,7 @@ class LocalStorageService {
|
|
|
29536
30127
|
return data;
|
|
29537
30128
|
}
|
|
29538
30129
|
}
|
|
29539
|
-
deleteData(key =
|
|
30130
|
+
deleteData(key = LocalStorageService.USER_DATA) {
|
|
29540
30131
|
this.testForInvalidKeys(key);
|
|
29541
30132
|
this.removeFromSessionStorage(key);
|
|
29542
30133
|
localStorage.removeItem(key);
|
|
@@ -29630,7 +30221,7 @@ class LocalStorageService {
|
|
|
29630
30221
|
this.syncKeys = this.getSyncKeysFromStorage();
|
|
29631
30222
|
}
|
|
29632
30223
|
getSyncKeysFromStorage(defaultValue = []) {
|
|
29633
|
-
const data = this.localStorageGetItem(
|
|
30224
|
+
const data = this.localStorageGetItem(LocalStorageService.SYNC_KEYS);
|
|
29634
30225
|
if (data == null) {
|
|
29635
30226
|
return defaultValue;
|
|
29636
30227
|
}
|
|
@@ -29648,7 +30239,7 @@ class LocalStorageService {
|
|
|
29648
30239
|
const storedSyncKeys = this.getSyncKeysFromStorage();
|
|
29649
30240
|
if (!storedSyncKeys.some(x => x == key)) {
|
|
29650
30241
|
storedSyncKeys.push(key);
|
|
29651
|
-
this.localStorageSetItem(
|
|
30242
|
+
this.localStorageSetItem(LocalStorageService.SYNC_KEYS, storedSyncKeys);
|
|
29652
30243
|
}
|
|
29653
30244
|
}
|
|
29654
30245
|
removeFromSyncKeysBackup(key) {
|
|
@@ -29656,7 +30247,7 @@ class LocalStorageService {
|
|
|
29656
30247
|
const index = storedSyncKeys.indexOf(key);
|
|
29657
30248
|
if (index > -1) {
|
|
29658
30249
|
storedSyncKeys.splice(index, 1);
|
|
29659
|
-
this.localStorageSetItem(
|
|
30250
|
+
this.localStorageSetItem(LocalStorageService.SYNC_KEYS, storedSyncKeys);
|
|
29660
30251
|
}
|
|
29661
30252
|
}
|
|
29662
30253
|
addToSyncKeysHelper(key) {
|
|
@@ -29842,14 +30433,94 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.19", ngImpo
|
|
|
29842
30433
|
args: [DOCUMENT]
|
|
29843
30434
|
}] }, { type: ConfigService }, { type: i2$1.HttpClient }, { type: ClassLoggerService }] });
|
|
29844
30435
|
|
|
30436
|
+
/**
|
|
30437
|
+
* Standalone Angular architecture for cwm-shared library
|
|
30438
|
+
* Export all standalone components, pipes, directives, and injectable services
|
|
30439
|
+
*/
|
|
30440
|
+
// Components
|
|
30441
|
+
|
|
30442
|
+
/**
|
|
30443
|
+
* @deprecated Use standalone components directly instead
|
|
30444
|
+
*
|
|
30445
|
+
* This module is maintained for backwards compatibility only.
|
|
30446
|
+
* New applications should import standalone components directly:
|
|
30447
|
+
*
|
|
30448
|
+
* @example
|
|
30449
|
+
* import { PageNotFoundComponent, SafeHtmlPipe } from '@transcommerce/cwm-shared';
|
|
30450
|
+
*
|
|
30451
|
+
* @Component({
|
|
30452
|
+
* selector: 'app-root',
|
|
30453
|
+
* standalone: true,
|
|
30454
|
+
* imports: [PageNotFoundComponent, SafeHtmlPipe]
|
|
30455
|
+
* })
|
|
30456
|
+
*/
|
|
30457
|
+
class CwmSharedModule {
|
|
30458
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: CwmSharedModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
30459
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.19", ngImport: i0, type: CwmSharedModule, imports: [CommonModule,
|
|
30460
|
+
RouterModule,
|
|
30461
|
+
MsalModule,
|
|
30462
|
+
BrowserModule,
|
|
30463
|
+
FormsModule,
|
|
30464
|
+
ExternalNavigationComponent,
|
|
30465
|
+
NavigateToRouteComponent,
|
|
30466
|
+
PageNotFoundComponent,
|
|
30467
|
+
SafeHtmlPipe], exports: [CommonModule,
|
|
30468
|
+
RouterModule,
|
|
30469
|
+
MsalModule,
|
|
30470
|
+
ExternalNavigationComponent,
|
|
30471
|
+
NavigateToRouteComponent,
|
|
30472
|
+
PageNotFoundComponent,
|
|
30473
|
+
SafeHtmlPipe] });
|
|
30474
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: CwmSharedModule, imports: [CommonModule,
|
|
30475
|
+
RouterModule,
|
|
30476
|
+
MsalModule,
|
|
30477
|
+
BrowserModule,
|
|
30478
|
+
FormsModule,
|
|
30479
|
+
ExternalNavigationComponent,
|
|
30480
|
+
NavigateToRouteComponent, CommonModule,
|
|
30481
|
+
RouterModule,
|
|
30482
|
+
MsalModule] });
|
|
30483
|
+
}
|
|
30484
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: CwmSharedModule, decorators: [{
|
|
30485
|
+
type: NgModule,
|
|
30486
|
+
args: [{
|
|
30487
|
+
imports: [
|
|
30488
|
+
CommonModule,
|
|
30489
|
+
RouterModule,
|
|
30490
|
+
MsalModule,
|
|
30491
|
+
BrowserModule,
|
|
30492
|
+
FormsModule,
|
|
30493
|
+
ExternalNavigationComponent,
|
|
30494
|
+
NavigateToRouteComponent,
|
|
30495
|
+
PageNotFoundComponent,
|
|
30496
|
+
SafeHtmlPipe
|
|
30497
|
+
],
|
|
30498
|
+
exports: [
|
|
30499
|
+
CommonModule,
|
|
30500
|
+
RouterModule,
|
|
30501
|
+
MsalModule,
|
|
30502
|
+
ExternalNavigationComponent,
|
|
30503
|
+
NavigateToRouteComponent,
|
|
30504
|
+
PageNotFoundComponent,
|
|
30505
|
+
SafeHtmlPipe
|
|
30506
|
+
]
|
|
30507
|
+
}]
|
|
30508
|
+
}] });
|
|
30509
|
+
|
|
30510
|
+
const MockConfig = DEFAULT_CONFIGURATION;
|
|
30511
|
+
|
|
30512
|
+
const MockProfile = DEFAULT_PROFILE;
|
|
30513
|
+
|
|
30514
|
+
const MockCustomer = DEFAULT_CUSTOMER;
|
|
30515
|
+
|
|
29845
30516
|
// ================================================
|
|
29846
30517
|
// Export every type you want this module to expose
|
|
29847
30518
|
// ================================================
|
|
29848
|
-
// Components
|
|
30519
|
+
// Standalone Components (can be used independently)
|
|
29849
30520
|
|
|
29850
30521
|
/**
|
|
29851
30522
|
* Generated bundle index. Do not edit.
|
|
29852
30523
|
*/
|
|
29853
30524
|
|
|
29854
|
-
export { API_CONFIG_SCHEMA, AUTH_CONFIG, AUTH_CONFIG_SCHEMA, BaseApiService, CAROUSEL_CONFIG_SCHEMA, CONFIGURATION_SCHEMA, COORDINATES_STYLE_SCHEMA, Category, ClassLoggerService, ColorStyle, ConfigService, CoordinatesStyle, Customer, CwmSharedModule, DEFAULT_API_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACKCOLOR, DEFAULT_CAROUSEL_CONFIG, DEFAULT_CATEGORY_SLIDE, DEFAULT_COMPANY_NAME, DEFAULT_CONFIGURATION, DEFAULT_COORDINATES, DEFAULT_CUSTOMER, DEFAULT_FONT, DEFAULT_FOOTER_SLIDE, DEFAULT_FORECOLOR, DEFAULT_HEX_COLOR, DEFAULT_IMAGE_STYLE, DEFAULT_INVENTORY_API_RESPONSE, DEFAULT_JUSTIFICATION, DEFAULT_MENU_BOARD_CONFIG, DEFAULT_NG_STYLE, DEFAULT_PROFILE, DEFAULT_RGBA_COLOR_STYLE, DEFAULT_SIZE_STYLE, DEFAULT_SLIDE, DEFAULT_SUBSCRIPTION_CONFIG, DEFAULT_TEXT_STYLE,
|
|
30525
|
+
export { API_CONFIG_SCHEMA, AUTH_CONFIG, AUTH_CONFIG_SCHEMA, BaseApiService, CAROUSEL_CONFIG_SCHEMA, CONFIGURATION_SCHEMA, COORDINATES_STYLE_SCHEMA, Category, ClassLoggerService, ColorStyle, ConfigService, CoordinatesStyle, Customer, CwmSharedModule, DEFAULT_API_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACKCOLOR, DEFAULT_CAROUSEL_CONFIG, DEFAULT_CATEGORY_SLIDE, DEFAULT_COMPANY_NAME, DEFAULT_CONFIGURATION, DEFAULT_COORDINATES, DEFAULT_CUSTOMER, DEFAULT_FONT, DEFAULT_FOOTER_SLIDE, DEFAULT_FORECOLOR, DEFAULT_HEX_COLOR, DEFAULT_IMAGE_STYLE, DEFAULT_INVENTORY_API_RESPONSE, DEFAULT_JUSTIFICATION, DEFAULT_MENU_BOARD_CONFIG, DEFAULT_NAMED_COLOR, DEFAULT_NG_STYLE, DEFAULT_PROFILE, DEFAULT_RGBA_COLOR_STYLE, DEFAULT_SIZE_STYLE, DEFAULT_SLIDE, DEFAULT_SUBSCRIPTION_CONFIG, DEFAULT_TEXT_STYLE, EXAMPLE_CREATE_SUBSCRIPTION_REQUEST, ExternalNavigationComponent, FONT_STYLE_SCHEMA, FOOTER_SLIDE_SCHEMA, FontStyle, FooterSlide, HEX_COLOR_SCHEMA, IMAGE_STYLE_SCHEMA, ImageStyle, InventoryApiService, JUSTIFICATIONS_SCHEMA, Justifications, LocalStorageService, LogService, MENU_BOARD_CONFIG_SCHEMA, MockConfig, MockCustomer, MockInventoryApiResponse, MockProfile, NAMED_COLOR_MAP, NAMED_COLOR_SCHEMA, NG_STYLE_SCHEMA, NamedColors, NavigateToRouteComponent, PageNotFoundComponent, Product, Profile, RGBA_COLOR_STYLE_SCHEMA, RgbaColorStyle, SIZE_STYLE_SCHEMA, SLIDE_SCHEMA, SUBSCRIPTION_CONFIG_SCHEMA, SafeHtmlPipe, SizeStyle, Stack, SubscriptionApiService, TEXT_STYLE_SCHEMA, TextStyle, TimeSpan, TimeSpanOverflowError, UserTypes, Utilities, adjustAlpha, cloneRGBA, createRGBA, cssStringToRGBA, darkenRGBA, decodeToken, doWithLock, getNamedColors, hexToNamedColor, hexToRGBA, isNamedColor, isTokenValid, isValidNamedColor, lightenRGBA, msalGuardConfigFactory, msalInstanceFactory, msalInterceptorConfigFactory, namedColorToHex, namedColorToHexColor, namedColorToRGBA, rgbaEquals, rgbaObjectToHex, rgbaToCssString, rgbaToHex, rgbaToNamedColor, waitFor };
|
|
29855
30526
|
//# sourceMappingURL=transcommerce-cwm-shared.mjs.map
|