http-request-manager 18.0.13 → 18.2.0

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.
Files changed (34) hide show
  1. package/esm2022/lib/http-request-manager.module.mjs +47 -7
  2. package/esm2022/lib/http-request-services-demo/http-request-services-demo.component.mjs +10 -6
  3. package/esm2022/lib/http-request-services-demo/local-storage-signals-demo/local-storage-signals-demo.component.mjs +184 -0
  4. package/esm2022/lib/http-request-services-demo/request-signals-manager-demo/models/sample-ai-prompt.mjs +9 -0
  5. package/esm2022/lib/http-request-services-demo/request-signals-manager-demo/models/sample-client-info.mjs +12 -0
  6. package/esm2022/lib/http-request-services-demo/request-signals-manager-demo/models/sample-mapper-client-info.mjs +14 -0
  7. package/esm2022/lib/http-request-services-demo/request-signals-manager-demo/request-signals-manager-demo.component.mjs +336 -0
  8. package/esm2022/lib/services/local-storage-manager-service/index.mjs +3 -2
  9. package/esm2022/lib/services/local-storage-manager-service/local-storage-signals-manager.service.mjs +277 -0
  10. package/esm2022/lib/services/local-storage-manager-service/models/setting-options.model.mjs +6 -2
  11. package/esm2022/lib/services/request-manager-services/http-manager-signals.service.mjs +199 -0
  12. package/esm2022/lib/services/request-manager-services/index.mjs +2 -1
  13. package/esm2022/lib/services/request-manager-services/request-signals.service.mjs +170 -0
  14. package/esm2022/lib/services/request-manager-services/rxjs-operators/request-polling.mjs +26 -7
  15. package/esm2022/lib/services/utils/object-merger.service.mjs +26 -6
  16. package/fesm2022/http-request-manager.mjs +2053 -842
  17. package/fesm2022/http-request-manager.mjs.map +1 -1
  18. package/http-request-manager-18.2.0.tgz +0 -0
  19. package/lib/http-request-manager.module.d.ts +24 -22
  20. package/lib/http-request-services-demo/http-request-services-demo.component.d.ts +8 -0
  21. package/lib/http-request-services-demo/local-storage-signals-demo/local-storage-signals-demo.component.d.ts +55 -0
  22. package/lib/http-request-services-demo/request-signals-manager-demo/models/sample-ai-prompt.d.ts +8 -0
  23. package/lib/http-request-services-demo/request-signals-manager-demo/models/sample-client-info.d.ts +14 -0
  24. package/lib/http-request-services-demo/request-signals-manager-demo/models/sample-mapper-client-info.d.ts +14 -0
  25. package/lib/http-request-services-demo/request-signals-manager-demo/request-signals-manager-demo.component.d.ts +106 -0
  26. package/lib/services/local-storage-manager-service/index.d.ts +2 -1
  27. package/lib/services/local-storage-manager-service/local-storage-signals-manager.service.d.ts +64 -0
  28. package/lib/services/request-manager-services/http-manager-signals.service.d.ts +38 -0
  29. package/lib/services/request-manager-services/index.d.ts +1 -0
  30. package/lib/services/request-manager-services/request-signals.service.d.ts +26 -0
  31. package/lib/services/request-manager-services/rxjs-operators/request-polling.d.ts +2 -7
  32. package/lib/services/utils/object-merger.service.d.ts +2 -0
  33. package/package.json +1 -1
  34. package/http-request-manager-18.0.13.tgz +0 -0
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, Injectable, APP_ID, Inject, InjectionToken, Injector, Optional, EventEmitter, Component, ViewEncapsulation, Input, Output, ViewChild, NgModule } from '@angular/core';
2
+ import { inject, Injectable, APP_ID, Inject, InjectionToken, Injector, Optional, signal, computed, effect, EventEmitter, Component, ViewEncapsulation, Input, Output, ViewChild, NgModule } from '@angular/core';
3
3
  import { ComponentStore } from '@ngrx/component-store';
4
4
  import { map, catchError, filter, delay, finalize, takeWhile, retry, startWith, tap, mergeMap, takeUntil, withLatestFrom, switchMap, concatMap, scan, distinctUntilChanged } from 'rxjs/operators';
5
5
  import { HttpClient, HttpHeaders, HttpEventType, HttpHeaderResponse, HttpErrorResponse, HTTP_INTERCEPTORS } from '@angular/common/http';
@@ -59,7 +59,11 @@ class SettingOptions {
59
59
  this.encrypted = encrypted;
60
60
  }
61
61
  static adapt(item) {
62
- return new SettingOptions((item?.storage) ? item.storage : StorageType.GLOBAL, (item?.expires) ? item.expires : 0, (item?.expiresIn) ? item.expiresIn : '', (item?.encrypted) ? item?.encrypted : false);
62
+ const storage = (typeof item?.storage !== 'undefined' && item?.storage !== null) ? Number(item.storage) : StorageType.GLOBAL;
63
+ const expires = (typeof item?.expires !== 'undefined' && item?.expires !== null) ? Number(item.expires) : 0;
64
+ const expiresIn = (typeof item?.expiresIn !== 'undefined' && item?.expiresIn !== null) ? item.expiresIn : '';
65
+ const encrypted = (typeof item?.encrypted !== 'undefined' && item?.encrypted !== null) ? Boolean(item.encrypted) : false;
66
+ return new SettingOptions(storage, expires, expiresIn, encrypted);
63
67
  }
64
68
  }
65
69
 
@@ -902,15 +906,34 @@ function delayedRetry(delayMs, maxRetry = DEFAULT_MAX_RETRIES) {
902
906
  }));
903
907
  }
904
908
 
905
- /**
906
- * @param pollInterval
907
- * @param stopCondition$
908
- * @param isPending$
909
- */
910
909
  function requestPolling(pollInterval, stopCondition$, isPending$) {
911
910
  return (source) => {
912
911
  return interval(pollInterval * 1000)
913
- .pipe(startWith(0), tap(() => isPending$.next(true)), mergeMap(() => source), tap(() => isPending$.next(false)), takeUntil(stopCondition$));
912
+ .pipe(startWith(0), tap(() => {
913
+ try {
914
+ if (isPending$ && typeof isPending$.next === 'function') {
915
+ isPending$.next(true);
916
+ }
917
+ else if (isPending$ && typeof isPending$.set === 'function') {
918
+ isPending$.set(true);
919
+ }
920
+ }
921
+ catch (e) {
922
+ // no-op if setting fails
923
+ }
924
+ }), mergeMap(() => source), tap(() => {
925
+ try {
926
+ if (isPending$ && typeof isPending$.next === 'function') {
927
+ isPending$.next(false);
928
+ }
929
+ else if (isPending$ && typeof isPending$.set === 'function') {
930
+ isPending$.set(false);
931
+ }
932
+ }
933
+ catch (e) {
934
+ // no-op if setting fails
935
+ }
936
+ }), takeUntil(stopCondition$));
914
937
  };
915
938
  }
916
939
 
@@ -1174,151 +1197,520 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
1174
1197
  args: [CONFIG_SETTINGS_TOKEN]
1175
1198
  }] }] });
1176
1199
 
1177
- class ApiRequest {
1178
- constructor(server = '', path, headers, adapter, mapper, polling, retry, stream, displayError, saveAs) {
1179
- this.server = server;
1180
- this.path = path;
1181
- this.headers = headers;
1182
- this.adapter = adapter;
1183
- this.mapper = mapper;
1184
- this.polling = polling;
1185
- this.retry = retry;
1186
- this.stream = stream;
1187
- this.displayError = displayError;
1188
- this.saveAs = saveAs;
1200
+ class RequestSignalsService {
1201
+ constructor() {
1202
+ this.http = inject(HttpClient);
1203
+ this.pathQueryService = inject(PathQueryService);
1204
+ this.headersService = inject(HeadersService);
1205
+ this.isPending = signal(false);
1206
+ this.progress = signal(0);
1207
+ this.isIdle = computed(() => !this.isPending());
1189
1208
  }
1190
- static adapt(item) {
1191
- const server = Array.isArray(item?.server) ? item.server.join('/') : item?.server || '';
1192
- return new ApiRequest(server, (item?.path) ? item.path : [], (item?.headers) ? item.headers : {}, item?.adapter, item?.mapper, item?.polling ? Math.floor(item.polling) : 0, item?.retry ? RetryOptions.adapt(item.retry) : RetryOptions.adapt(), (item?.stream) ? item.stream : false, (item?.displayError) ? item.displayError : false, item?.saveAs);
1209
+ getRecordRequest(options) {
1210
+ const urlPath = this.buildUrlPath(options);
1211
+ const headers = this.buildCombinedHeaders(options);
1212
+ this.isPending.set(true);
1213
+ return (options.stream)
1214
+ ? this.http.get(urlPath, headers).pipe(requestStreaming(), this.handleFinalize())
1215
+ : this.http.get(urlPath, headers).pipe(this.request(options), this.handleFinalize());
1193
1216
  }
1194
- }
1195
-
1196
- class RequestOptions {
1197
- constructor(path = [], headers = {}) {
1198
- this.path = path;
1199
- this.headers = headers;
1217
+ createRecordRequest(options, data) {
1218
+ const urlPath = this.buildUrlPath(options);
1219
+ const headers = this.buildCombinedHeaders(options);
1220
+ this.isPending.set(true);
1221
+ return (options.stream)
1222
+ ? this.http.post(urlPath, data, headers).pipe(requestStreaming(), this.handleFinalize())
1223
+ : this.http.post(urlPath, data, headers).pipe(this.request(options), this.handleFinalize());
1200
1224
  }
1201
- static adapt(item) {
1202
- return new RequestOptions(item?.path, item?.headers);
1225
+ updateRecordRequest(options, data) {
1226
+ const urlPath = this.buildUrlPath(options);
1227
+ const headers = this.buildHeaders(options);
1228
+ this.isPending.set(true);
1229
+ return this.http.put(urlPath, data, headers).pipe(this.request(options), this.handleFinalize());
1203
1230
  }
1204
- }
1205
-
1206
- class ObjectMergerService {
1207
- constructor(configOptions) {
1208
- this.configOptions = configOptions;
1209
- if (!this.configOptions)
1210
- this.configOptions = ConfigOptions.adapt();
1231
+ deleteRecordRequest(options) {
1232
+ const urlPath = this.buildUrlPath(options);
1233
+ const headers = this.buildHeaders(options);
1234
+ this.isPending.set(true);
1235
+ return this.http.delete(urlPath, headers).pipe(this.request(options), this.handleFinalize());
1211
1236
  }
1212
- mergeOptions(options = ApiRequest.adapt()) {
1213
- const configForRoot = (this.configOptions?.httpRequestOptions) ? this.configOptions.httpRequestOptions : ConfigHTTPOptions.adapt();
1214
- const mergedOptions = ApiRequest.adapt(options);
1215
- mergedOptions.server = (options && options.server === '') ? configForRoot?.server || '' : options.server;
1216
- mergedOptions.path = [...options.path || [], ...configForRoot?.path || []];
1217
- mergedOptions.headers = { ...options.headers || {}, ...configForRoot?.headers || {} };
1218
- mergedOptions.retry = (options && (options?.retry?.times !== 0 || options?.retry?.delay !== 3)) ? options.retry : configForRoot?.retry || { times: 0, delay: 3 };
1219
- mergedOptions.polling = (options && options?.polling !== 0) ? options.polling : configForRoot?.polling || 0;
1220
- mergedOptions.displayError = (options && options?.displayError) ? options.displayError : configForRoot?.displayError || false;
1221
- mergedOptions.stream = (options && options?.stream) ? options.stream : configForRoot?.stream || false;
1222
- return mergedOptions;
1237
+ buildUrlPath(options) {
1238
+ return this.pathQueryService.buildAPIPath(options.server, options.path);
1223
1239
  }
1224
- mergeStorageOptions(options = SettingOptions.adapt()) {
1225
- const configForRoot = (this.configOptions?.LocalStorageOptions) ? this.configOptions.LocalStorageOptions : LocalStorageOptions.adapt();
1226
- const configForRootOptions = configForRoot.options || LocalStorageOptions.adapt().options;
1227
- const mergedOptions = SettingOptions.adapt(options);
1228
- mergedOptions.storage = (options && options.storage === 0) ? configForRootOptions?.storage || 0 : options.storage;
1229
- mergedOptions.expires = (options && options.expires === 0) ? configForRootOptions?.expires || 0 : options.expires;
1230
- mergedOptions.expiresIn = (options && options.expiresIn === '') ? configForRootOptions?.expiresIn || '' : options.expiresIn;
1231
- mergedOptions.encrypted = (options && !options.encrypted) ? configForRootOptions?.encrypted || false : options.encrypted;
1232
- return mergedOptions;
1240
+ buildHeaders(options) {
1241
+ return this.headersService.generateHeaders(options.headers);
1233
1242
  }
1234
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ObjectMergerService, deps: [{ token: CONFIG_SETTINGS_TOKEN, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
1235
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ObjectMergerService, providedIn: 'root' }); }
1243
+ buildCombinedHeaders(options) {
1244
+ const headers = this.headersService.generateHeaders(options.headers);
1245
+ return this.combineHeaders(headers, options.stream || false);
1246
+ }
1247
+ request(options) {
1248
+ return (source$) => {
1249
+ return source$.pipe(map(data => {
1250
+ if (options?.adapter) {
1251
+ return Array.isArray(data)
1252
+ ? data.map((item) => options.adapter(item))
1253
+ : options.adapter(data);
1254
+ }
1255
+ return data;
1256
+ }));
1257
+ };
1258
+ }
1259
+ downloadFileRequest(options) {
1260
+ this.isPending.set(true);
1261
+ const urlPath = this.buildUrlPath(options);
1262
+ return this.http.get(urlPath, { responseType: 'blob', observe: 'events', reportProgress: true })
1263
+ .pipe(map((event) => {
1264
+ this.isPending.set(true);
1265
+ if (event instanceof HttpHeaderResponse) {
1266
+ if (event.status !== 200) {
1267
+ this.isPending.set(false);
1268
+ throw new Error('Download failed');
1269
+ }
1270
+ }
1271
+ switch (event.type) {
1272
+ case HttpEventType.DownloadProgress:
1273
+ const status = (event.total) ? Math.round(event.loaded / (event.total || 1) * 100) : 100;
1274
+ this.progress.set(status);
1275
+ return status;
1276
+ case HttpEventType.Response:
1277
+ try {
1278
+ const fileNamePath = (options?.saveAs) ? options.saveAs : (options.path) ? options.path[options.path.length - 1] : [];
1279
+ const header_content = event.headers.get('Content-Disposition') || '';
1280
+ const file = (header_content) ? header_content.split('=')[1].substring(0, header_content.split('=')[1].length) : '';
1281
+ const fileName = (fileNamePath !== '') ? fileNamePath : file;
1282
+ if (fileName === '') {
1283
+ this.isPending.set(false);
1284
+ throw new Error('Save File: (file name and extension) not found in Headers or Path');
1285
+ }
1286
+ this.downloadFile(fileName, event.body);
1287
+ this.isPending.set(false);
1288
+ return 100;
1289
+ }
1290
+ catch (error) {
1291
+ throw new Error('Download failed');
1292
+ }
1293
+ default:
1294
+ this.isPending.set(false);
1295
+ return 0;
1296
+ }
1297
+ }), catchError(err => {
1298
+ return throwError(() => err);
1299
+ }));
1300
+ }
1301
+ handleFinalize() {
1302
+ return finalize(() => this.isPending.set(false));
1303
+ }
1304
+ downloadFile(file, fileData) {
1305
+ const navigatorAny = window.navigator;
1306
+ const extension = file.split('.')[1]?.toLowerCase();
1307
+ const newBlob = new Blob([fileData], { type: this.createFileType(extension) });
1308
+ if (navigatorAny.msSaveOrOpenBlob) {
1309
+ navigatorAny.msSaveOrOpenBlob(newBlob, file);
1310
+ }
1311
+ else {
1312
+ const link = document.createElement('a');
1313
+ const url = window.URL.createObjectURL(newBlob);
1314
+ link.href = url;
1315
+ link.download = file;
1316
+ link.click();
1317
+ window.URL.revokeObjectURL(url);
1318
+ }
1319
+ }
1320
+ createFileType(ext) {
1321
+ let fileType = "";
1322
+ if (ext === 'pdf' || ext === 'csv')
1323
+ fileType = `application/${ext}`;
1324
+ else if (ext === 'jpeg' || ext === 'jpg' || ext === 'png')
1325
+ fileType = `image/${ext}`;
1326
+ else if (ext === 'txt')
1327
+ fileType = 'text/plain';
1328
+ else if (ext === 'ppt' || ext === 'pot' || ext === 'pps' || ext === 'ppa')
1329
+ fileType = 'application/vnd.ms-powerpoint';
1330
+ else if (ext === 'pptx')
1331
+ fileType = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
1332
+ else if (ext === 'doc' || ext === 'dot')
1333
+ fileType = 'application/msword';
1334
+ else if (ext === 'docx')
1335
+ fileType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
1336
+ else if (ext === 'xls' || ext === 'xlt' || ext === 'xla')
1337
+ fileType = 'application/vnd.ms-excel';
1338
+ else if (ext === 'xlsx')
1339
+ fileType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
1340
+ return fileType;
1341
+ }
1342
+ combineHeaders(headers, isStreaming) {
1343
+ return (isStreaming)
1344
+ ? {
1345
+ ...headers,
1346
+ observe: 'events',
1347
+ responseType: 'text',
1348
+ reportProgress: true,
1349
+ Accept: 'text/event-stream'
1350
+ }
1351
+ : headers;
1352
+ }
1353
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RequestSignalsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1354
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RequestSignalsService, providedIn: 'root' }); }
1236
1355
  }
1237
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ObjectMergerService, decorators: [{
1356
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RequestSignalsService, decorators: [{
1238
1357
  type: Injectable,
1239
1358
  args: [{
1240
1359
  providedIn: 'root'
1241
1360
  }]
1242
- }], ctorParameters: () => [{ type: ConfigOptions, decorators: [{
1243
- type: Inject,
1244
- args: [CONFIG_SETTINGS_TOKEN]
1245
- }, {
1246
- type: Optional
1247
- }] }] });
1361
+ }] });
1248
1362
 
1249
- const storage = {
1250
- localStores: [],
1251
- sessionStores: [],
1252
- settings: [],
1253
- };
1254
- class LocalStorageManagerService extends ComponentStore {
1255
- startTimer() {
1256
- const timer$ = interval(1000 * 3).pipe(withLatestFrom(this.data$), map(([_, state]) => state), tap((state) => {
1257
- const expired = this.expired(state) ? this.expired(state) : [];
1258
- if (expired.length > 0) {
1259
- const ids = expired.map((item) => item.id);
1260
- const updatedState = {
1261
- ...state,
1262
- localStores: state.localStores.filter((item) => !ids.includes(item.id)),
1263
- sessionStores: state.sessionStores.filter((item) => !ids.includes(item.id)),
1264
- settings: state.settings.filter((item) => !ids.includes(item.id)),
1265
- };
1266
- this.persistState(updatedState);
1267
- this.updateState(updatedState);
1268
- }
1269
- }));
1270
- timer$.subscribe();
1271
- }
1363
+ class HTTPManagerSignalsService extends RequestSignalsService {
1272
1364
  constructor(configOptions) {
1273
- super(storage);
1365
+ super();
1274
1366
  this.configOptions = configOptions;
1275
- this.storageName = 'storage';
1276
- this.storageSettingsName = 'global-storage';
1277
- this.defaultOptions = SettingOptions.adapt();
1278
- this.stateRetrieved = false;
1279
- this.encrypted = false;
1280
- this.app = inject(AppService);
1281
- this.utils = inject(UtilsService);
1367
+ this.toastMessage = inject(ToastMessageDisplayService);
1368
+ this.ng_injector = inject(Injector);
1282
1369
  this.objectMergerService = inject(ObjectMergerService);
1283
- this.encryption = inject(SymmetricalEncryptionService);
1284
- this.encryptionTest = inject(EncryptionTestService);
1285
- // SELECTORS
1286
- this.data$ = this.select((state) => state);
1287
- this.stores$ = this.select((state) => state.settings);
1288
- this.storeExists$ = (store) => this.select(this.data$, (data) => data.settings.find(item => item.name === store) ? true : false);
1289
- this.store$ = (store) => this.select(this.data$, (data) => {
1290
- const foundStore = data.settings.find(item => item.name === store);
1291
- if (foundStore) {
1292
- const options = SettingOptions.adapt(foundStore.options);
1293
- const found = foundStore.options?.storage === StorageType.GLOBAL
1294
- ? data.localStores.find(item => item.id === foundStore.id)
1295
- : data.sessionStores.find(item => item.id === foundStore.id);
1296
- if (!found) {
1297
- this.deleteStore({ name: store });
1298
- return;
1299
- }
1300
- if (!this.app?.appID) {
1301
- console.warn('No App ID found - AppId not Provided');
1302
- return;
1303
- }
1304
- const storageData = (options.encrypted) ? this.encryption.decrypt(found.data, this.app.appID) : found.data;
1305
- return (this.isString(storageData)) ? JSON.parse(storageData) : storageData;
1306
- }
1307
- else {
1308
- return null;
1309
- }
1310
- });
1311
- this.settings$ = this.select(state => (state) ? state.settings : storage.settings);
1312
- this.setting$ = (store) => this.select(this.data$, (state) => {
1313
- const foundSetting = state.settings.find(item => item.name === store);
1314
- return (foundSetting) ? foundSetting : null;
1315
- });
1316
- this.persistence$ = this.data$
1317
- .subscribe(data => {
1318
- if (this.stateRetrieved)
1319
- this.persistState(data);
1320
- });
1321
- this.updateState = this.updater((state, updatedState) => ({
1370
+ // Replaced BehaviorSubjects with Angular signals
1371
+ this.countdown = signal(0);
1372
+ this.error = signal(false);
1373
+ this.data = signal(null);
1374
+ this.polling$ = new Subject();
1375
+ this.config = ApiRequest.adapt();
1376
+ this.config = configOptions
1377
+ ? ApiRequest.adapt(configOptions.httpRequestOptions)
1378
+ : this.config;
1379
+ }
1380
+ // REQUESTS
1381
+ getRequest(options, params) {
1382
+ this.isPending.set(true);
1383
+ this.data.set(null);
1384
+ const updatedOptions = this.defineReqOptions(options, params);
1385
+ const func = this.getRecordRequest;
1386
+ const requests = this.createRequest(func, updatedOptions);
1387
+ return this.createObservable(updatedOptions, requests, func.name).pipe(tap(data => this.data.set(data)), finalize(() => this.isPending.set(false)), catchError((err) => {
1388
+ if (updatedOptions.displayError)
1389
+ this.handleErrorWithSnackBar(err);
1390
+ this.isPending.set(false);
1391
+ return this.handleError(err);
1392
+ }));
1393
+ }
1394
+ postRequest(data, options, params) {
1395
+ this.isPending.set(true);
1396
+ this.data.set(null);
1397
+ const updatedOptions = this.defineReqOptions(options, params);
1398
+ const func = this.createRecordRequest;
1399
+ const requests = this.createRequest(func, updatedOptions, data);
1400
+ return this.createObservable(updatedOptions, requests, func.name).pipe(tap(data => this.data.set(data)), finalize(() => this.isPending.set(false)), catchError((err) => {
1401
+ if (updatedOptions.displayError)
1402
+ this.handleErrorWithSnackBar(err);
1403
+ this.isPending.set(false);
1404
+ return this.handleError(err);
1405
+ }));
1406
+ }
1407
+ putRequest(data, options, params) {
1408
+ this.isPending.set(true);
1409
+ this.data.set(null);
1410
+ const updatedOptions = this.defineReqOptions(options, params);
1411
+ const func = this.updateRecordRequest;
1412
+ const requests = this.createRequest(func, updatedOptions, data);
1413
+ return this.createObservable(updatedOptions, requests, func.name).pipe(tap(data => this.data.set(data)), finalize(() => this.isPending.set(false)), catchError((err) => {
1414
+ if (updatedOptions.displayError)
1415
+ this.handleErrorWithSnackBar(err);
1416
+ this.isPending.set(false);
1417
+ return this.handleError(err);
1418
+ }));
1419
+ }
1420
+ deleteRequest(options, params) {
1421
+ this.isPending.set(true);
1422
+ this.data.set(null);
1423
+ const updatedOptions = this.defineReqOptions(options, params);
1424
+ const func = this.deleteRecordRequest;
1425
+ const requests = this.createRequest(func, updatedOptions);
1426
+ return this.createObservable(updatedOptions, requests, func.name).pipe(tap(data => this.data.set(data)), finalize(() => this.isPending.set(false)), catchError((err) => {
1427
+ if (updatedOptions.displayError)
1428
+ this.handleErrorWithSnackBar(err);
1429
+ this.isPending.set(false);
1430
+ return this.handleError(err);
1431
+ }));
1432
+ }
1433
+ downloadRequest(options, params, saveAs) {
1434
+ this.isPending.set(true);
1435
+ const updatedOptions = this.defineReqOptions(options, params);
1436
+ const func = this.downloadFileRequest;
1437
+ const requests = this.createRequest(func, updatedOptions);
1438
+ return this.createObservable(updatedOptions, requests, func.name).pipe(catchError((err) => {
1439
+ this.error.set(true);
1440
+ this.isPending.set(false);
1441
+ return this.handleError(err);
1442
+ }));
1443
+ }
1444
+ // --- private helpers ---
1445
+ createObservable(options, request$, funcName) {
1446
+ const polling = options.polling ? (options.polling > 0 ? true : false) : false;
1447
+ const isPolling = polling &&
1448
+ !(funcName === 'deleteRecordRequest' ||
1449
+ funcName === 'updateRecordRequest' ||
1450
+ funcName === 'createRecordRequest');
1451
+ this.polling$.next();
1452
+ const polling$ = (isPolling && options.polling) || 0 >= 3
1453
+ ? request$.pipe(requestPolling((options.polling || 0) + 1, this.polling$, this.isPending), tap(() => this.countdown.set(0)), tap(() => {
1454
+ if (!options.polling)
1455
+ return;
1456
+ const count = options.polling ? options.polling : 0;
1457
+ countdown(count)
1458
+ .pipe(map((x) => {
1459
+ const pollingInSec = options.polling || 0;
1460
+ const percentageCompleted = ((pollingInSec - x) / pollingInSec) * 100;
1461
+ return Math.round(percentageCompleted);
1462
+ }))
1463
+ .subscribe((countDownValue) => {
1464
+ this.countdown.set(countDownValue);
1465
+ });
1466
+ }))
1467
+ : request$.pipe(catchError((err) => {
1468
+ if (err instanceof HttpErrorResponse) {
1469
+ this.error.set(true);
1470
+ return this.handleError(err);
1471
+ }
1472
+ return throwError(() => err);
1473
+ }));
1474
+ return polling$.pipe(catchError((err, caught) => {
1475
+ if (err instanceof HttpErrorResponse) {
1476
+ this.error.set(true);
1477
+ if (isPolling)
1478
+ this.stopPolling();
1479
+ return this.handleError(err);
1480
+ }
1481
+ return throwError(() => err);
1482
+ }), options?.retry && options.retry.times > 0
1483
+ ? delayedRetry((options.retry.delay || 3) * 1000, (options.retry.times || 0) - 1)
1484
+ : (source) => source);
1485
+ }
1486
+ createRequest(func, options, data) {
1487
+ const dataItem = this.prepareRequestData(options, data, func.name);
1488
+ return func.bind(this)(dataItem.options, dataItem.data);
1489
+ }
1490
+ prepareRequestData(options, data, funcName) {
1491
+ if ((options.mapper && funcName === 'updateRecordRequest') || funcName === 'createRecordRequest') {
1492
+ if (options?.mapper) {
1493
+ data = options.mapper
1494
+ ? Array.isArray(data)
1495
+ ? map((item) => options.mapper(item))
1496
+ : options.mapper(data)
1497
+ : data;
1498
+ }
1499
+ }
1500
+ else {
1501
+ if (options?.adapter) {
1502
+ data = Array.isArray(data)
1503
+ ? map((item) => options.adapter(item))
1504
+ : options.adapter(data);
1505
+ }
1506
+ }
1507
+ return { options: options, data: data };
1508
+ }
1509
+ handleError(error) {
1510
+ this.error.set(error.message || `${error.status} - ${error.statusText}`);
1511
+ return throwError(() => error);
1512
+ }
1513
+ handleErrorWithSnackBar(error) {
1514
+ const displayError = ToastDisplay.adapt({
1515
+ message: error.message || `${error.status} - ${error.statusText}`,
1516
+ action: 'OK',
1517
+ color: ToastColors.ERROR,
1518
+ icon: 'error',
1519
+ duration: 5 * 1000,
1520
+ });
1521
+ this.toastMessage.toastMessage(displayError);
1522
+ }
1523
+ stopPolling() {
1524
+ this.isPending.set(false);
1525
+ this.polling$.next();
1526
+ }
1527
+ defineReqOptions(options, params) {
1528
+ const req = ApiRequest.adapt(options);
1529
+ if (req?.path)
1530
+ req.path = params ? [...req.path, ...params] : req.path;
1531
+ const optionsReq = req ? req : ApiRequest.adapt();
1532
+ const updatedOptions = this.objectMergerService.mergeOptions(optionsReq);
1533
+ return updatedOptions;
1534
+ }
1535
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: HTTPManagerSignalsService, deps: [{ token: CONFIG_SETTINGS_TOKEN, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
1536
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: HTTPManagerSignalsService, providedIn: 'root' }); }
1537
+ }
1538
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: HTTPManagerSignalsService, decorators: [{
1539
+ type: Injectable,
1540
+ args: [{
1541
+ providedIn: 'root',
1542
+ }]
1543
+ }], ctorParameters: () => [{ type: ConfigOptions, decorators: [{
1544
+ type: Optional
1545
+ }, {
1546
+ type: Inject,
1547
+ args: [CONFIG_SETTINGS_TOKEN]
1548
+ }] }] });
1549
+
1550
+ class ApiRequest {
1551
+ constructor(server = '', path, headers, adapter, mapper, polling, retry, stream, displayError, saveAs) {
1552
+ this.server = server;
1553
+ this.path = path;
1554
+ this.headers = headers;
1555
+ this.adapter = adapter;
1556
+ this.mapper = mapper;
1557
+ this.polling = polling;
1558
+ this.retry = retry;
1559
+ this.stream = stream;
1560
+ this.displayError = displayError;
1561
+ this.saveAs = saveAs;
1562
+ }
1563
+ static adapt(item) {
1564
+ const server = Array.isArray(item?.server) ? item.server.join('/') : item?.server || '';
1565
+ return new ApiRequest(server, (item?.path) ? item.path : [], (item?.headers) ? item.headers : {}, item?.adapter, item?.mapper, item?.polling ? Math.floor(item.polling) : 0, item?.retry ? RetryOptions.adapt(item.retry) : RetryOptions.adapt(), (item?.stream) ? item.stream : false, (item?.displayError) ? item.displayError : false, item?.saveAs);
1566
+ }
1567
+ }
1568
+
1569
+ class RequestOptions {
1570
+ constructor(path = [], headers = {}) {
1571
+ this.path = path;
1572
+ this.headers = headers;
1573
+ }
1574
+ static adapt(item) {
1575
+ return new RequestOptions(item?.path, item?.headers);
1576
+ }
1577
+ }
1578
+
1579
+ class ObjectMergerService {
1580
+ constructor(configOptions) {
1581
+ this.configOptions = configOptions;
1582
+ this.utils = inject(UtilsService);
1583
+ if (!this.configOptions)
1584
+ this.configOptions = ConfigOptions.adapt();
1585
+ }
1586
+ mergeOptions(options = ApiRequest.adapt()) {
1587
+ const configForRoot = (this.configOptions?.httpRequestOptions) ? this.configOptions.httpRequestOptions : ConfigHTTPOptions.adapt();
1588
+ const mergedOptions = ApiRequest.adapt(options);
1589
+ mergedOptions.server = (options && options.server === '') ? configForRoot?.server || '' : options.server;
1590
+ mergedOptions.path = [...options.path || [], ...configForRoot?.path || []];
1591
+ mergedOptions.headers = { ...options.headers || {}, ...configForRoot?.headers || {} };
1592
+ mergedOptions.retry = (options && (options?.retry?.times !== 0 || options?.retry?.delay !== 3)) ? options.retry : configForRoot?.retry || { times: 0, delay: 3 };
1593
+ mergedOptions.polling = (options && options?.polling !== 0) ? options.polling : configForRoot?.polling || 0;
1594
+ mergedOptions.displayError = (options && options?.displayError) ? options.displayError : configForRoot?.displayError || false;
1595
+ mergedOptions.stream = (options && options?.stream) ? options.stream : configForRoot?.stream || false;
1596
+ return mergedOptions;
1597
+ }
1598
+ mergeStorageOptions(options = SettingOptions.adapt()) {
1599
+ const configForRoot = (this.configOptions?.LocalStorageOptions) ? this.configOptions.LocalStorageOptions : LocalStorageOptions.adapt();
1600
+ const configForRootOptions = configForRoot.options || LocalStorageOptions.adapt().options;
1601
+ const mergedOptions = SettingOptions.adapt(options);
1602
+ // storage type
1603
+ mergedOptions.storage = (options && (typeof options.storage !== 'undefined')) ? options.storage : (configForRootOptions?.storage ?? 0);
1604
+ // expires: prefer explicit numeric expires; if not provided, compute from expiresIn string; otherwise use config default
1605
+ // if an explicit, non-zero numeric expires is provided, use it
1606
+ if (options && (typeof options.expires !== 'undefined') && options.expires !== null && Number(options.expires) > 0) {
1607
+ mergedOptions.expires = Number(options.expires);
1608
+ }
1609
+ else if (options && options.expiresIn) {
1610
+ // compute numeric epoch from expiresIn string using UtilsService if available
1611
+ try {
1612
+ mergedOptions.expires = this.utils ? this.utils.expires(options.expiresIn) : 0;
1613
+ }
1614
+ catch {
1615
+ mergedOptions.expires = 0;
1616
+ }
1617
+ }
1618
+ else {
1619
+ mergedOptions.expires = configForRootOptions?.expires ?? 0;
1620
+ }
1621
+ // keep expiresIn string if explicitly provided, otherwise use config default
1622
+ mergedOptions.expiresIn = (options && (typeof options.expiresIn !== 'undefined')) ? options.expiresIn : (configForRootOptions?.expiresIn || '');
1623
+ mergedOptions.encrypted = (options && (typeof options.encrypted !== 'undefined')) ? options.encrypted : (configForRootOptions?.encrypted || false);
1624
+ return mergedOptions;
1625
+ }
1626
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ObjectMergerService, deps: [{ token: CONFIG_SETTINGS_TOKEN, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
1627
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ObjectMergerService, providedIn: 'root' }); }
1628
+ }
1629
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ObjectMergerService, decorators: [{
1630
+ type: Injectable,
1631
+ args: [{
1632
+ providedIn: 'root'
1633
+ }]
1634
+ }], ctorParameters: () => [{ type: ConfigOptions, decorators: [{
1635
+ type: Inject,
1636
+ args: [CONFIG_SETTINGS_TOKEN]
1637
+ }, {
1638
+ type: Optional
1639
+ }] }] });
1640
+
1641
+ const storage = {
1642
+ localStores: [],
1643
+ sessionStores: [],
1644
+ settings: [],
1645
+ };
1646
+ class LocalStorageManagerService extends ComponentStore {
1647
+ startTimer() {
1648
+ const timer$ = interval(1000 * 3).pipe(withLatestFrom(this.data$), map(([_, state]) => state), tap((state) => {
1649
+ const expired = this.expired(state) ? this.expired(state) : [];
1650
+ if (expired.length > 0) {
1651
+ const ids = expired.map((item) => item.id);
1652
+ const updatedState = {
1653
+ ...state,
1654
+ localStores: state.localStores.filter((item) => !ids.includes(item.id)),
1655
+ sessionStores: state.sessionStores.filter((item) => !ids.includes(item.id)),
1656
+ settings: state.settings.filter((item) => !ids.includes(item.id)),
1657
+ };
1658
+ this.persistState(updatedState);
1659
+ this.updateState(updatedState);
1660
+ }
1661
+ }));
1662
+ timer$.subscribe();
1663
+ }
1664
+ constructor(configOptions) {
1665
+ super(storage);
1666
+ this.configOptions = configOptions;
1667
+ this.storageName = 'storage';
1668
+ this.storageSettingsName = 'global-storage';
1669
+ this.defaultOptions = SettingOptions.adapt();
1670
+ this.stateRetrieved = false;
1671
+ this.encrypted = false;
1672
+ this.app = inject(AppService);
1673
+ this.utils = inject(UtilsService);
1674
+ this.objectMergerService = inject(ObjectMergerService);
1675
+ this.encryption = inject(SymmetricalEncryptionService);
1676
+ this.encryptionTest = inject(EncryptionTestService);
1677
+ // SELECTORS
1678
+ this.data$ = this.select((state) => state);
1679
+ this.stores$ = this.select((state) => state.settings);
1680
+ this.storeExists$ = (store) => this.select(this.data$, (data) => data.settings.find(item => item.name === store) ? true : false);
1681
+ this.store$ = (store) => this.select(this.data$, (data) => {
1682
+ const foundStore = data.settings.find(item => item.name === store);
1683
+ if (foundStore) {
1684
+ const options = SettingOptions.adapt(foundStore.options);
1685
+ const found = foundStore.options?.storage === StorageType.GLOBAL
1686
+ ? data.localStores.find(item => item.id === foundStore.id)
1687
+ : data.sessionStores.find(item => item.id === foundStore.id);
1688
+ if (!found) {
1689
+ this.deleteStore({ name: store });
1690
+ return;
1691
+ }
1692
+ if (!this.app?.appID) {
1693
+ console.warn('No App ID found - AppId not Provided');
1694
+ return;
1695
+ }
1696
+ const storageData = (options.encrypted) ? this.encryption.decrypt(found.data, this.app.appID) : found.data;
1697
+ return (this.isString(storageData)) ? JSON.parse(storageData) : storageData;
1698
+ }
1699
+ else {
1700
+ return null;
1701
+ }
1702
+ });
1703
+ this.settings$ = this.select(state => (state) ? state.settings : storage.settings);
1704
+ this.setting$ = (store) => this.select(this.data$, (state) => {
1705
+ const foundSetting = state.settings.find(item => item.name === store);
1706
+ return (foundSetting) ? foundSetting : null;
1707
+ });
1708
+ this.persistence$ = this.data$
1709
+ .subscribe(data => {
1710
+ if (this.stateRetrieved)
1711
+ this.persistState(data);
1712
+ });
1713
+ this.updateState = this.updater((state, updatedState) => ({
1322
1714
  ...updatedState,
1323
1715
  }));
1324
1716
  this.setStore = this.updater((state, store) => {
@@ -1457,54 +1849,308 @@ class LocalStorageManagerService extends ComponentStore {
1457
1849
  localStorage.setItem(this.storageSettingsName, settingsStr || '');
1458
1850
  }
1459
1851
  expired(state) {
1460
- if (!state)
1461
- return [];
1462
- return state.settings?.filter(item => (item.options?.expires || 0) > 0 && this.utils.hasExpired(item.options?.expires || 0));
1852
+ if (!state)
1853
+ return [];
1854
+ return state.settings?.filter(item => (item.options?.expires || 0) > 0 && this.utils.hasExpired(item.options?.expires || 0));
1855
+ }
1856
+ retrieveState() {
1857
+ const str = localStorage.getItem(this.storageSettingsName);
1858
+ const localStr = localStorage.getItem(this.storageName);
1859
+ const sessionStr = sessionStorage.getItem(this.storageName);
1860
+ const localData = (localStr) ? JSON.parse(localStr) : null;
1861
+ const sessionData = (sessionStr) ? JSON.parse(sessionStr) : null;
1862
+ const decryptedStr = str ? this.encryption.decrypt(str, this.app.appID) : null;
1863
+ const settings = (decryptedStr && decryptedStr !== null) ? JSON.parse(decryptedStr) : [];
1864
+ settings.forEach(store => {
1865
+ const expired = (store.options?.expires || 0) > 0 && this.utils.hasExpired(store.options?.expires || 0);
1866
+ if (!expired) {
1867
+ const hasStorage = this.hasGlobalStorage(store.options?.storage, store.id || '');
1868
+ if (!hasStorage) {
1869
+ this.createStore({ id: store.id, name: store.name, data: [], options: SettingOptions.adapt(store.options) });
1870
+ }
1871
+ else {
1872
+ const storeData = (store.options?.storage === StorageType.GLOBAL) ? localData.find(item => item.id === store.id) : sessionData.find(item => item.id === store.id);
1873
+ this.setStore({ id: store.id || '', name: store.name, data: storeData?.data, options: SettingOptions.adapt(store.options) });
1874
+ }
1875
+ }
1876
+ });
1877
+ this.stateRetrieved = true;
1878
+ }
1879
+ isString(obj) {
1880
+ return (Object.prototype.toString.call(obj) === '[object String]');
1881
+ }
1882
+ fixAndParseJSON(jsonString) {
1883
+ const fixedString = jsonString
1884
+ .replace(/'/g, '"')
1885
+ .replace(/([{,]\s*)(\w+)\s*:/g, '$1"$2":')
1886
+ .replace(/,\s*(\}|\])/g, '$1');
1887
+ try {
1888
+ return JSON.parse(fixedString);
1889
+ }
1890
+ catch (error) {
1891
+ throw new Error('Failed to parse JSON: ' + error.message);
1892
+ }
1893
+ }
1894
+ validStoreName(str) {
1895
+ return str
1896
+ .toLowerCase() // Convert to lowercase
1897
+ .replace(/\s+/g, '_') // Replace spaces with underscores
1898
+ .replace(/[^ -~]/g, ''); // Remove non-ASCII characters
1899
+ }
1900
+ hasGlobalStorage(type = StorageType.GLOBAL, id) {
1901
+ const strData = [];
1902
+ if (type === StorageType.GLOBAL) {
1903
+ const str = localStorage.getItem(this.storageName);
1904
+ strData.push(...(str ? JSON.parse(str) : []));
1905
+ }
1906
+ else {
1907
+ const str = sessionStorage.getItem(this.storageName);
1908
+ strData.push(...(str ? JSON.parse(str) : []));
1909
+ }
1910
+ const found = strData.find(store => store.id === id);
1911
+ return (found) ? true : false;
1912
+ }
1913
+ ngOnDestroy() {
1914
+ this.persistence$.unsubscribe();
1915
+ }
1916
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LocalStorageManagerService, deps: [{ token: CONFIG_SETTINGS_TOKEN, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
1917
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LocalStorageManagerService, providedIn: 'root' }); }
1918
+ }
1919
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LocalStorageManagerService, decorators: [{
1920
+ type: Injectable,
1921
+ args: [{ providedIn: 'root' }]
1922
+ }], ctorParameters: () => [{ type: ConfigOptions, decorators: [{
1923
+ type: Optional
1924
+ }, {
1925
+ type: Inject,
1926
+ args: [CONFIG_SETTINGS_TOKEN]
1927
+ }] }] });
1928
+
1929
+ const initialState = {
1930
+ localStores: [],
1931
+ sessionStores: [],
1932
+ settings: [],
1933
+ };
1934
+ class LocalStorageSignalsManagerService {
1935
+ constructor(configOptions) {
1936
+ this.configOptions = configOptions;
1937
+ this.state = signal(initialState);
1938
+ this.storageName = 'storage';
1939
+ this.storageSettingsName = 'global-storage';
1940
+ this.defaultOptions = SettingOptions.adapt();
1941
+ this.stateRetrieved = false;
1942
+ this.encrypted = false;
1943
+ this.app = inject(AppService);
1944
+ this.utils = inject(UtilsService);
1945
+ this.objectMergerService = inject(ObjectMergerService);
1946
+ this.encryption = inject(SymmetricalEncryptionService);
1947
+ this.encryptionTest = inject(EncryptionTestService);
1948
+ // --- SELECTORS ---
1949
+ this.stores = computed(() => this.state().settings);
1950
+ this.storeExists = (store) => computed(() => !!this.state().settings.find(item => item.name === store));
1951
+ this.store = (store) => computed(() => {
1952
+ const data = this.state();
1953
+ const foundStore = data.settings.find(item => item.name === store);
1954
+ if (!foundStore)
1955
+ return null;
1956
+ const options = SettingOptions.adapt(foundStore.options);
1957
+ const found = options.storage === StorageType.GLOBAL
1958
+ ? data.localStores.find(item => item.id === foundStore.id)
1959
+ : data.sessionStores.find(item => item.id === foundStore.id);
1960
+ if (!found) {
1961
+ this.deleteStore({ name: store });
1962
+ return null;
1963
+ }
1964
+ if (!this.app?.appID) {
1965
+ console.warn('No App ID found - AppId not Provided');
1966
+ return null;
1967
+ }
1968
+ const storageData = options.encrypted
1969
+ ? this.encryption.decrypt(found.data, this.app.appID)
1970
+ : found.data;
1971
+ return this.isString(storageData) ? JSON.parse(storageData) : storageData;
1972
+ });
1973
+ this.settings = computed(() => this.state().settings);
1974
+ this.setting = (store) => computed(() => this.state().settings.find(item => item.name === store) ?? null);
1975
+ this.storageName = configOptions?.LocalStorageOptions?.storageName || this.storageName;
1976
+ this.storageSettingsName = configOptions?.LocalStorageOptions?.storageSettingsName || this.storageSettingsName;
1977
+ this.defaultOptions = configOptions?.LocalStorageOptions?.options || this.defaultOptions;
1978
+ this.retrieveState();
1979
+ // Auto-persist when state changes
1980
+ effect(() => {
1981
+ if (this.stateRetrieved) {
1982
+ this.persistState(this.state());
1983
+ }
1984
+ });
1985
+ // Expiration timer every 3s
1986
+ setInterval(() => {
1987
+ const state = this.state();
1988
+ const expired = this.expired(state);
1989
+ if (expired.length > 0) {
1990
+ const ids = expired.map(i => i.id);
1991
+ const updated = {
1992
+ ...state,
1993
+ localStores: state.localStores.filter(i => !ids.includes(i.id)),
1994
+ sessionStores: state.sessionStores.filter(i => !ids.includes(i.id)),
1995
+ settings: state.settings.filter(i => !ids.includes(i.id)),
1996
+ };
1997
+ this.state.set(updated);
1998
+ this.persistState(updated);
1999
+ }
2000
+ }, 3000);
2001
+ }
2002
+ // --- STATE MUTATORS ---
2003
+ setStore(store) {
2004
+ this.state.update(state => {
2005
+ const settings = StorageOption.adapt(store);
2006
+ settings.options = this.objectMergerService.mergeStorageOptions(settings.options);
2007
+ store.name = this.validStoreName(store.name);
2008
+ const str = this.encryptionTest.isEncrypted(store.data) ? store.data : JSON.stringify(store.data);
2009
+ const dataStr = this.isObjectOrArray(str) && store.options.encrypted
2010
+ ? this.encryption.encrypt(str, this.app.appID)
2011
+ : store.data;
2012
+ const localData = dataStr && settings.options?.storage === StorageType.GLOBAL
2013
+ ? [{ data: dataStr, id: settings.id }]
2014
+ : [];
2015
+ const sessionData = dataStr && settings.options?.storage === StorageType.SESSION
2016
+ ? [{ data: dataStr, id: settings.id }]
2017
+ : [];
2018
+ return {
2019
+ ...state,
2020
+ localStores: [...state.localStores, ...localData],
2021
+ sessionStores: [...state.sessionStores, ...sessionData],
2022
+ settings: [...state.settings, settings],
2023
+ };
2024
+ });
2025
+ }
2026
+ createStore(store) {
2027
+ this.state.update(state => {
2028
+ const settings = StorageOption.adapt(store);
2029
+ settings.options = this.objectMergerService.mergeStorageOptions(settings.options);
2030
+ if (state.settings.some(item => item.name === store.name)) {
2031
+ return state;
2032
+ }
2033
+ if (!this.isObjectOrArray(store.data)) {
2034
+ console.warn('Data must be an Object or Array');
2035
+ return state;
2036
+ }
2037
+ const dataStr = store.options.encrypted ? this.encryption.encrypt(store.data, this.app.appID) : store.data;
2038
+ const localData = settings.options?.storage === StorageType.GLOBAL ? [{ data: dataStr, id: settings.id }] : [];
2039
+ const sessionData = settings.options?.storage === StorageType.SESSION ? [{ data: dataStr, id: settings.id }] : [];
2040
+ return {
2041
+ ...state,
2042
+ localStores: [...state.localStores, ...localData],
2043
+ sessionStores: [...state.sessionStores, ...sessionData],
2044
+ settings: [...state.settings, settings],
2045
+ };
2046
+ });
2047
+ }
2048
+ updateStore(store) {
2049
+ this.state.update(state => {
2050
+ const settings = state.settings.find(item => item.name === store.name);
2051
+ if (!settings)
2052
+ return state;
2053
+ const dataStr = settings.options?.encrypted
2054
+ ? this.encryption.encrypt(store.data, this.app.appID)
2055
+ : store.data;
2056
+ const localData = settings.options?.storage === StorageType.GLOBAL ? [{ data: dataStr, id: settings.id }] : [];
2057
+ const sessionData = settings.options?.storage === StorageType.SESSION ? [{ data: dataStr, id: settings.id }] : [];
2058
+ return {
2059
+ ...state,
2060
+ localStores: [...state.localStores.filter(i => i.id !== settings.id), ...localData],
2061
+ sessionStores: [...state.sessionStores.filter(i => i.id !== settings.id), ...sessionData],
2062
+ };
2063
+ });
2064
+ }
2065
+ deleteStore(store) {
2066
+ this.state.update(state => {
2067
+ const id = state.settings.find(item => item.name === store.name)?.id;
2068
+ if (!id)
2069
+ return state;
2070
+ return {
2071
+ ...state,
2072
+ localStores: state.localStores.filter(i => i.id !== id),
2073
+ sessionStores: state.sessionStores.filter(i => i.id !== id),
2074
+ settings: state.settings.filter(i => i.id !== id),
2075
+ };
2076
+ });
2077
+ }
2078
+ resetStore() {
2079
+ const cleared = { localStores: [], sessionStores: [], settings: [] };
2080
+ this.state.set(cleared);
2081
+ this.persistState(cleared);
2082
+ }
2083
+ // --- HELPERS ---
2084
+ persistState(state) {
2085
+ localStorage.setItem(this.storageName, JSON.stringify(state.localStores));
2086
+ sessionStorage.setItem(this.storageName, JSON.stringify(state.sessionStores));
2087
+ const settingsStr = this.encryption.encrypt(state.settings, this.app.appID);
2088
+ localStorage.setItem(this.storageSettingsName, settingsStr || '');
2089
+ }
2090
+ expired(state) {
2091
+ return state.settings.filter(s => (s.options?.expires || 0) > 0 && this.utils.hasExpired(s.options?.expires));
1463
2092
  }
1464
2093
  retrieveState() {
1465
2094
  const str = localStorage.getItem(this.storageSettingsName);
1466
2095
  const localStr = localStorage.getItem(this.storageName);
1467
2096
  const sessionStr = sessionStorage.getItem(this.storageName);
1468
- const localData = (localStr) ? JSON.parse(localStr) : null;
1469
- const sessionData = (sessionStr) ? JSON.parse(sessionStr) : null;
2097
+ const localData = localStr ? JSON.parse(localStr) : [];
2098
+ const sessionData = sessionStr ? JSON.parse(sessionStr) : [];
1470
2099
  const decryptedStr = str ? this.encryption.decrypt(str, this.app.appID) : null;
1471
- const settings = (decryptedStr && decryptedStr !== null) ? JSON.parse(decryptedStr) : [];
2100
+ const settings = decryptedStr ? JSON.parse(decryptedStr) : [];
1472
2101
  settings.forEach(store => {
1473
- const expired = (store.options?.expires || 0) > 0 && this.utils.hasExpired(store.options?.expires || 0);
2102
+ // normalize options so we compare numbers and compute expires correctly
2103
+ const options = SettingOptions.adapt(store.options);
2104
+ // if persisted data included expiresIn but not numeric expires, compute it now
2105
+ if ((!options.expires || options.expires === 0) && options.expiresIn) {
2106
+ try {
2107
+ const computed = this.utils.expires(options.expiresIn);
2108
+ options.expires = computed || 0;
2109
+ }
2110
+ catch {
2111
+ options.expires = 0;
2112
+ }
2113
+ }
2114
+ const expired = (options.expires || 0) > 0 && this.utils.hasExpired(options.expires || 0);
1474
2115
  if (!expired) {
1475
- const hasStorage = this.hasGlobalStorage(store.options?.storage, store.id || '');
2116
+ const hasStorage = this.hasGlobalStorage(options.storage, store.id || '');
1476
2117
  if (!hasStorage) {
1477
- this.createStore({ id: store.id, name: store.name, data: [], options: SettingOptions.adapt(store.options) });
2118
+ this.createStore({ id: store.id, name: store.name, data: [], options });
1478
2119
  }
1479
2120
  else {
1480
- const storeData = (store.options?.storage === StorageType.GLOBAL) ? localData.find(item => item.id === store.id) : sessionData.find(item => item.id === store.id);
1481
- this.setStore({ id: store.id || '', name: store.name, data: storeData?.data, options: SettingOptions.adapt(store.options) });
2121
+ const storeData = (options.storage === StorageType.GLOBAL)
2122
+ ? localData.find(item => item.id === store.id)
2123
+ : sessionData.find(item => item.id === store.id);
2124
+ this.setStore({ id: store.id || '', name: store.name, data: storeData?.data, options });
1482
2125
  }
1483
2126
  }
1484
2127
  });
1485
2128
  this.stateRetrieved = true;
1486
- }
1487
- isString(obj) {
1488
- return (Object.prototype.toString.call(obj) === '[object String]');
1489
- }
1490
- fixAndParseJSON(jsonString) {
1491
- const fixedString = jsonString
1492
- .replace(/'/g, '"')
1493
- .replace(/([{,]\s*)(\w+)\s*:/g, '$1"$2":')
1494
- .replace(/,\s*(\}|\])/g, '$1');
2129
+ // Ensure all stored settings have numeric expires computed and persisted
1495
2130
  try {
1496
- return JSON.parse(fixedString);
2131
+ const current = this.state();
2132
+ let changed = false;
2133
+ const normalizedSettings = current.settings.map(s => {
2134
+ const opts = SettingOptions.adapt(s.options);
2135
+ if ((!opts.expires || opts.expires === 0) && opts.expiresIn) {
2136
+ const computed = this.utils.expires(opts.expiresIn) || 0;
2137
+ if (computed && computed !== opts.expires) {
2138
+ opts.expires = computed;
2139
+ changed = true;
2140
+ }
2141
+ }
2142
+ return { ...s, options: opts };
2143
+ });
2144
+ if (changed) {
2145
+ const updated = { ...current, settings: normalizedSettings };
2146
+ this.state.set(updated);
2147
+ this.persistState(updated);
2148
+ }
1497
2149
  }
1498
- catch (error) {
1499
- throw new Error('Failed to parse JSON: ' + error.message);
2150
+ catch (e) {
2151
+ // swallow normalization errors to avoid breaking init
1500
2152
  }
1501
2153
  }
1502
- validStoreName(str) {
1503
- return str
1504
- .toLowerCase() // Convert to lowercase
1505
- .replace(/\s+/g, '_') // Replace spaces with underscores
1506
- .replace(/[^ -~]/g, ''); // Remove non-ASCII characters
1507
- }
1508
2154
  hasGlobalStorage(type = StorageType.GLOBAL, id) {
1509
2155
  const strData = [];
1510
2156
  if (type === StorageType.GLOBAL) {
@@ -1516,15 +2162,27 @@ class LocalStorageManagerService extends ComponentStore {
1516
2162
  strData.push(...(str ? JSON.parse(str) : []));
1517
2163
  }
1518
2164
  const found = strData.find(store => store.id === id);
1519
- return (found) ? true : false;
2165
+ return !!found;
1520
2166
  }
1521
- ngOnDestroy() {
1522
- this.persistence$.unsubscribe();
2167
+ isObjectOrArray(obj) {
2168
+ try {
2169
+ const parsed = typeof obj === 'object' ? obj : JSON.parse(obj);
2170
+ return typeof parsed === 'object' && parsed !== null;
2171
+ }
2172
+ catch {
2173
+ return false;
2174
+ }
1523
2175
  }
1524
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LocalStorageManagerService, deps: [{ token: CONFIG_SETTINGS_TOKEN, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
1525
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LocalStorageManagerService, providedIn: 'root' }); }
2176
+ isString(obj) {
2177
+ return Object.prototype.toString.call(obj) === '[object String]';
2178
+ }
2179
+ validStoreName(str) {
2180
+ return str.toLowerCase().replace(/\s+/g, '_').replace(/[^ -~]/g, '');
2181
+ }
2182
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LocalStorageSignalsManagerService, deps: [{ token: CONFIG_SETTINGS_TOKEN, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
2183
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LocalStorageSignalsManagerService, providedIn: 'root' }); }
1526
2184
  }
1527
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LocalStorageManagerService, decorators: [{
2185
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LocalStorageSignalsManagerService, decorators: [{
1528
2186
  type: Injectable,
1529
2187
  args: [{ providedIn: 'root' }]
1530
2188
  }], ctorParameters: () => [{ type: ConfigOptions, decorators: [{
@@ -1858,247 +2516,627 @@ class RequestErrorInterceptor {
1858
2516
  constructor() {
1859
2517
  this.toastMessage = inject(ToastMessageDisplayService);
1860
2518
  }
1861
- intercept(req, next) {
1862
- return next.handle(req).pipe(catchError$1((error) => {
1863
- const displayError = ToastDisplay.adapt({
1864
- message: 'This is a toast message. This is an Error!!',
1865
- action: 'OK',
1866
- color: ToastColors.SUCCESS,
1867
- icon: 'info',
1868
- duration: 5 * 1000, //5 seconds
1869
- });
1870
- if (error.status >= 400 && error.status < 500) {
1871
- displayError.color = ToastColors.WARN;
1872
- displayError.message = error.error || `${error.status}: ${error.statusText}`;
1873
- console.error('Client Error:', {
1874
- status: error.status,
1875
- message: error.message,
1876
- error: error.error,
1877
- text: error.statusText,
1878
- });
1879
- this.toastMessage.toastMessage(displayError);
1880
- }
1881
- else if (error.status >= 500) {
1882
- displayError.color = ToastColors.ERROR;
1883
- displayError.message = error.error || `${error.status}: ${error.statusText}`;
1884
- console.error('Server Error:', {
1885
- status: error.status,
1886
- message: error.message,
1887
- error: error.error,
1888
- text: error.statusText,
1889
- });
1890
- this.toastMessage.toastMessage(displayError);
1891
- }
1892
- return throwError(() => error);
1893
- }));
2519
+ intercept(req, next) {
2520
+ return next.handle(req).pipe(catchError$1((error) => {
2521
+ const displayError = ToastDisplay.adapt({
2522
+ message: 'This is a toast message. This is an Error!!',
2523
+ action: 'OK',
2524
+ color: ToastColors.SUCCESS,
2525
+ icon: 'info',
2526
+ duration: 5 * 1000, //5 seconds
2527
+ });
2528
+ if (error.status >= 400 && error.status < 500) {
2529
+ displayError.color = ToastColors.WARN;
2530
+ displayError.message = error.error || `${error.status}: ${error.statusText}`;
2531
+ console.error('Client Error:', {
2532
+ status: error.status,
2533
+ message: error.message,
2534
+ error: error.error,
2535
+ text: error.statusText,
2536
+ });
2537
+ this.toastMessage.toastMessage(displayError);
2538
+ }
2539
+ else if (error.status >= 500) {
2540
+ displayError.color = ToastColors.ERROR;
2541
+ displayError.message = error.error || `${error.status}: ${error.statusText}`;
2542
+ console.error('Server Error:', {
2543
+ status: error.status,
2544
+ message: error.message,
2545
+ error: error.error,
2546
+ text: error.statusText,
2547
+ });
2548
+ this.toastMessage.toastMessage(displayError);
2549
+ }
2550
+ return throwError(() => error);
2551
+ }));
2552
+ }
2553
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RequestErrorInterceptor, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2554
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RequestErrorInterceptor }); }
2555
+ }
2556
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RequestErrorInterceptor, decorators: [{
2557
+ type: Injectable
2558
+ }] });
2559
+
2560
+ let ClientInfo$1 = class ClientInfo {
2561
+ constructor(domain = '', service = '', id = 0, name = '') {
2562
+ this.domain = domain;
2563
+ this.service = service;
2564
+ this.id = id;
2565
+ this.name = name;
2566
+ }
2567
+ static adapt(item) {
2568
+ return new ClientInfo(item?.domain, item?.service, item?.id, (item?.first_name || item?.last_name) ? `${item?.first_name} ${item?.last_name}` : '');
2569
+ }
2570
+ };
2571
+
2572
+ let ClientInfoMapper$1 = class ClientInfoMapper {
2573
+ constructor(id = 0, first_name = '', last_name = '', email = '') {
2574
+ this.id = id;
2575
+ this.first_name = first_name;
2576
+ this.last_name = last_name;
2577
+ this.email = email;
2578
+ }
2579
+ static adapt(item) {
2580
+ const first_name = (item?.name) ? item.name.split(' ')[0] : '';
2581
+ const last_name = (item?.name) ? item.name.split(' ')[1] : '';
2582
+ return new ClientInfoMapper(item?.id, first_name, last_name, item?.email);
2583
+ }
2584
+ };
2585
+
2586
+ let AIPrompt$1 = class AIPrompt {
2587
+ constructor(response = '') {
2588
+ this.response = response;
2589
+ }
2590
+ static adapt(item) {
2591
+ return new AIPrompt(item?.response);
2592
+ }
2593
+ };
2594
+
2595
+ class DownloadLabels {
2596
+ constructor(error = '', action = '', icon = 'error') {
2597
+ this.error = error;
2598
+ this.action = action;
2599
+ this.icon = icon;
2600
+ }
2601
+ static adapt(item) {
2602
+ return new DownloadLabels(item?.error, item?.action, item?.icon);
2603
+ }
2604
+ }
2605
+
2606
+ class DownloadFileComponent {
2607
+ constructor() {
2608
+ this.subscriptions = new Subscription();
2609
+ this.displayError = 3; // seconds
2610
+ this.diameter = 32;
2611
+ this.mode = 'determinate';
2612
+ this.isPending = false;
2613
+ this.active = false;
2614
+ this.disabled = false;
2615
+ this.error = new EventEmitter();
2616
+ this._progress = 0;
2617
+ this._hasError = false;
2618
+ this.errorTimerActive = false;
2619
+ }
2620
+ set progress(value) {
2621
+ this._progress = value ?? 0;
2622
+ }
2623
+ get progress() {
2624
+ return this._progress;
2625
+ }
2626
+ set hasError(value) {
2627
+ this._hasError = !!value;
2628
+ if (this._hasError && !this.errorTimerActive) {
2629
+ this.errorTimerActive = true;
2630
+ this.active = false;
2631
+ this.error.emit();
2632
+ this.subscriptions.add(timer(this.displayError * 1000)
2633
+ .subscribe((err) => {
2634
+ this._hasError = false;
2635
+ this.errorTimerActive = false;
2636
+ }));
2637
+ }
2638
+ }
2639
+ get hasError() {
2640
+ return this._hasError;
2641
+ }
2642
+ ngOnInit() { }
2643
+ onAction() {
2644
+ this.isPending = false;
2645
+ if (this.event)
2646
+ this.event();
2647
+ }
2648
+ ngOnDestroy() {
2649
+ this.subscriptions.unsubscribe();
2650
+ }
2651
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DownloadFileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2652
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: DownloadFileComponent, selector: "app-download-file", inputs: { event: "event", displayError: "displayError", diameter: "diameter", mode: "mode", isPending: "isPending", active: "active", disabled: "disabled", progress: "progress", hasError: "hasError" }, outputs: { error: "error" }, ngImport: i0, template: "<ng-container *ngIf=\"!isPending; else DOWNLOADING\">\n <ng-container *ngIf=\"hasError; else NORMAL\">\n <div class=\"width center-txt\" style=\"margin-bottom: 4px;\">\n <mat-icon color=\"warn\" class=\"warn-icon\">warning</mat-icon>\n </div>\n </ng-container>\n <ng-template #NORMAL>\n <ng-container *ngIf=\"active; else ACTION\">\n\n <div class=\"container-obj\">\n <div class=\"centered-obj-div\">\n <mat-progress-spinner\n color=\"primary\"\n mode=\"indeterminate\"\n [diameter]=\"44\"\n ></mat-progress-spinner>\n </div>\n </div>\n\n </ng-container>\n <ng-template #ACTION>\n <button data-tracking=\"export-btn\" mat-icon-button (click)=\"onAction()\" class=\"icon-button\" [disabled]=\"disabled\">\n <mat-icon class=\"custom-icon\">file_download</mat-icon>\n </button>\n </ng-template>\n </ng-template>\n</ng-container>\n\n<ng-template #DOWNLOADING>\n <div\n class=\"spinner-container\"\n *ngIf=\"(progress > 0 && progress < 100); else INDETERMINATE\"\n >\n <div class=\"spinner-background\">\n {{progress}}%\n </div>\n <mat-progress-spinner\n color=\"primary\"\n [mode]=\"mode\"\n [value]=\"progress\"\n [diameter]=\"44\"\n ></mat-progress-spinner>\n </div>\n <ng-template #INDETERMINATE>\n <div class=\"container-obj\">\n <div class=\"centered-obj-div\">\n <mat-progress-spinner\n color=\"primary\"\n mode=\"indeterminate\"\n [diameter]=\"44\"\n ></mat-progress-spinner>\n </div>\n </div>\n </ng-template>\n\n</ng-template>\n\n\n\n", styles: [":not(spinner-container).spinner-container{position:relative}:not(spinner-container).spinner-container .spinner-background{position:absolute;width:44px;height:44px;font-size:12px;line-height:32px;text-align:center;overflow:hidden;border-radius:50%;border:solid 5px whitesmoke}.center-txt{align-content:center;text-align:-webkit-center}.width{width:48px;height:48px}.icon-button{display:flex;align-items:center;justify-content:center;width:48px;height:48px;padding:0}.container-obj{display:flex;justify-content:center;align-items:center;width:48px;height:48px}.centered-obj-div{text-align:center}\n"], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i3.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }], encapsulation: i0.ViewEncapsulation.None }); }
2653
+ }
2654
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DownloadFileComponent, decorators: [{
2655
+ type: Component,
2656
+ args: [{ selector: 'app-download-file', encapsulation: ViewEncapsulation.None, template: "<ng-container *ngIf=\"!isPending; else DOWNLOADING\">\n <ng-container *ngIf=\"hasError; else NORMAL\">\n <div class=\"width center-txt\" style=\"margin-bottom: 4px;\">\n <mat-icon color=\"warn\" class=\"warn-icon\">warning</mat-icon>\n </div>\n </ng-container>\n <ng-template #NORMAL>\n <ng-container *ngIf=\"active; else ACTION\">\n\n <div class=\"container-obj\">\n <div class=\"centered-obj-div\">\n <mat-progress-spinner\n color=\"primary\"\n mode=\"indeterminate\"\n [diameter]=\"44\"\n ></mat-progress-spinner>\n </div>\n </div>\n\n </ng-container>\n <ng-template #ACTION>\n <button data-tracking=\"export-btn\" mat-icon-button (click)=\"onAction()\" class=\"icon-button\" [disabled]=\"disabled\">\n <mat-icon class=\"custom-icon\">file_download</mat-icon>\n </button>\n </ng-template>\n </ng-template>\n</ng-container>\n\n<ng-template #DOWNLOADING>\n <div\n class=\"spinner-container\"\n *ngIf=\"(progress > 0 && progress < 100); else INDETERMINATE\"\n >\n <div class=\"spinner-background\">\n {{progress}}%\n </div>\n <mat-progress-spinner\n color=\"primary\"\n [mode]=\"mode\"\n [value]=\"progress\"\n [diameter]=\"44\"\n ></mat-progress-spinner>\n </div>\n <ng-template #INDETERMINATE>\n <div class=\"container-obj\">\n <div class=\"centered-obj-div\">\n <mat-progress-spinner\n color=\"primary\"\n mode=\"indeterminate\"\n [diameter]=\"44\"\n ></mat-progress-spinner>\n </div>\n </div>\n </ng-template>\n\n</ng-template>\n\n\n\n", styles: [":not(spinner-container).spinner-container{position:relative}:not(spinner-container).spinner-container .spinner-background{position:absolute;width:44px;height:44px;font-size:12px;line-height:32px;text-align:center;overflow:hidden;border-radius:50%;border:solid 5px whitesmoke}.center-txt{align-content:center;text-align:-webkit-center}.width{width:48px;height:48px}.icon-button{display:flex;align-items:center;justify-content:center;width:48px;height:48px;padding:0}.container-obj{display:flex;justify-content:center;align-items:center;width:48px;height:48px}.centered-obj-div{text-align:center}\n"] }]
2657
+ }], propDecorators: { event: [{
2658
+ type: Input
2659
+ }], displayError: [{
2660
+ type: Input
2661
+ }], diameter: [{
2662
+ type: Input
2663
+ }], mode: [{
2664
+ type: Input
2665
+ }], isPending: [{
2666
+ type: Input
2667
+ }], active: [{
2668
+ type: Input
2669
+ }], disabled: [{
2670
+ type: Input
2671
+ }], error: [{
2672
+ type: Output
2673
+ }], progress: [{
2674
+ type: Input
2675
+ }], hasError: [{
2676
+ type: Input
2677
+ }] } });
2678
+
2679
+ class FileDownloaderComponent extends HTTPManagerService {
2680
+ set labels(value) {
2681
+ this._labels = (value) ? DownloadLabels.adapt(value) : DownloadLabels.adapt();
2682
+ }
2683
+ get labels() {
2684
+ return this._labels;
2685
+ }
2686
+ constructor() {
2687
+ super();
2688
+ this.delayError = 3;
2689
+ this.apiRequest = ApiRequest.adapt();
2690
+ this.displayErrorMessage = false;
2691
+ this._labels = DownloadLabels.adapt();
2692
+ this.active = false;
2693
+ this.subscription = new Subscription();
2694
+ this.completed = new EventEmitter();
2695
+ this.failed = new EventEmitter();
2696
+ this.disabled = false;
2697
+ }
2698
+ ngOnInit() {
2699
+ }
2700
+ onDownloadStreaming() {
2701
+ if (this.active)
2702
+ return;
2703
+ this.active = true;
2704
+ return this.downloadRequest(this.apiRequest, [])
2705
+ .pipe(distinctUntilChanged(), catchError((err) => {
2706
+ this.onError(err.message);
2707
+ this.active = false;
2708
+ this.failed.emit(err);
2709
+ return throwError(() => err);
2710
+ }), finalize(() => {
2711
+ if (!this.active)
2712
+ return;
2713
+ this.active = false;
2714
+ this.completed.emit();
2715
+ }))
2716
+ .subscribe();
2717
+ }
2718
+ onError(message) {
2719
+ if (!message || !this.displayErrorMessage)
2720
+ return;
2721
+ const display = ToastDisplay.adapt({
2722
+ message,
2723
+ action: 'Ok',
2724
+ color: ToastColors.ERROR,
2725
+ icon: 'error',
2726
+ });
2727
+ this.active = false;
2728
+ this.toastMessage.toastMessage(display);
2729
+ }
2730
+ OnDestroy() {
2731
+ this.subscription.unsubscribe();
1894
2732
  }
1895
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RequestErrorInterceptor, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1896
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RequestErrorInterceptor }); }
2733
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FileDownloaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2734
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: FileDownloaderComponent, selector: "app-file-downloader", inputs: { delayError: "delayError", apiRequest: "apiRequest", displayErrorMessage: "displayErrorMessage", saveFileAs: "saveFileAs", labels: "labels", disabled: "disabled" }, outputs: { completed: "completed", failed: "failed" }, usesInheritance: true, ngImport: i0, template: "<app-download-file\n [disabled]=\"disabled\"\n [displayError]=\"3\"\n [event]=\"onDownloadStreaming.bind(this)\"\n [isPending]=\"(isPending$ | async) || false\"\n [progress]=\"(progress$ | async)\"\n [hasError]=\"(error$ | async)\"\n (error)=\"onError(labels.error)\"\n [active]=\"active\"\n></app-download-file>\n", styles: [".snackBarInfo{background-color:#f44336;color:#fff}.mat-simple-snackbar>span{font-weight:700}.mat-simple-snackbar-action .mat-button .mat-button-wrapper{color:#fff}.cdk-overlay-pane>.mat-snack-bar-container{width:100%}.mat-snack-bar-container{max-width:100%!important;width:100%}\n"], dependencies: [{ kind: "component", type: DownloadFileComponent, selector: "app-download-file", inputs: ["event", "displayError", "diameter", "mode", "isPending", "active", "disabled", "progress", "hasError"], outputs: ["error"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], encapsulation: i0.ViewEncapsulation.None }); }
1897
2735
  }
1898
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RequestErrorInterceptor, decorators: [{
1899
- type: Injectable
1900
- }] });
2736
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FileDownloaderComponent, decorators: [{
2737
+ type: Component,
2738
+ args: [{ selector: 'app-file-downloader', encapsulation: ViewEncapsulation.None, template: "<app-download-file\n [disabled]=\"disabled\"\n [displayError]=\"3\"\n [event]=\"onDownloadStreaming.bind(this)\"\n [isPending]=\"(isPending$ | async) || false\"\n [progress]=\"(progress$ | async)\"\n [hasError]=\"(error$ | async)\"\n (error)=\"onError(labels.error)\"\n [active]=\"active\"\n></app-download-file>\n", styles: [".snackBarInfo{background-color:#f44336;color:#fff}.mat-simple-snackbar>span{font-weight:700}.mat-simple-snackbar-action .mat-button .mat-button-wrapper{color:#fff}.cdk-overlay-pane>.mat-snack-bar-container{width:100%}.mat-snack-bar-container{max-width:100%!important;width:100%}\n"] }]
2739
+ }], ctorParameters: () => [], propDecorators: { delayError: [{
2740
+ type: Input
2741
+ }], apiRequest: [{
2742
+ type: Input
2743
+ }], displayErrorMessage: [{
2744
+ type: Input
2745
+ }], saveFileAs: [{
2746
+ type: Input
2747
+ }], labels: [{
2748
+ type: Input
2749
+ }], completed: [{
2750
+ type: Output
2751
+ }], failed: [{
2752
+ type: Output
2753
+ }], disabled: [{
2754
+ type: Input
2755
+ }] } });
1901
2756
 
1902
- class ClientInfo {
1903
- constructor(domain = '', service = '', id = 0, name = '') {
1904
- this.domain = domain;
1905
- this.service = service;
1906
- this.id = id;
1907
- this.name = name;
2757
+ class RequestManagerDemoComponent {
2758
+ get retry() {
2759
+ return this.requestForm.get('retry')?.value;
1908
2760
  }
1909
- static adapt(item) {
1910
- return new ClientInfo(item?.domain, item?.service, item?.id, (item?.first_name || item?.last_name) ? `${item?.first_name} ${item?.last_name}` : '');
2761
+ get headers() {
2762
+ return this.requestForm.get('headers');
1911
2763
  }
1912
- }
1913
-
1914
- class ClientInfoMapper {
1915
- constructor(id = 0, first_name = '', last_name = '', email = '') {
1916
- this.id = id;
1917
- this.first_name = first_name;
1918
- this.last_name = last_name;
1919
- this.email = email;
2764
+ get isValid() {
2765
+ this.requestForm.markAllAsTouched();
2766
+ return this.requestForm.valid;
1920
2767
  }
1921
- static adapt(item) {
1922
- const first_name = (item?.name) ? item.name.split(' ')[0] : '';
1923
- const last_name = (item?.name) ? item.name.split(' ')[1] : '';
1924
- return new ClientInfoMapper(item?.id, first_name, last_name, item?.email);
2768
+ constructor() {
2769
+ this.displayedColumns = ['id', 'name', 'lastName', 'age'];
2770
+ this.fb = inject(FormBuilder);
2771
+ this.toastMessage = inject(ToastMessageDisplayService);
2772
+ this.httpManagerService = inject(HTTPManagerService);
2773
+ this.isPending$ = this.httpManagerService.isPending$;
2774
+ this.countdown$ = this.httpManagerService.countdown$;
2775
+ this.GET_error$ = new BehaviorSubject('');
2776
+ this.POST_error$ = new BehaviorSubject('');
2777
+ this.PUT_error$ = new BehaviorSubject('');
2778
+ this.DELETE_error$ = new BehaviorSubject('');
2779
+ this.STREAM_error$ = new BehaviorSubject('');
2780
+ this.STREAM_AI_error$ = new BehaviorSubject('');
2781
+ this.requestParams = {
2782
+ GET: ApiRequest.adapt(),
2783
+ POST: ApiRequest.adapt(),
2784
+ PUT: ApiRequest.adapt(),
2785
+ DELETE: ApiRequest.adapt(),
2786
+ STREAM: ApiRequest.adapt(),
2787
+ };
2788
+ this.questionControl = this.fb.control("", [Validators.required]);
2789
+ this.downloadRequest = ApiRequest.adapt({
2790
+ server: 'assets/images',
2791
+ path: ['me.jpg'],
2792
+ // saveAs: 'john.jpg' // Optional
2793
+ });
2794
+ // downloadRequest = ApiRequest.adapt({
2795
+ // server: 'oidc/ai/file'
2796
+ // })
2797
+ this.sampleClientData = {
2798
+ id: 0,
2799
+ name: "Old School Dates",
2800
+ domain: "osd.com",
2801
+ service: "osd",
2802
+ spiffe: "osd.com/osd",
2803
+ secret: "SMOPECXP-OS4P-USOG-X2II-3XMD1FQDR3IJX",
2804
+ created: 1693003138,
2805
+ modified: 1693003138,
2806
+ icon: "",
2807
+ imageFile: "",
2808
+ email: "wavecoders@gmail.com"
2809
+ };
2810
+ this.requestForm = this.fb.group({
2811
+ path: this.fb.control("ai/"),
2812
+ headers: this.fb.array([]),
2813
+ adapter: [null],
2814
+ mapper: [null],
2815
+ retry: this.fb.group({
2816
+ times: [3],
2817
+ delay: [3],
2818
+ }),
2819
+ polling: [3],
2820
+ });
2821
+ this.AIType = 0;
2822
+ this.sampleAdaptors = [
2823
+ { label: "ClientInfo Basic", value: ClientInfo$1.adapt },
2824
+ { label: "AI Prompt", value: AIPrompt$1.adapt },
2825
+ ];
2826
+ this.sampleMappers = [
2827
+ { label: "Mapper Basic", value: ClientInfoMapper$1.adapt },
2828
+ { label: "AI Prompt", value: AIPrompt$1.adapt },
2829
+ ];
2830
+ this.hasId = (arr) => {
2831
+ if (arr.length === 0)
2832
+ return false;
2833
+ return !isNaN(arr[arr.length - 1]);
2834
+ };
2835
+ this.props = (adapter) => {
2836
+ return (adapter) ? adapter() : null;
2837
+ };
2838
+ // server = `http://sample-endpoint/as/authorization.oauth2`
2839
+ this.arrayObjectsToObjects = (arr) => {
2840
+ return Array.isArray(arr) ? arr.reduce((obj, item) => Object.assign(obj, { [item.key]: item.value }), {}) : {};
2841
+ };
1925
2842
  }
1926
- }
1927
-
1928
- class AIPrompt {
1929
- constructor(response = '') {
1930
- this.response = response;
2843
+ ngOnInit() {
2844
+ // const reqGet2 = ApiRequest.adapt({
2845
+ // server,
2846
+ // path: ['clients'],
2847
+ // headers: { authentication: "Bearer <KEY>" },
2848
+ // adapter: ClientInfo,
2849
+ // dataType: DataType.OBJECT,
2850
+ // // concurrent: false,
2851
+ // // polling: 3, //seconds
2852
+ // })
2853
+ // const req2 = [1024,1025,1026].map(item => {
2854
+ // return this.httpManagerService.getRequest<ClientInfo[]>(reqGet2, [item])
2855
+ // .pipe(
2856
+ // catchError(error => {
2857
+ // return throwError(() => new Error(error.error.message))
2858
+ // })
2859
+ // )
2860
+ // })
2861
+ // forkJoin(req2)
2862
+ // .subscribe(res => console.log(res))
1931
2863
  }
1932
- static adapt(item) {
1933
- return new AIPrompt(item?.response);
2864
+ addHeader() {
2865
+ const header = this.fb.group({
2866
+ key: ['', Validators.required],
2867
+ value: ['']
2868
+ });
2869
+ this.headers.push(header);
1934
2870
  }
1935
- }
1936
-
1937
- class DownloadLabels {
1938
- constructor(error = '', action = '', icon = 'error') {
1939
- this.error = error;
1940
- this.action = action;
1941
- this.icon = icon;
2871
+ removeHeader(index) {
2872
+ this.headers.removeAt(index);
1942
2873
  }
1943
- static adapt(item) {
1944
- return new DownloadLabels(item?.error, item?.action, item?.icon);
2874
+ compileRequest() {
2875
+ const requestParams = this.requestForm.value;
2876
+ requestParams.headers = this.arrayObjectsToObjects(requestParams.headers || []);
2877
+ const pathReq = (requestParams.path === "") ? [] : (requestParams.path || "").split("/");
2878
+ if (!this.pollingState.checked)
2879
+ requestParams.polling = 0;
2880
+ if (!this.failedState.checked) {
2881
+ requestParams.retry = { times: 0, delay: 0 };
2882
+ }
2883
+ const apiOptions = ApiRequest.adapt(requestParams);
2884
+ apiOptions.path = [];
2885
+ return { apiOptions: apiOptions, path: pathReq };
1945
2886
  }
1946
- }
1947
-
1948
- class DownloadFileComponent {
1949
- constructor() {
1950
- this.subscriptions = new Subscription();
1951
- this.displayError = 3; // seconds
1952
- this.diameter = 32;
1953
- this.mode = 'determinate';
1954
- this.isPending = false;
1955
- this.active = false;
1956
- this.disabled = false;
1957
- this.error = new EventEmitter();
1958
- this._progress = 0;
1959
- this._hasError = false;
1960
- this.errorTimerActive = false;
2887
+ onGetRequest() {
2888
+ if (!this.isValid)
2889
+ return;
2890
+ const reqParams = this.compileRequest();
2891
+ this.requestParams.GET = reqParams.apiOptions;
2892
+ this.GET$ = EMPTY; //Cancels Previous
2893
+ this.GET_error$.next('');
2894
+ this.GET$ = this.httpManagerService.getRequest(reqParams.apiOptions, reqParams.path)
2895
+ .pipe(
2896
+ // tap((data) => console.log("API GET response", data)),
2897
+ catchError(error => {
2898
+ return throwError(() => this.errorHandling(error, 'GET'));
2899
+ }));
2900
+ }
2901
+ onCreateRequest() {
2902
+ if (!this.isValid)
2903
+ return;
2904
+ const reqParams = this.compileRequest();
2905
+ this.requestParams.POST = reqParams.apiOptions;
2906
+ this.POST$ = EMPTY; //Cancels Previous
2907
+ this.POST_error$.next('');
2908
+ console.log("POST", this.sampleClientData);
2909
+ console.log("POST", reqParams.apiOptions);
2910
+ console.log("POST", reqParams.path);
2911
+ this.POST$ = this.httpManagerService.postRequest(this.sampleClientData, reqParams.apiOptions, reqParams.path)
2912
+ .pipe(
2913
+ // tap((data) => console.log("API POST response", data)),
2914
+ catchError(error => {
2915
+ return throwError(() => this.errorHandling(error, 'POST'));
2916
+ }));
1961
2917
  }
1962
- set progress(value) {
1963
- this._progress = value ?? 0;
2918
+ onUpdateRequest() {
2919
+ if (!this.isValid)
2920
+ return;
2921
+ const reqParams = this.compileRequest();
2922
+ if (!this.hasId(reqParams.path)) {
2923
+ console.log("Missing ID");
2924
+ return;
2925
+ }
2926
+ this.sampleClientData.id = parseInt(reqParams.path[reqParams.path.length - 1]);
2927
+ this.requestParams.PUT = reqParams.apiOptions;
2928
+ this.PUT$ = EMPTY; //Cancels Previous
2929
+ this.PUT_error$.next('');
2930
+ this.PUT$ = this.httpManagerService.putRequest(this.sampleClientData, reqParams.apiOptions, reqParams.path)
2931
+ .pipe(
2932
+ // tap((data) => console.log("API PUT response", data)),
2933
+ catchError(error => {
2934
+ return throwError(() => this.errorHandling(error, 'PUT'));
2935
+ }));
1964
2936
  }
1965
- get progress() {
1966
- return this._progress;
2937
+ onDeleteRequest() {
2938
+ if (!this.isValid)
2939
+ return;
2940
+ const reqParams = this.compileRequest();
2941
+ this.requestParams.DELETE = reqParams.apiOptions;
2942
+ if (!this.hasId(reqParams.path)) {
2943
+ console.log("Missing ID");
2944
+ return;
2945
+ }
2946
+ this.sampleClientData.id = parseInt(reqParams.path[reqParams.path.length - 1]);
2947
+ this.requestParams.DELETE = reqParams.apiOptions;
2948
+ this.DELETE$ = EMPTY; //Cancels Previous
2949
+ this.DELETE_error$.next('');
2950
+ this.DELETE$ = this.httpManagerService.deleteRequest(reqParams.apiOptions, reqParams.path)
2951
+ .pipe(
2952
+ // tap((data) => console.log("API DELETE response", data)),
2953
+ catchError(error => {
2954
+ return throwError(() => this.errorHandling(error, 'DELETE'));
2955
+ }));
1967
2956
  }
1968
- set hasError(value) {
1969
- this._hasError = !!value;
1970
- if (this._hasError && !this.errorTimerActive) {
1971
- this.errorTimerActive = true;
1972
- this.active = false;
1973
- this.error.emit();
1974
- this.subscriptions.add(timer(this.displayError * 1000)
1975
- .subscribe((err) => {
1976
- this._hasError = false;
1977
- this.errorTimerActive = false;
1978
- }));
2957
+ onStreamPostRequest() {
2958
+ if (!this.isValid)
2959
+ return;
2960
+ const reqParams = this.compileRequest();
2961
+ let payload = {};
2962
+ let apiPath = reqParams.path;
2963
+ let apiOptions = reqParams.apiOptions;
2964
+ let responseMapper = (items) => items.response;
2965
+ if (this.AIType === 0) {
2966
+ // API request
2967
+ payload = { prompt: this.questionControl.value };
2968
+ }
2969
+ else {
2970
+ // Local Ollama request
2971
+ apiOptions.server = "api";
2972
+ apiPath = ["generate"];
2973
+ apiOptions.stream = true;
2974
+ payload = {
2975
+ model: "phi3:latest",
2976
+ prompt: this.questionControl.value,
2977
+ stream: true,
2978
+ };
2979
+ responseMapper = (items) => items.map((word) => word.response).flat().join('');
1979
2980
  }
2981
+ this.requestParams.STREAM = apiOptions;
2982
+ this.STREAM_AI$ = EMPTY;
2983
+ this.STREAM_AI_error$.next('');
2984
+ this.STREAM_AI$ = this.httpManagerService.postRequest(payload, apiOptions, apiPath).pipe(map(responseMapper), tap(() => this.questionControl.reset()), catchError(error => throwError(() => this.errorHandling(error, 'STREAM'))));
1980
2985
  }
1981
- get hasError() {
1982
- return this._hasError;
2986
+ onStreamRequest() {
2987
+ if (!this.isValid)
2988
+ return;
2989
+ const reqParams = this.compileRequest();
2990
+ reqParams.apiOptions.stream = true;
2991
+ this.requestParams.GET = reqParams.apiOptions;
2992
+ this.STREAM$ = this.httpManagerService.getRequest(reqParams.apiOptions, reqParams.path)
2993
+ .pipe(
2994
+ // tap((data) => console.log("API STREAM response", data)),
2995
+ catchError(error => {
2996
+ return throwError(() => this.errorHandling(error, 'STREAM'));
2997
+ }));
1983
2998
  }
1984
- ngOnInit() { }
1985
- onAction() {
1986
- this.isPending = false;
1987
- if (this.event)
1988
- this.event();
2999
+ onDownloadCompleted() {
3000
+ const message = "Download Completed";
3001
+ const display = ToastDisplay.adapt({
3002
+ message,
3003
+ action: 'Ok',
3004
+ color: ToastColors.SUCCESS,
3005
+ icon: 'sentiment_satisfied_alt',
3006
+ });
3007
+ this.toastMessage.toastMessage(display);
1989
3008
  }
1990
- ngOnDestroy() {
1991
- this.subscriptions.unsubscribe();
3009
+ onDownloadFailed(err) {
3010
+ const message = "Download Failed";
3011
+ const display = ToastDisplay.adapt({
3012
+ message,
3013
+ action: 'Ok',
3014
+ color: ToastColors.ERROR,
3015
+ icon: 'warning',
3016
+ });
3017
+ this.toastMessage.toastMessage(display);
1992
3018
  }
1993
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DownloadFileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1994
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: DownloadFileComponent, selector: "app-download-file", inputs: { event: "event", displayError: "displayError", diameter: "diameter", mode: "mode", isPending: "isPending", active: "active", disabled: "disabled", progress: "progress", hasError: "hasError" }, outputs: { error: "error" }, ngImport: i0, template: "<ng-container *ngIf=\"!isPending; else DOWNLOADING\">\n <ng-container *ngIf=\"hasError; else NORMAL\">\n <div class=\"width center-txt\" style=\"margin-bottom: 4px;\">\n <mat-icon color=\"warn\" class=\"warn-icon\">warning</mat-icon>\n </div>\n </ng-container>\n <ng-template #NORMAL>\n <ng-container *ngIf=\"active; else ACTION\">\n\n <div class=\"container-obj\">\n <div class=\"centered-obj-div\">\n <mat-progress-spinner\n color=\"primary\"\n mode=\"indeterminate\"\n [diameter]=\"44\"\n ></mat-progress-spinner>\n </div>\n </div>\n\n </ng-container>\n <ng-template #ACTION>\n <button data-tracking=\"export-btn\" mat-icon-button (click)=\"onAction()\" class=\"icon-button\" [disabled]=\"disabled\">\n <mat-icon class=\"custom-icon\">file_download</mat-icon>\n </button>\n </ng-template>\n </ng-template>\n</ng-container>\n\n<ng-template #DOWNLOADING>\n <div\n class=\"spinner-container\"\n *ngIf=\"(progress > 0 && progress < 100); else INDETERMINATE\"\n >\n <div class=\"spinner-background\">\n {{progress}}%\n </div>\n <mat-progress-spinner\n color=\"primary\"\n [mode]=\"mode\"\n [value]=\"progress\"\n [diameter]=\"44\"\n ></mat-progress-spinner>\n </div>\n <ng-template #INDETERMINATE>\n <div class=\"container-obj\">\n <div class=\"centered-obj-div\">\n <mat-progress-spinner\n color=\"primary\"\n mode=\"indeterminate\"\n [diameter]=\"44\"\n ></mat-progress-spinner>\n </div>\n </div>\n </ng-template>\n\n</ng-template>\n\n\n\n", styles: [":not(spinner-container).spinner-container{position:relative}:not(spinner-container).spinner-container .spinner-background{position:absolute;width:44px;height:44px;font-size:12px;line-height:32px;text-align:center;overflow:hidden;border-radius:50%;border:solid 5px whitesmoke}.center-txt{align-content:center;text-align:-webkit-center}.width{width:48px;height:48px}.icon-button{display:flex;align-items:center;justify-content:center;width:48px;height:48px;padding:0}.container-obj{display:flex;justify-content:center;align-items:center;width:48px;height:48px}.centered-obj-div{text-align:center}\n"], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i3.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }], encapsulation: i0.ViewEncapsulation.None }); }
3019
+ errorHandling(err, type) {
3020
+ if (type === 'GET')
3021
+ this.GET_error$.next(err.message);
3022
+ if (type === 'POST')
3023
+ this.POST_error$.next(err.message);
3024
+ if (type === 'PUT')
3025
+ this.PUT_error$.next(err.message);
3026
+ if (type === 'DELETE')
3027
+ this.DELETE_error$.next(err.message);
3028
+ if (type === 'STREAM')
3029
+ this.STREAM_error$.next(err.message);
3030
+ }
3031
+ onSelectAIType(type) {
3032
+ this.AIType = type;
3033
+ }
3034
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RequestManagerDemoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3035
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: RequestManagerDemoComponent, selector: "app-request-manager-demo", viewQueries: [{ propertyName: "failedState", first: true, predicate: ["failedState"], descendants: true, static: true }, { propertyName: "pollingState", first: true, predicate: ["pollingState"], descendants: true, static: true }], ngImport: i0, template: "<div style=\"margin: 2rem;\">\n\n <h2>\n HTTP Request Manager\n </h2>\n\n <div [formGroup]=\"requestForm\" style=\"margin-top: 2rem;\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Adapter (Model)</mat-label>\n <mat-select formControlName=\"adapter\" #adapterSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let adapter of sampleAdaptors\" [value]=\"adapter.value\">\n {{adapter.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Mapper (Model)</mat-label>\n <mat-select formControlName=\"mapper\" #mapperSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let mapper of sampleMappers\" [value]=\"mapper.value\">\n {{mapper.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"display: flex; margin-bottom: 2rem;\" *ngIf=\"adapterSelect.value || mapperSelect.value\">\n <div style=\"flex:1\" class=\"box\">\n <h3>Adapter (Incoming)</h3>\n <div *ngIf=\"adapterSelect.value; else NO_ADAPTER\">\n {{ props(adapterSelect.value) | json }}\n </div>\n <ng-template #NO_ADAPTER>No Transformation</ng-template>\n </div>\n <div style=\"flex:1\" class=\"box\">\n <h3>Mapper (Outgoing)</h3>\n <div *ngIf=\"mapperSelect.value; else NO_MAPPER\">\n {{ props(mapperSelect.value) | json }}\n </div>\n <ng-template #NO_MAPPER>No Transformation</ng-template>\n </div>\n </div>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>RestPath (/ delimited)</mat-label>\n <input matInput placeholder=\"clients/list\" formControlName=\"path\">\n </mat-form-field>\n </div>\n <div>\n <div formArrayName=\"headers\">\n <div *ngFor=\"let task of headers.controls; let i = index\" [formGroupName]=\"i\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Key</mat-label>\n <input matInput placeholder=\"authentication\" formControlName=\"key\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Value</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"value\">\n </mat-form-field>\n <div style=\"margin-top: .5rem;\">\n <button mat-icon-button (click)=\"removeHeader(i)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n </div>\n </div>\n <button mat-stroked-button (click)=\"addHeader()\" class=\"btn\">Add Header</button>\n </div>\n <div style=\"margin-top: 2rem; display: flex; flex-direction:column; gap: 1rem;\">\n <div>\n <mat-slide-toggle #failedState>Retry on Failed</mat-slide-toggle>\n <div *ngIf=\"failedState.checked\" style=\"display: flex; gap: .5rem; margin-top: 1rem;\" formGroupName=\"retry\">\n <mat-form-field appearance=\"outline\">\n <mat-label>#of Times</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"times\" value=\"3\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Delay Until Next</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"delay\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n <div>\n <mat-slide-toggle #pollingState>Polling</mat-slide-toggle>\n <div *ngIf=\"pollingState.checked\">\n <mat-form-field appearance=\"outline\" style=\"margin-top: 1rem\">\n <mat-label>#of Seconds</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"polling\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n </div>\n </div>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n <mat-progress-bar mode=\"indeterminate\"\n *ngIf=\"(isPending$ | async)\"\n ></mat-progress-bar>\n <mat-progress-bar mode=\"determinate\"\n *ngIf=\"pollingState.checked && !(isPending$ | async)\"\n [value]=\"(this.countdown$ | async)\"\n ></mat-progress-bar>\n </div>\n\n <div style=\"margin-top: 1rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onGetRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(GET_error$ | async) as get_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ get_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(GET$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(GET$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">POST Request</h2>\n <div>\n <button mat-raised-button (click)=\"onCreateRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(POST_error$ | async) as post_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ post_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(POST$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(POST$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">PUT Request</h2>\n <div>\n <button mat-raised-button (click)=\"onUpdateRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(PUT_error$ | async) as put_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ put_error }}</mat-error>\n </div>\n\n <h3>Include Record ID in the RestPath</h3>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(PUT$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(PUT$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">DELETE Request</h2>\n <div>\n <button mat-raised-button (click)=\"onDeleteRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <h3>Include Record ID in the RestPath</h3>\n\n <div *ngIf=\"(DELETE_error$ | async) as delete_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ delete_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(DELETE$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(DELETE$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">Streaming GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <!-- <div *ngIf=\"(STREAM_error$ | async) as stream_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div> -->\n\n <div style=\"margin-top: 1rem;\">\n <div *ngIf=\"(STREAM$ | async) as data\" class=\"container\">\n <table mat-table [dataSource]=\"data\">\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> First Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.name}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"lastName\">\n <th mat-header-cell *matHeaderCellDef> Last Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.lastName}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"age\">\n <th mat-header-cell *matHeaderCellDef> Age </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.age}} </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1; padding-top: .5rem;\">AI -<span *ngIf=\"AIType === 1\">STREAMING</span> POST Request</h2>\n <div style=\"display: flex; gap: 1rem;\">\n <button mat-raised-button [matMenuTriggerFor]=\"menu\" style=\"min-width: 120px;\">\n <mat-icon>lan</mat-icon>\n <span *ngIf=\"AIType === 0; else LOCAL\">Server</span>\n <ng-template #LOCAL>\n Local\n </ng-template>\n </button>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item (click)=\"onSelectAIType(0)\">Server</button>\n <button mat-menu-item (click)=\"onSelectAIType(1)\">Local</button>\n </mat-menu>\n <div>\n <button mat-raised-button (click)=\"onStreamPostRequest()\" class=\"btn\">Ask Me</button>\n </div>\n </div>\n </div>\n\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Ask me a Question</mat-label>\n <textarea matInput placeholder=\"Why is the sky blue?\" [formControl]=\"questionControl\"></textarea>\n </mat-form-field>\n </div>\n\n <div *ngIf=\"(STREAM_AI_error$ | async) as stream_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div>\n\n <div *ngIf=\"AIType === 1; else ALTERNATIVE\" style=\"color: red;\">\n You must have Ollama active and the 'phi3:latest' model to use this feature.\n </div>\n <ng-template #ALTERNATIVE>\n <span style=\"color: gray;\">\n Define the RestPath to the API endpoint that will handle the AI request.\n Use: 'ai/chat' for server\n </span>\n </ng-template>\n\n <div>\n <div *ngIf=\"(STREAM_AI$ | async) as data\" style=\"margin-top: 1rem; font-size: 1.2rem; border-radius: 1rem; border: black 1px solid; padding: 2rem;\">\n <p style=\"margin-bottom: .5rem; white-space:pre-wrap; line-height: 1.6rem;\">{{data}}</p>\n </div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 1.5rem; margin-bottom: 1rem; line-height: 1.5rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <div>\n <div style=\"display: flex;\">\n <h2 style=\"flex:1; margin-bottom: 0; padding-top: .5rem; display: flex;\">\n <div>\n Download File\n </div>\n <div style=\"flex:1; margin-left: 1rem;\">\n <mat-slide-toggle #disable>\n <span *ngIf=\"disable.checked; else DISABLE\">\n Enable\n </span>\n <ng-template #DISABLE>Disable</ng-template>\n </mat-slide-toggle>\n </div>\n </h2>\n <div>\n <app-file-downloader\n [disabled]=\"disable.checked\"\n [delayError]=\"3\"\n [apiRequest]=\"downloadRequest\"\n (completed)=\"onDownloadCompleted()\"\n (failed)=\"onDownloadFailed($event)\"\n ></app-file-downloader>\n </div>\n </div>\n </div>\n\n</div>\n", styles: [".btn{min-width:120px}.mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor;background-color:#f5f5f5}.container{height:400px;overflow:auto}.box{padding:10px;border:1px solid #ccc}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { 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.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i2.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i2.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "component", type: i3$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i7.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i7.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i7.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i9.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i9.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i9.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i9.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i9.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i9.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i9.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i9.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i9.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i10.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "component", type: i11.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "component", type: i12.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "directive", type: i13.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: FileDownloaderComponent, selector: "app-file-downloader", inputs: ["delayError", "apiRequest", "displayErrorMessage", "saveFileAs", "labels", "disabled"], outputs: ["completed", "failed"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }] }); }
1995
3036
  }
1996
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DownloadFileComponent, decorators: [{
3037
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RequestManagerDemoComponent, decorators: [{
1997
3038
  type: Component,
1998
- args: [{ selector: 'app-download-file', encapsulation: ViewEncapsulation.None, template: "<ng-container *ngIf=\"!isPending; else DOWNLOADING\">\n <ng-container *ngIf=\"hasError; else NORMAL\">\n <div class=\"width center-txt\" style=\"margin-bottom: 4px;\">\n <mat-icon color=\"warn\" class=\"warn-icon\">warning</mat-icon>\n </div>\n </ng-container>\n <ng-template #NORMAL>\n <ng-container *ngIf=\"active; else ACTION\">\n\n <div class=\"container-obj\">\n <div class=\"centered-obj-div\">\n <mat-progress-spinner\n color=\"primary\"\n mode=\"indeterminate\"\n [diameter]=\"44\"\n ></mat-progress-spinner>\n </div>\n </div>\n\n </ng-container>\n <ng-template #ACTION>\n <button data-tracking=\"export-btn\" mat-icon-button (click)=\"onAction()\" class=\"icon-button\" [disabled]=\"disabled\">\n <mat-icon class=\"custom-icon\">file_download</mat-icon>\n </button>\n </ng-template>\n </ng-template>\n</ng-container>\n\n<ng-template #DOWNLOADING>\n <div\n class=\"spinner-container\"\n *ngIf=\"(progress > 0 && progress < 100); else INDETERMINATE\"\n >\n <div class=\"spinner-background\">\n {{progress}}%\n </div>\n <mat-progress-spinner\n color=\"primary\"\n [mode]=\"mode\"\n [value]=\"progress\"\n [diameter]=\"44\"\n ></mat-progress-spinner>\n </div>\n <ng-template #INDETERMINATE>\n <div class=\"container-obj\">\n <div class=\"centered-obj-div\">\n <mat-progress-spinner\n color=\"primary\"\n mode=\"indeterminate\"\n [diameter]=\"44\"\n ></mat-progress-spinner>\n </div>\n </div>\n </ng-template>\n\n</ng-template>\n\n\n\n", styles: [":not(spinner-container).spinner-container{position:relative}:not(spinner-container).spinner-container .spinner-background{position:absolute;width:44px;height:44px;font-size:12px;line-height:32px;text-align:center;overflow:hidden;border-radius:50%;border:solid 5px whitesmoke}.center-txt{align-content:center;text-align:-webkit-center}.width{width:48px;height:48px}.icon-button{display:flex;align-items:center;justify-content:center;width:48px;height:48px;padding:0}.container-obj{display:flex;justify-content:center;align-items:center;width:48px;height:48px}.centered-obj-div{text-align:center}\n"] }]
1999
- }], propDecorators: { event: [{
2000
- type: Input
2001
- }], displayError: [{
2002
- type: Input
2003
- }], diameter: [{
2004
- type: Input
2005
- }], mode: [{
2006
- type: Input
2007
- }], isPending: [{
2008
- type: Input
2009
- }], active: [{
2010
- type: Input
2011
- }], disabled: [{
2012
- type: Input
2013
- }], error: [{
2014
- type: Output
2015
- }], progress: [{
2016
- type: Input
2017
- }], hasError: [{
2018
- type: Input
3039
+ args: [{ selector: 'app-request-manager-demo', template: "<div style=\"margin: 2rem;\">\n\n <h2>\n HTTP Request Manager\n </h2>\n\n <div [formGroup]=\"requestForm\" style=\"margin-top: 2rem;\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Adapter (Model)</mat-label>\n <mat-select formControlName=\"adapter\" #adapterSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let adapter of sampleAdaptors\" [value]=\"adapter.value\">\n {{adapter.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Mapper (Model)</mat-label>\n <mat-select formControlName=\"mapper\" #mapperSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let mapper of sampleMappers\" [value]=\"mapper.value\">\n {{mapper.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"display: flex; margin-bottom: 2rem;\" *ngIf=\"adapterSelect.value || mapperSelect.value\">\n <div style=\"flex:1\" class=\"box\">\n <h3>Adapter (Incoming)</h3>\n <div *ngIf=\"adapterSelect.value; else NO_ADAPTER\">\n {{ props(adapterSelect.value) | json }}\n </div>\n <ng-template #NO_ADAPTER>No Transformation</ng-template>\n </div>\n <div style=\"flex:1\" class=\"box\">\n <h3>Mapper (Outgoing)</h3>\n <div *ngIf=\"mapperSelect.value; else NO_MAPPER\">\n {{ props(mapperSelect.value) | json }}\n </div>\n <ng-template #NO_MAPPER>No Transformation</ng-template>\n </div>\n </div>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>RestPath (/ delimited)</mat-label>\n <input matInput placeholder=\"clients/list\" formControlName=\"path\">\n </mat-form-field>\n </div>\n <div>\n <div formArrayName=\"headers\">\n <div *ngFor=\"let task of headers.controls; let i = index\" [formGroupName]=\"i\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Key</mat-label>\n <input matInput placeholder=\"authentication\" formControlName=\"key\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Value</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"value\">\n </mat-form-field>\n <div style=\"margin-top: .5rem;\">\n <button mat-icon-button (click)=\"removeHeader(i)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n </div>\n </div>\n <button mat-stroked-button (click)=\"addHeader()\" class=\"btn\">Add Header</button>\n </div>\n <div style=\"margin-top: 2rem; display: flex; flex-direction:column; gap: 1rem;\">\n <div>\n <mat-slide-toggle #failedState>Retry on Failed</mat-slide-toggle>\n <div *ngIf=\"failedState.checked\" style=\"display: flex; gap: .5rem; margin-top: 1rem;\" formGroupName=\"retry\">\n <mat-form-field appearance=\"outline\">\n <mat-label>#of Times</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"times\" value=\"3\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Delay Until Next</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"delay\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n <div>\n <mat-slide-toggle #pollingState>Polling</mat-slide-toggle>\n <div *ngIf=\"pollingState.checked\">\n <mat-form-field appearance=\"outline\" style=\"margin-top: 1rem\">\n <mat-label>#of Seconds</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"polling\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n </div>\n </div>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n <mat-progress-bar mode=\"indeterminate\"\n *ngIf=\"(isPending$ | async)\"\n ></mat-progress-bar>\n <mat-progress-bar mode=\"determinate\"\n *ngIf=\"pollingState.checked && !(isPending$ | async)\"\n [value]=\"(this.countdown$ | async)\"\n ></mat-progress-bar>\n </div>\n\n <div style=\"margin-top: 1rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onGetRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(GET_error$ | async) as get_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ get_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(GET$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(GET$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">POST Request</h2>\n <div>\n <button mat-raised-button (click)=\"onCreateRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(POST_error$ | async) as post_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ post_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(POST$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(POST$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">PUT Request</h2>\n <div>\n <button mat-raised-button (click)=\"onUpdateRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(PUT_error$ | async) as put_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ put_error }}</mat-error>\n </div>\n\n <h3>Include Record ID in the RestPath</h3>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(PUT$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(PUT$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">DELETE Request</h2>\n <div>\n <button mat-raised-button (click)=\"onDeleteRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <h3>Include Record ID in the RestPath</h3>\n\n <div *ngIf=\"(DELETE_error$ | async) as delete_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ delete_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(DELETE$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(DELETE$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">Streaming GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <!-- <div *ngIf=\"(STREAM_error$ | async) as stream_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div> -->\n\n <div style=\"margin-top: 1rem;\">\n <div *ngIf=\"(STREAM$ | async) as data\" class=\"container\">\n <table mat-table [dataSource]=\"data\">\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> First Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.name}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"lastName\">\n <th mat-header-cell *matHeaderCellDef> Last Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.lastName}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"age\">\n <th mat-header-cell *matHeaderCellDef> Age </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.age}} </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1; padding-top: .5rem;\">AI -<span *ngIf=\"AIType === 1\">STREAMING</span> POST Request</h2>\n <div style=\"display: flex; gap: 1rem;\">\n <button mat-raised-button [matMenuTriggerFor]=\"menu\" style=\"min-width: 120px;\">\n <mat-icon>lan</mat-icon>\n <span *ngIf=\"AIType === 0; else LOCAL\">Server</span>\n <ng-template #LOCAL>\n Local\n </ng-template>\n </button>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item (click)=\"onSelectAIType(0)\">Server</button>\n <button mat-menu-item (click)=\"onSelectAIType(1)\">Local</button>\n </mat-menu>\n <div>\n <button mat-raised-button (click)=\"onStreamPostRequest()\" class=\"btn\">Ask Me</button>\n </div>\n </div>\n </div>\n\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Ask me a Question</mat-label>\n <textarea matInput placeholder=\"Why is the sky blue?\" [formControl]=\"questionControl\"></textarea>\n </mat-form-field>\n </div>\n\n <div *ngIf=\"(STREAM_AI_error$ | async) as stream_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div>\n\n <div *ngIf=\"AIType === 1; else ALTERNATIVE\" style=\"color: red;\">\n You must have Ollama active and the 'phi3:latest' model to use this feature.\n </div>\n <ng-template #ALTERNATIVE>\n <span style=\"color: gray;\">\n Define the RestPath to the API endpoint that will handle the AI request.\n Use: 'ai/chat' for server\n </span>\n </ng-template>\n\n <div>\n <div *ngIf=\"(STREAM_AI$ | async) as data\" style=\"margin-top: 1rem; font-size: 1.2rem; border-radius: 1rem; border: black 1px solid; padding: 2rem;\">\n <p style=\"margin-bottom: .5rem; white-space:pre-wrap; line-height: 1.6rem;\">{{data}}</p>\n </div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 1.5rem; margin-bottom: 1rem; line-height: 1.5rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <div>\n <div style=\"display: flex;\">\n <h2 style=\"flex:1; margin-bottom: 0; padding-top: .5rem; display: flex;\">\n <div>\n Download File\n </div>\n <div style=\"flex:1; margin-left: 1rem;\">\n <mat-slide-toggle #disable>\n <span *ngIf=\"disable.checked; else DISABLE\">\n Enable\n </span>\n <ng-template #DISABLE>Disable</ng-template>\n </mat-slide-toggle>\n </div>\n </h2>\n <div>\n <app-file-downloader\n [disabled]=\"disable.checked\"\n [delayError]=\"3\"\n [apiRequest]=\"downloadRequest\"\n (completed)=\"onDownloadCompleted()\"\n (failed)=\"onDownloadFailed($event)\"\n ></app-file-downloader>\n </div>\n </div>\n </div>\n\n</div>\n", styles: [".btn{min-width:120px}.mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor;background-color:#f5f5f5}.container{height:400px;overflow:auto}.box{padding:10px;border:1px solid #ccc}\n"] }]
3040
+ }], ctorParameters: () => [], propDecorators: { failedState: [{
3041
+ type: ViewChild,
3042
+ args: ["failedState", { static: true }]
3043
+ }], pollingState: [{
3044
+ type: ViewChild,
3045
+ args: ["pollingState", { static: true }]
2019
3046
  }] } });
2020
3047
 
2021
- class FileDownloaderComponent extends HTTPManagerService {
2022
- set labels(value) {
2023
- this._labels = (value) ? DownloadLabels.adapt(value) : DownloadLabels.adapt();
3048
+ class StateManagerDemoService extends HTTPManagerStateService {
3049
+ constructor() {
3050
+ super(ApiRequest.adapt({
3051
+ server: "",
3052
+ path: [],
3053
+ headers: {},
3054
+ adapter: ClientInfo$1.adapt,
3055
+ mapper: ClientInfoMapper$1.adapt,
3056
+ stream: false,
3057
+ }), DataType.ARRAY, DatabaseStorage.adapt());
2024
3058
  }
2025
- get labels() {
2026
- return this._labels;
3059
+ setAPIOptions(apiOptions, dataType, database) {
3060
+ this.setApiRequestOptions(apiOptions, dataType, database);
2027
3061
  }
2028
- constructor() {
2029
- super();
2030
- this.delayError = 3;
2031
- this.apiRequest = ApiRequest.adapt();
2032
- this.displayErrorMessage = false;
2033
- this._labels = DownloadLabels.adapt();
2034
- this.active = false;
2035
- this.subscription = new Subscription();
2036
- this.completed = new EventEmitter();
2037
- this.failed = new EventEmitter();
2038
- this.disabled = false;
3062
+ getClients() {
3063
+ // const headers = {
3064
+ // auth: "sample-auth-token"
3065
+ // }
3066
+ // const sampleOptions = RequestOptions.adapt({ path: ["id", 12], headers, sample: true })
3067
+ this.fetchRecords();
2039
3068
  }
2040
- ngOnInit() {
3069
+ createClient(data) {
3070
+ // const headers = {
3071
+ // auth: "sample-auth-token"
3072
+ // }
3073
+ // const sampleOptions = RequestOptions.adapt({ path: ["id", 12], headers, sample: true })
3074
+ this.createRecord(data);
2041
3075
  }
2042
- onDownloadStreaming() {
2043
- if (this.active)
2044
- return;
2045
- this.active = true;
2046
- return this.downloadRequest(this.apiRequest, [])
2047
- .pipe(distinctUntilChanged(), catchError((err) => {
2048
- this.onError(err.message);
2049
- this.active = false;
2050
- this.failed.emit(err);
2051
- return throwError(() => err);
2052
- }), finalize(() => {
2053
- if (!this.active)
2054
- return;
2055
- this.active = false;
2056
- this.completed.emit();
2057
- }))
2058
- .subscribe();
3076
+ updateClient(data) {
3077
+ // const headers = {
3078
+ // auth: "sample-auth-token"
3079
+ // }
3080
+ data.id = 1031;
3081
+ const sampleOptions = RequestOptions.adapt({ path: [data.id] });
3082
+ this.updateRecord(data, sampleOptions);
2059
3083
  }
2060
- onError(message) {
2061
- if (!message || !this.displayErrorMessage)
2062
- return;
2063
- const display = ToastDisplay.adapt({
2064
- message,
2065
- action: 'Ok',
2066
- color: ToastColors.ERROR,
2067
- icon: 'error',
2068
- });
2069
- this.active = false;
2070
- this.toastMessage.toastMessage(display);
3084
+ deleteClient(data) {
3085
+ // const headers = {
3086
+ // auth: "sample-auth-token"
3087
+ // }
3088
+ data.id = 1031;
3089
+ const sampleOptions = RequestOptions.adapt({ path: [data.id] });
3090
+ this.deleteRecord(sampleOptions);
2071
3091
  }
2072
- OnDestroy() {
2073
- this.subscription.unsubscribe();
3092
+ streamRequest() {
3093
+ const headers = {
3094
+ auth: "sample-auth-token"
3095
+ };
3096
+ // const sampleOptions = RequestOptions.adapt({ path: [1], headers })
3097
+ this.fetchStream();
2074
3098
  }
2075
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FileDownloaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2076
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: FileDownloaderComponent, selector: "app-file-downloader", inputs: { delayError: "delayError", apiRequest: "apiRequest", displayErrorMessage: "displayErrorMessage", saveFileAs: "saveFileAs", labels: "labels", disabled: "disabled" }, outputs: { completed: "completed", failed: "failed" }, usesInheritance: true, ngImport: i0, template: "<app-download-file\n [disabled]=\"disabled\"\n [displayError]=\"3\"\n [event]=\"onDownloadStreaming.bind(this)\"\n [isPending]=\"(isPending$ | async) || false\"\n [progress]=\"(progress$ | async)\"\n [hasError]=\"(error$ | async)\"\n (error)=\"onError(labels.error)\"\n [active]=\"active\"\n></app-download-file>\n", styles: [".snackBarInfo{background-color:#f44336;color:#fff}.mat-simple-snackbar>span{font-weight:700}.mat-simple-snackbar-action .mat-button .mat-button-wrapper{color:#fff}.cdk-overlay-pane>.mat-snack-bar-container{width:100%}.mat-snack-bar-container{max-width:100%!important;width:100%}\n"], dependencies: [{ kind: "component", type: DownloadFileComponent, selector: "app-download-file", inputs: ["event", "displayError", "diameter", "mode", "isPending", "active", "disabled", "progress", "hasError"], outputs: ["error"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], encapsulation: i0.ViewEncapsulation.None }); }
3099
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: StateManagerDemoService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3100
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: StateManagerDemoService, providedIn: 'root' }); }
2077
3101
  }
2078
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FileDownloaderComponent, decorators: [{
2079
- type: Component,
2080
- args: [{ selector: 'app-file-downloader', encapsulation: ViewEncapsulation.None, template: "<app-download-file\n [disabled]=\"disabled\"\n [displayError]=\"3\"\n [event]=\"onDownloadStreaming.bind(this)\"\n [isPending]=\"(isPending$ | async) || false\"\n [progress]=\"(progress$ | async)\"\n [hasError]=\"(error$ | async)\"\n (error)=\"onError(labels.error)\"\n [active]=\"active\"\n></app-download-file>\n", styles: [".snackBarInfo{background-color:#f44336;color:#fff}.mat-simple-snackbar>span{font-weight:700}.mat-simple-snackbar-action .mat-button .mat-button-wrapper{color:#fff}.cdk-overlay-pane>.mat-snack-bar-container{width:100%}.mat-snack-bar-container{max-width:100%!important;width:100%}\n"] }]
2081
- }], ctorParameters: () => [], propDecorators: { delayError: [{
2082
- type: Input
2083
- }], apiRequest: [{
2084
- type: Input
2085
- }], displayErrorMessage: [{
2086
- type: Input
2087
- }], saveFileAs: [{
2088
- type: Input
2089
- }], labels: [{
2090
- type: Input
2091
- }], completed: [{
2092
- type: Output
2093
- }], failed: [{
2094
- type: Output
2095
- }], disabled: [{
2096
- type: Input
2097
- }] } });
3102
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: StateManagerDemoService, decorators: [{
3103
+ type: Injectable,
3104
+ args: [{
3105
+ providedIn: 'root'
3106
+ }]
3107
+ }], ctorParameters: () => [] });
2098
3108
 
2099
- class RequestManagerDemoComponent {
3109
+ class RequestManagerStateDemoComponent {
3110
+ get dataObservable$() {
3111
+ switch (this.requestType) {
3112
+ case 'GET':
3113
+ return this.GET$;
3114
+ case 'PUT':
3115
+ return this.PUT$;
3116
+ case 'POST':
3117
+ return this.POST$;
3118
+ case 'DELETE':
3119
+ return this.DELETE$;
3120
+ case 'STREAM':
3121
+ return this.STREAM;
3122
+ case 'STREAM_AI':
3123
+ return this.STREAM_AI;
3124
+ default:
3125
+ return this.GET$;
3126
+ break;
3127
+ }
3128
+ }
3129
+ get hasChanged() {
3130
+ return this.requestForm.dirty || this.requestForm.untouched;
3131
+ }
3132
+ get dataType() {
3133
+ return this.requestForm.get("datatype")?.value;
3134
+ }
3135
+ get database() {
3136
+ return this.requestForm.get("database")?.value;
3137
+ }
2100
3138
  get retry() {
2101
- return this.requestForm.get('retry')?.value;
3139
+ return this.requestForm.get("retry")?.value;
2102
3140
  }
2103
3141
  get headers() {
2104
3142
  return this.requestForm.get('headers');
@@ -2108,11 +3146,12 @@ class RequestManagerDemoComponent {
2108
3146
  return this.requestForm.valid;
2109
3147
  }
2110
3148
  constructor() {
3149
+ this.stateManagerDemoService = inject(StateManagerDemoService);
2111
3150
  this.displayedColumns = ['id', 'name', 'lastName', 'age'];
2112
3151
  this.fb = inject(FormBuilder);
2113
- this.toastMessage = inject(ToastMessageDisplayService);
2114
3152
  this.httpManagerService = inject(HTTPManagerService);
2115
- this.isPending$ = this.httpManagerService.isPending$;
3153
+ this.isPending$ = this.stateManagerDemoService.isPending$;
3154
+ this.error$ = this.stateManagerDemoService.error$;
2116
3155
  this.countdown$ = this.httpManagerService.countdown$;
2117
3156
  this.GET_error$ = new BehaviorSubject('');
2118
3157
  this.POST_error$ = new BehaviorSubject('');
@@ -2120,22 +3159,19 @@ class RequestManagerDemoComponent {
2120
3159
  this.DELETE_error$ = new BehaviorSubject('');
2121
3160
  this.STREAM_error$ = new BehaviorSubject('');
2122
3161
  this.STREAM_AI_error$ = new BehaviorSubject('');
2123
- this.requestParams = {
2124
- GET: ApiRequest.adapt(),
2125
- POST: ApiRequest.adapt(),
2126
- PUT: ApiRequest.adapt(),
2127
- DELETE: ApiRequest.adapt(),
2128
- STREAM: ApiRequest.adapt(),
2129
- };
3162
+ this.GET$ = new BehaviorSubject(null);
3163
+ this.POST$ = new BehaviorSubject(null);
3164
+ this.PUT$ = new BehaviorSubject(null);
3165
+ this.DELETE$ = new BehaviorSubject(null);
3166
+ this.STREAM = new BehaviorSubject(null);
3167
+ this.STREAM$ = this.STREAM.asObservable();
3168
+ this.STREAM_AI = new BehaviorSubject([]);
3169
+ this.STREAM_AI$ = this.STREAM_AI.asObservable()
3170
+ .pipe(map$1((items) => (items) ? items.map((item) => item.response) : []), map$1((items) => items.join('\n').trim()));
2130
3171
  this.questionControl = this.fb.control("", [Validators.required]);
2131
- this.downloadRequest = ApiRequest.adapt({
2132
- server: 'assets/images',
2133
- path: ['me.jpg'],
2134
- // saveAs: 'john.jpg' // Optional
2135
- });
2136
- // downloadRequest = ApiRequest.adapt({
2137
- // server: 'oidc/ai/file'
2138
- // })
3172
+ this.requestType = '';
3173
+ this.prompts = [];
3174
+ this.AIType = 0;
2139
3175
  this.sampleClientData = {
2140
3176
  id: 0,
2141
3177
  name: "Old School Dates",
@@ -2147,9 +3183,10 @@ class RequestManagerDemoComponent {
2147
3183
  modified: 1693003138,
2148
3184
  icon: "",
2149
3185
  imageFile: "",
2150
- email: "wavecoders@gmail.com"
2151
3186
  };
3187
+ this.selectedRecord = this.fb.control(null);
2152
3188
  this.requestForm = this.fb.group({
3189
+ datatype: this.fb.control('ARRAY'),
2153
3190
  path: this.fb.control("ai/"),
2154
3191
  headers: this.fb.array([]),
2155
3192
  adapter: [null],
@@ -2159,49 +3196,64 @@ class RequestManagerDemoComponent {
2159
3196
  delay: [3],
2160
3197
  }),
2161
3198
  polling: [3],
3199
+ database: this.fb.group({
3200
+ table: [''],
3201
+ expiresIn: ['1m'],
3202
+ })
2162
3203
  });
2163
- this.AIType = 0;
2164
3204
  this.sampleAdaptors = [
2165
- { label: "ClientInfo Basic", value: ClientInfo.adapt },
2166
- { label: "AI Prompt", value: AIPrompt.adapt },
3205
+ { label: "ClientInfo Basic", value: ClientInfo$1.adapt },
3206
+ { label: "AI Prompt", value: AIPrompt$1.adapt },
2167
3207
  ];
2168
3208
  this.sampleMappers = [
2169
- { label: "Mapper Basic", value: ClientInfoMapper.adapt },
2170
- { label: "AI Prompt", value: AIPrompt.adapt },
3209
+ { label: "Mapper Basic", value: ClientInfoMapper$1.adapt },
3210
+ { label: "AI Prompt", value: AIPrompt$1.adapt },
2171
3211
  ];
2172
- this.hasId = (arr) => {
2173
- if (arr.length === 0)
2174
- return false;
2175
- return !isNaN(arr[arr.length - 1]);
2176
- };
2177
- this.props = (adapter) => {
2178
- return (adapter) ? adapter() : null;
2179
- };
2180
3212
  // server = `http://sample-endpoint/as/authorization.oauth2`
2181
3213
  this.arrayObjectsToObjects = (arr) => {
2182
3214
  return Array.isArray(arr) ? arr.reduce((obj, item) => Object.assign(obj, { [item.key]: item.value }), {}) : {};
2183
3215
  };
3216
+ this.props = (adapter) => {
3217
+ return (adapter) ? adapter() : null;
3218
+ };
2184
3219
  }
2185
3220
  ngOnInit() {
2186
- // const reqGet2 = ApiRequest.adapt({
2187
- // server,
2188
- // path: ['clients'],
2189
- // headers: { authentication: "Bearer <KEY>" },
2190
- // adapter: ClientInfo,
2191
- // dataType: DataType.OBJECT,
2192
- // // concurrent: false,
2193
- // // polling: 3, //seconds
2194
- // })
2195
- // const req2 = [1024,1025,1026].map(item => {
2196
- // return this.httpManagerService.getRequest<ClientInfo[]>(reqGet2, [item])
2197
- // .pipe(
2198
- // catchError(error => {
2199
- // return throwError(() => new Error(error.error.message))
2200
- // })
2201
- // )
2202
- // })
2203
- // forkJoin(req2)
2204
- // .subscribe(res => console.log(res))
3221
+ this.stateManagerDemoService.data$.pipe(tap$1((data) => console.log("API STREAM_AI response", data)));
3222
+ this.error$.pipe(tap$1((data) => {
3223
+ debugger;
3224
+ console.log("API STREAM response", data);
3225
+ }), catchError$1(error => {
3226
+ return throwError(() => this.errorHandling(error, 'STREAM'));
3227
+ }));
3228
+ this.selectedRecord.valueChanges
3229
+ .subscribe((data) => {
3230
+ this.selectedRecord$ = (data) ? this.stateManagerDemoService.selectRecord$(data.id) : EMPTY;
3231
+ });
3232
+ this.stateManagerDemoService.data$
3233
+ .pipe(tap$1((data) => {
3234
+ switch (this.requestType) {
3235
+ case 'GET':
3236
+ this.GET$.next(data);
3237
+ break;
3238
+ case 'PUT':
3239
+ this.PUT$.next(data);
3240
+ break;
3241
+ case 'POST':
3242
+ this.POST$.next(data);
3243
+ break;
3244
+ case 'DELETE':
3245
+ this.DELETE$.next(data);
3246
+ break;
3247
+ case 'STREAM':
3248
+ this.STREAM.next(data);
3249
+ break;
3250
+ case 'STREAM_AI':
3251
+ this.STREAM_AI.next(data);
3252
+ break;
3253
+ default:
3254
+ break;
3255
+ }
3256
+ })).subscribe();
2205
3257
  }
2206
3258
  addHeader() {
2207
3259
  const header = this.fb.group({
@@ -2210,155 +3262,64 @@ class RequestManagerDemoComponent {
2210
3262
  });
2211
3263
  this.headers.push(header);
2212
3264
  }
2213
- removeHeader(index) {
2214
- this.headers.removeAt(index);
2215
- }
2216
- compileRequest() {
2217
- const requestParams = this.requestForm.value;
2218
- requestParams.headers = this.arrayObjectsToObjects(requestParams.headers || []);
2219
- const pathReq = (requestParams.path === "") ? [] : (requestParams.path || "").split("/");
2220
- if (!this.pollingState.checked)
2221
- requestParams.polling = 0;
2222
- if (!this.failedState.checked) {
2223
- requestParams.retry = { times: 0, delay: 0 };
2224
- }
2225
- const apiOptions = ApiRequest.adapt(requestParams);
2226
- apiOptions.path = [];
2227
- return { apiOptions: apiOptions, path: pathReq };
2228
- }
2229
- onGetRequest() {
2230
- if (!this.isValid)
2231
- return;
2232
- const reqParams = this.compileRequest();
2233
- this.requestParams.GET = reqParams.apiOptions;
2234
- this.GET$ = EMPTY; //Cancels Previous
2235
- this.GET_error$.next('');
2236
- this.GET$ = this.httpManagerService.getRequest(reqParams.apiOptions, reqParams.path)
2237
- .pipe(
2238
- // tap((data) => console.log("API GET response", data)),
2239
- catchError(error => {
2240
- return throwError(() => this.errorHandling(error, 'GET'));
2241
- }));
2242
- }
2243
- onCreateRequest() {
2244
- if (!this.isValid)
2245
- return;
2246
- const reqParams = this.compileRequest();
2247
- this.requestParams.POST = reqParams.apiOptions;
2248
- this.POST$ = EMPTY; //Cancels Previous
2249
- this.POST_error$.next('');
2250
- console.log("POST", this.sampleClientData);
2251
- console.log("POST", reqParams.apiOptions);
2252
- console.log("POST", reqParams.path);
2253
- this.POST$ = this.httpManagerService.postRequest(this.sampleClientData, reqParams.apiOptions, reqParams.path)
2254
- .pipe(
2255
- // tap((data) => console.log("API POST response", data)),
2256
- catchError(error => {
2257
- return throwError(() => this.errorHandling(error, 'POST'));
2258
- }));
2259
- }
2260
- onUpdateRequest() {
2261
- if (!this.isValid)
2262
- return;
2263
- const reqParams = this.compileRequest();
2264
- if (!this.hasId(reqParams.path)) {
2265
- console.log("Missing ID");
2266
- return;
2267
- }
2268
- this.sampleClientData.id = parseInt(reqParams.path[reqParams.path.length - 1]);
2269
- this.requestParams.PUT = reqParams.apiOptions;
2270
- this.PUT$ = EMPTY; //Cancels Previous
2271
- this.PUT_error$.next('');
2272
- this.PUT$ = this.httpManagerService.putRequest(this.sampleClientData, reqParams.apiOptions, reqParams.path)
2273
- .pipe(
2274
- // tap((data) => console.log("API PUT response", data)),
2275
- catchError(error => {
2276
- return throwError(() => this.errorHandling(error, 'PUT'));
2277
- }));
2278
- }
2279
- onDeleteRequest() {
2280
- if (!this.isValid)
2281
- return;
2282
- const reqParams = this.compileRequest();
2283
- this.requestParams.DELETE = reqParams.apiOptions;
2284
- if (!this.hasId(reqParams.path)) {
2285
- console.log("Missing ID");
2286
- return;
3265
+ removeHeader(index) {
3266
+ this.headers.removeAt(index);
3267
+ }
3268
+ compileRequest() {
3269
+ const requestParams = this.requestForm.value;
3270
+ requestParams.headers = this.arrayObjectsToObjects(requestParams.headers || []);
3271
+ const pathReq = (requestParams.path === "") ? [] : (requestParams.path || "").split("/");
3272
+ if (!this.pollingState.checked)
3273
+ requestParams.polling = 0;
3274
+ if (!this.failedState.checked) {
3275
+ requestParams.retry = { times: 0, delay: 0 };
2287
3276
  }
2288
- this.sampleClientData.id = parseInt(reqParams.path[reqParams.path.length - 1]);
2289
- this.requestParams.DELETE = reqParams.apiOptions;
2290
- this.DELETE$ = EMPTY; //Cancels Previous
2291
- this.DELETE_error$.next('');
2292
- this.DELETE$ = this.httpManagerService.deleteRequest(reqParams.apiOptions, reqParams.path)
2293
- .pipe(
2294
- // tap((data) => console.log("API DELETE response", data)),
2295
- catchError(error => {
2296
- return throwError(() => this.errorHandling(error, 'DELETE'));
2297
- }));
3277
+ const currentOptions = ApiRequest.adapt(requestParams);
3278
+ currentOptions.path = [];
3279
+ const apiOptions = ApiRequest.adapt({ ...currentOptions, path: pathReq });
3280
+ return { apiOptions: apiOptions, path: pathReq };
2298
3281
  }
2299
- onStreamPostRequest() {
3282
+ onSetStateOptions() {
2300
3283
  if (!this.isValid)
2301
3284
  return;
2302
3285
  const reqParams = this.compileRequest();
2303
- let payload = {};
2304
- let apiPath = reqParams.path;
2305
- let apiOptions = reqParams.apiOptions;
2306
- let responseMapper = (items) => items.response;
2307
- if (this.AIType === 0) {
2308
- // API request
2309
- payload = { prompt: this.questionControl.value };
2310
- }
2311
- else {
2312
- // Local Ollama request
2313
- apiOptions.server = "api";
2314
- apiPath = ["generate"];
2315
- apiOptions.stream = true;
2316
- payload = {
2317
- model: "phi3:latest",
2318
- prompt: this.questionControl.value,
2319
- stream: true,
2320
- };
2321
- responseMapper = (items) => items.map((word) => word.response).flat().join('');
2322
- }
2323
- this.requestParams.STREAM = apiOptions;
2324
- this.STREAM_AI$ = EMPTY;
2325
- this.STREAM_AI_error$.next('');
2326
- this.STREAM_AI$ = this.httpManagerService.postRequest(payload, apiOptions, apiPath).pipe(map(responseMapper), tap(() => this.questionControl.reset()), catchError(error => throwError(() => this.errorHandling(error, 'STREAM'))));
3286
+ const db = DatabaseStorage.adapt(this.database);
3287
+ const type = this.dataType === "ARRAY" ? DataType.ARRAY : DataType.OBJECT;
3288
+ this.stateManagerDemoService.setAPIOptions(reqParams.apiOptions, type, db);
3289
+ this.requestForm.markAsPristine();
3290
+ }
3291
+ onClearRecords() {
3292
+ this.stateManagerDemoService.clearRecords();
3293
+ }
3294
+ onRefreshRecords() {
3295
+ this.stateManagerDemoService.refreshData();
3296
+ }
3297
+ onGetRequest() {
3298
+ this.requestType = 'GET';
3299
+ this.stateManagerDemoService.getClients();
3300
+ }
3301
+ onCreateRequest() {
3302
+ this.requestType = 'POST';
3303
+ this.stateManagerDemoService.createClient(this.sampleClientData);
3304
+ }
3305
+ onUpdateRequest() {
3306
+ this.requestType = 'PUT';
3307
+ this.stateManagerDemoService.updateClient(this.sampleClientData);
3308
+ }
3309
+ onDeleteRequest() {
3310
+ this.requestType = 'DELETE';
3311
+ this.stateManagerDemoService.deleteClient(this.sampleClientData);
2327
3312
  }
2328
3313
  onStreamRequest() {
2329
3314
  if (!this.isValid)
2330
3315
  return;
2331
3316
  const reqParams = this.compileRequest();
2332
3317
  reqParams.apiOptions.stream = true;
2333
- this.requestParams.GET = reqParams.apiOptions;
2334
- this.STREAM$ = this.httpManagerService.getRequest(reqParams.apiOptions, reqParams.path)
2335
- .pipe(
2336
- // tap((data) => console.log("API STREAM response", data)),
2337
- catchError(error => {
2338
- return throwError(() => this.errorHandling(error, 'STREAM'));
2339
- }));
2340
- }
2341
- onDownloadCompleted() {
2342
- const message = "Download Completed";
2343
- const display = ToastDisplay.adapt({
2344
- message,
2345
- action: 'Ok',
2346
- color: ToastColors.SUCCESS,
2347
- icon: 'sentiment_satisfied_alt',
2348
- });
2349
- this.toastMessage.toastMessage(display);
2350
- }
2351
- onDownloadFailed(err) {
2352
- const message = "Download Failed";
2353
- const display = ToastDisplay.adapt({
2354
- message,
2355
- action: 'Ok',
2356
- color: ToastColors.ERROR,
2357
- icon: 'warning',
2358
- });
2359
- this.toastMessage.toastMessage(display);
3318
+ this.requestType = 'STREAM';
3319
+ this.stateManagerDemoService.streamRequest();
2360
3320
  }
2361
3321
  errorHandling(err, type) {
3322
+ console.log(err, type);
2362
3323
  if (type === 'GET')
2363
3324
  this.GET_error$.next(err.message);
2364
3325
  if (type === 'POST')
@@ -2369,16 +3330,21 @@ class RequestManagerDemoComponent {
2369
3330
  this.DELETE_error$.next(err.message);
2370
3331
  if (type === 'STREAM')
2371
3332
  this.STREAM_error$.next(err.message);
3333
+ if (type === 'STREAM_AI')
3334
+ this.STREAM_AI_error$.next(err.message);
2372
3335
  }
2373
3336
  onSelectAIType(type) {
2374
3337
  this.AIType = type;
2375
3338
  }
2376
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RequestManagerDemoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2377
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: RequestManagerDemoComponent, selector: "app-request-manager-demo", viewQueries: [{ propertyName: "failedState", first: true, predicate: ["failedState"], descendants: true, static: true }, { propertyName: "pollingState", first: true, predicate: ["pollingState"], descendants: true, static: true }], ngImport: i0, template: "<div style=\"margin: 2rem;\">\n\n <h2>\n HTTP Request Manager\n </h2>\n\n <div [formGroup]=\"requestForm\" style=\"margin-top: 2rem;\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Adapter (Model)</mat-label>\n <mat-select formControlName=\"adapter\" #adapterSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let adapter of sampleAdaptors\" [value]=\"adapter.value\">\n {{adapter.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Mapper (Model)</mat-label>\n <mat-select formControlName=\"mapper\" #mapperSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let mapper of sampleMappers\" [value]=\"mapper.value\">\n {{mapper.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"display: flex; margin-bottom: 2rem;\" *ngIf=\"adapterSelect.value || mapperSelect.value\">\n <div style=\"flex:1\" class=\"box\">\n <h3>Adapter (Incoming)</h3>\n <div *ngIf=\"adapterSelect.value; else NO_ADAPTER\">\n {{ props(adapterSelect.value) | json }}\n </div>\n <ng-template #NO_ADAPTER>No Transformation</ng-template>\n </div>\n <div style=\"flex:1\" class=\"box\">\n <h3>Mapper (Outgoing)</h3>\n <div *ngIf=\"mapperSelect.value; else NO_MAPPER\">\n {{ props(mapperSelect.value) | json }}\n </div>\n <ng-template #NO_MAPPER>No Transformation</ng-template>\n </div>\n </div>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>RestPath (/ delimited)</mat-label>\n <input matInput placeholder=\"clients/list\" formControlName=\"path\">\n </mat-form-field>\n </div>\n <div>\n <div formArrayName=\"headers\">\n <div *ngFor=\"let task of headers.controls; let i = index\" [formGroupName]=\"i\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Key</mat-label>\n <input matInput placeholder=\"authentication\" formControlName=\"key\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Value</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"value\">\n </mat-form-field>\n <div style=\"margin-top: .5rem;\">\n <button mat-icon-button (click)=\"removeHeader(i)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n </div>\n </div>\n <button mat-stroked-button (click)=\"addHeader()\" class=\"btn\">Add Header</button>\n </div>\n <div style=\"margin-top: 2rem; display: flex; flex-direction:column; gap: 1rem;\">\n <div>\n <mat-slide-toggle #failedState>Retry on Failed</mat-slide-toggle>\n <div *ngIf=\"failedState.checked\" style=\"display: flex; gap: .5rem; margin-top: 1rem;\" formGroupName=\"retry\">\n <mat-form-field appearance=\"outline\">\n <mat-label>#of Times</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"times\" value=\"3\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Delay Until Next</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"delay\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n <div>\n <mat-slide-toggle #pollingState>Polling</mat-slide-toggle>\n <div *ngIf=\"pollingState.checked\">\n <mat-form-field appearance=\"outline\" style=\"margin-top: 1rem\">\n <mat-label>#of Seconds</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"polling\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n </div>\n </div>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n <mat-progress-bar mode=\"indeterminate\"\n *ngIf=\"(isPending$ | async)\"\n ></mat-progress-bar>\n <mat-progress-bar mode=\"determinate\"\n *ngIf=\"pollingState.checked && !(isPending$ | async)\"\n [value]=\"(this.countdown$ | async)\"\n ></mat-progress-bar>\n </div>\n\n <div style=\"margin-top: 1rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onGetRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(GET_error$ | async) as get_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ get_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(GET$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(GET$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">POST Request</h2>\n <div>\n <button mat-raised-button (click)=\"onCreateRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(POST_error$ | async) as post_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ post_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(POST$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(POST$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">PUT Request</h2>\n <div>\n <button mat-raised-button (click)=\"onUpdateRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(PUT_error$ | async) as put_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ put_error }}</mat-error>\n </div>\n\n <h3>Include Record ID in the RestPath</h3>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(PUT$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(PUT$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">DELETE Request</h2>\n <div>\n <button mat-raised-button (click)=\"onDeleteRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <h3>Include Record ID in the RestPath</h3>\n\n <div *ngIf=\"(DELETE_error$ | async) as delete_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ delete_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(DELETE$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(DELETE$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">Streaming GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <!-- <div *ngIf=\"(STREAM_error$ | async) as stream_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div> -->\n\n <div style=\"margin-top: 1rem;\">\n <div *ngIf=\"(STREAM$ | async) as data\" class=\"container\">\n <table mat-table [dataSource]=\"data\">\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> First Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.name}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"lastName\">\n <th mat-header-cell *matHeaderCellDef> Last Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.lastName}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"age\">\n <th mat-header-cell *matHeaderCellDef> Age </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.age}} </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1; padding-top: .5rem;\">AI -<span *ngIf=\"AIType === 1\">STREAMING</span> POST Request</h2>\n <div style=\"display: flex; gap: 1rem;\">\n <button mat-raised-button [matMenuTriggerFor]=\"menu\" style=\"min-width: 120px;\">\n <mat-icon>lan</mat-icon>\n <span *ngIf=\"AIType === 0; else LOCAL\">Server</span>\n <ng-template #LOCAL>\n Local\n </ng-template>\n </button>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item (click)=\"onSelectAIType(0)\">Server</button>\n <button mat-menu-item (click)=\"onSelectAIType(1)\">Local</button>\n </mat-menu>\n <div>\n <button mat-raised-button (click)=\"onStreamPostRequest()\" class=\"btn\">Ask Me</button>\n </div>\n </div>\n </div>\n\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Ask me a Question</mat-label>\n <textarea matInput placeholder=\"Why is the sky blue?\" [formControl]=\"questionControl\"></textarea>\n </mat-form-field>\n </div>\n\n <div *ngIf=\"(STREAM_AI_error$ | async) as stream_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div>\n\n <div *ngIf=\"AIType === 1; else ALTERNATIVE\" style=\"color: red;\">\n You must have Ollama active and the 'phi3:latest' model to use this feature.\n </div>\n <ng-template #ALTERNATIVE>\n <span style=\"color: gray;\">\n Define the RestPath to the API endpoint that will handle the AI request.\n Use: 'ai/chat' for server\n </span>\n </ng-template>\n\n <div>\n <div *ngIf=\"(STREAM_AI$ | async) as data\" style=\"margin-top: 1rem; font-size: 1.2rem; border-radius: 1rem; border: black 1px solid; padding: 2rem;\">\n <p style=\"margin-bottom: .5rem; white-space:pre-wrap; line-height: 1.6rem;\">{{data}}</p>\n </div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 1.5rem; margin-bottom: 1rem; line-height: 1.5rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <div>\n <div style=\"display: flex;\">\n <h2 style=\"flex:1; margin-bottom: 0; padding-top: .5rem; display: flex;\">\n <div>\n Download File\n </div>\n <div style=\"flex:1; margin-left: 1rem;\">\n <mat-slide-toggle #disable>\n <span *ngIf=\"disable.checked; else DISABLE\">\n Enable\n </span>\n <ng-template #DISABLE>Disable</ng-template>\n </mat-slide-toggle>\n </div>\n </h2>\n <div>\n <app-file-downloader\n [disabled]=\"disable.checked\"\n [delayError]=\"3\"\n [apiRequest]=\"downloadRequest\"\n (completed)=\"onDownloadCompleted()\"\n (failed)=\"onDownloadFailed($event)\"\n ></app-file-downloader>\n </div>\n </div>\n </div>\n\n</div>\n", styles: [".btn{min-width:120px}.mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor;background-color:#f5f5f5}.container{height:400px;overflow:auto}.box{padding:10px;border:1px solid #ccc}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { 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.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i2.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i2.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "component", type: i3$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i7.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i7.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i7.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i9.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i9.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i9.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i9.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i9.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i9.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i9.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i9.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i9.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i10.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "component", type: i11.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "component", type: i12.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "directive", type: i13.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: FileDownloaderComponent, selector: "app-file-downloader", inputs: ["delayError", "apiRequest", "displayErrorMessage", "saveFileAs", "labels", "disabled"], outputs: ["completed", "failed"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }] }); }
3339
+ onClearHistory() {
3340
+ this.prompts = [];
3341
+ }
3342
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RequestManagerStateDemoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3343
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: RequestManagerStateDemoComponent, selector: "app-request-manager-state-demo", viewQueries: [{ propertyName: "failedState", first: true, predicate: ["failedState"], descendants: true, static: true }, { propertyName: "pollingState", first: true, predicate: ["pollingState"], descendants: true, static: true }], ngImport: i0, template: "<div style=\"margin: 2rem;\">\n\n <h2>\n HTTP Request State Manager\n </h2>\n\n <div [formGroup]=\"requestForm\" style=\"margin-top: 2rem;\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>State Data Type</mat-label>\n <mat-select formControlName=\"datatype\">\n <mat-option value=\"ARRAY\">Array</mat-option>\n <mat-option value=\"OBJECT\">Object</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Adapter (Model)</mat-label>\n <mat-select formControlName=\"adapter\" #adapterSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let adapter of sampleAdaptors\" [value]=\"adapter.value\">\n {{adapter.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Mapper (Model)</mat-label>\n <mat-select formControlName=\"mapper\" #mapperSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let mapper of sampleMappers\" [value]=\"mapper.value\">\n {{mapper.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <div style=\"display: flex; margin-bottom: 2rem;\" *ngIf=\"adapterSelect.value || mapperSelect.value\">\n <div style=\"flex:1\" class=\"box\">\n <h3>Adapter (Incoming)</h3>\n <div *ngIf=\"adapterSelect.value; else NO_ADAPTER\">\n {{ props(adapterSelect.value) | json }}\n </div>\n <ng-template #NO_ADAPTER>No Transformation</ng-template>\n </div>\n <div style=\"flex:1\" class=\"box\">\n <h3>Mapper (Outgoing)</h3>\n <div *ngIf=\"mapperSelect.value; else NO_MAPPER\">\n {{ props(mapperSelect.value) | json }}\n </div>\n <ng-template #NO_MAPPER>No Transformation</ng-template>\n </div>\n </div>\n\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>RestPath (/ delimited)</mat-label>\n <input matInput placeholder=\"clients/list\" formControlName=\"path\">\n </mat-form-field>\n </div>\n <div>\n <div formArrayName=\"headers\">\n <div *ngFor=\"let task of headers.controls; let i = index\" [formGroupName]=\"i\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Key</mat-label>\n <input matInput placeholder=\"authentication\" formControlName=\"key\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Value</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"value\">\n </mat-form-field>\n <div style=\"margin-top: .5rem;\">\n <button mat-icon-button (click)=\"removeHeader(i)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n </div>\n </div>\n <button mat-stroked-button (click)=\"addHeader()\">Add Header</button>\n </div>\n <div style=\"margin-top: 2rem; display: flex; flex-direction:column; gap: 1rem;\">\n <div>\n <mat-slide-toggle #failedState>Retry on Failed</mat-slide-toggle>\n <div *ngIf=\"failedState.checked\" style=\"display: flex; gap: .5rem; margin-top: 1rem;\" formGroupName=\"retry\">\n <mat-form-field appearance=\"outline\">\n <mat-label>#of Times</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"times\" value=\"3\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Delay Until Next</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"delay\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n <div>\n <mat-slide-toggle #pollingState>Polling</mat-slide-toggle>\n <div *ngIf=\"pollingState.checked\">\n <mat-form-field appearance=\"outline\" style=\"margin-top: 1rem\">\n <mat-label>#of Seconds</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"polling\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n <div>\n <mat-slide-toggle #DBState>Database Storage</mat-slide-toggle>\n <div *ngIf=\"DBState.checked\" style=\"display: flex; gap: .5rem; margin-top: 1rem\" formGroupName=\"database\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Table Name</mat-label>\n <input matInput placeholder=\"table\" formControlName=\"table\" value=\"\">\n </mat-form-field>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Expires In</mat-label>\n <mat-select formControlName=\"expiresIn\">\n <mat-option value=\"1m\">One Minute</mat-option>\n <mat-option value=\"1h\">One Hour</mat-option>\n <mat-option value=\"1d\">One Day</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n </div>\n <div style=\"margin-top: 1rem; display: flex;\">\n <span style=\"flex:1\"></span>\n <button mat-stroked-button (click)=\"onSetStateOptions()\" [disabled]=\"!hasChanged\">\n Set API Request Options\n </button>\n </div>\n <div>\n <mat-error *ngIf=\"(error$ | async) as error\">\n {{ error }}\n </mat-error>\n </div>\n </div>\n </div>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n <mat-progress-bar mode=\"indeterminate\"\n *ngIf=\"(isPending$ | async)\"\n ></mat-progress-bar>\n <div *ngIf=\"pollingState.checked\">\n <mat-progress-bar mode=\"determinate\"\n *ngIf=\"!(isPending$ | async) && (this.countdown$ | async) || -1 > 0\"\n [value]=\"(this.countdown$ | async)\"\n ></mat-progress-bar>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\" *ngIf=\"(GET$ | async) as data\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1;\">CURRENT STATE ({{ dataType }})</h2>\n </div>\n <div *ngIf=\"data.length > 1\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Records</mat-label>\n <mat-select [formControl]=\"selectedRecord\" [disabled]=\"data === null && data?.length === 0\">\n <mat-option [value]=\"\">None</mat-option>\n <mat-option *ngFor=\"let item of data\" [value]=\"item\">\n {{item.name || (item.first_name) | titlecase}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div *ngIf=\"(dataObservable$ | async) as dataRecord\">\n <div *ngIf=\"(selectedRecord$ | async) as record; else NO_RECORD\">\n {{ record | json }}\n </div>\n <ng-template #NO_RECORD>No Record Selected from State</ng-template>\n\n </div>\n <div style=\"margin-top: 1rem;\">\n <span *ngIf=\"data !== null && data?.length > 0; else NO_DATA\">\n State contains a Total of {{ data.length }} Records\n </span>\n <ng-template #NO_DATA>No Records</ng-template>\n <div style=\"display: flex; gap: .5rem; text-align: end; justify-content: flex-end;\">\n <button class=\"btn\" mat-stroked-button (click)=\"onClearRecords()\">Clear</button>\n <button class=\"btn\" mat-stroked-button (click)=\"onRefreshRecords()\" [disabled]=\"hasChanged\">Refresh</button>\n </div>\n </div>\n </div>\n\n\n\n\n <div style=\"margin-top: 1rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">GET Request ({{ dataType }})</h2>\n <div>\n <button mat-raised-button (click)=\"onGetRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(GET_error$ | async) as get_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ get_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(GET$ | async) | jsonv\"></div> -->\n <div *ngIf=\"(GET$ | async) as getData\">{{ getData | json }}</div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">POST Request</h2>\n <div>\n <button mat-raised-button (click)=\"onCreateRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(POST_error$ | async) as post_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ post_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(POST$ | async) | jsonv\"></div> -->\n <div *ngIf=\"(POST$ | async) as postData\">{{ postData | json }}</div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">PUT Request</h2>\n <div>\n <button mat-raised-button (click)=\"onUpdateRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(PUT_error$ | async) as put_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ put_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(PUT$ | async) | jsonv\"></div> -->\n <div *ngIf=\"(PUT$ | async) as putData\">{{ putData | json }}</div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">DELETE Request</h2>\n <div>\n <button mat-raised-button (click)=\"onDeleteRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(DELETE_error$ | async) as delete_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ delete_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(DELETE$ | async) | jsonv\"></div> -->\n <div *ngIf=\"(DELETE$ | async) as deleteData\">{{ deleteData | json }}</div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">Streaming GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\" [disabled]=\"hasChanged\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(STREAM_error$ | async) as stream_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <div *ngIf=\"(STREAM$ | async) as data\" class=\"container\">\n <table mat-table [dataSource]=\"data\">\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> First Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.name}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"lastName\">\n <th mat-header-cell *matHeaderCellDef> Last Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.lastName}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"age\">\n <th mat-header-cell *matHeaderCellDef> Age </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.age}} </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n </div>\n\n </div>\n\n</div>\n", styles: [".btn{min-width:120px}.mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor;background-color:#f5f5f5}.container{height:400px;overflow:auto}.box{padding:10px;border:1px solid #ccc}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { 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.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i2.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i2.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "component", type: i3$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i9.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i9.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i9.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i9.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i9.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i9.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i9.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i9.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i9.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i10.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "component", type: i11.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "component", type: i12.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "directive", type: i13.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }, { kind: "pipe", type: i1$1.TitleCasePipe, name: "titlecase" }] }); }
2378
3344
  }
2379
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RequestManagerDemoComponent, decorators: [{
3345
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RequestManagerStateDemoComponent, decorators: [{
2380
3346
  type: Component,
2381
- args: [{ selector: 'app-request-manager-demo', template: "<div style=\"margin: 2rem;\">\n\n <h2>\n HTTP Request Manager\n </h2>\n\n <div [formGroup]=\"requestForm\" style=\"margin-top: 2rem;\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Adapter (Model)</mat-label>\n <mat-select formControlName=\"adapter\" #adapterSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let adapter of sampleAdaptors\" [value]=\"adapter.value\">\n {{adapter.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Mapper (Model)</mat-label>\n <mat-select formControlName=\"mapper\" #mapperSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let mapper of sampleMappers\" [value]=\"mapper.value\">\n {{mapper.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"display: flex; margin-bottom: 2rem;\" *ngIf=\"adapterSelect.value || mapperSelect.value\">\n <div style=\"flex:1\" class=\"box\">\n <h3>Adapter (Incoming)</h3>\n <div *ngIf=\"adapterSelect.value; else NO_ADAPTER\">\n {{ props(adapterSelect.value) | json }}\n </div>\n <ng-template #NO_ADAPTER>No Transformation</ng-template>\n </div>\n <div style=\"flex:1\" class=\"box\">\n <h3>Mapper (Outgoing)</h3>\n <div *ngIf=\"mapperSelect.value; else NO_MAPPER\">\n {{ props(mapperSelect.value) | json }}\n </div>\n <ng-template #NO_MAPPER>No Transformation</ng-template>\n </div>\n </div>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>RestPath (/ delimited)</mat-label>\n <input matInput placeholder=\"clients/list\" formControlName=\"path\">\n </mat-form-field>\n </div>\n <div>\n <div formArrayName=\"headers\">\n <div *ngFor=\"let task of headers.controls; let i = index\" [formGroupName]=\"i\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Key</mat-label>\n <input matInput placeholder=\"authentication\" formControlName=\"key\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Value</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"value\">\n </mat-form-field>\n <div style=\"margin-top: .5rem;\">\n <button mat-icon-button (click)=\"removeHeader(i)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n </div>\n </div>\n <button mat-stroked-button (click)=\"addHeader()\" class=\"btn\">Add Header</button>\n </div>\n <div style=\"margin-top: 2rem; display: flex; flex-direction:column; gap: 1rem;\">\n <div>\n <mat-slide-toggle #failedState>Retry on Failed</mat-slide-toggle>\n <div *ngIf=\"failedState.checked\" style=\"display: flex; gap: .5rem; margin-top: 1rem;\" formGroupName=\"retry\">\n <mat-form-field appearance=\"outline\">\n <mat-label>#of Times</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"times\" value=\"3\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Delay Until Next</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"delay\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n <div>\n <mat-slide-toggle #pollingState>Polling</mat-slide-toggle>\n <div *ngIf=\"pollingState.checked\">\n <mat-form-field appearance=\"outline\" style=\"margin-top: 1rem\">\n <mat-label>#of Seconds</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"polling\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n </div>\n </div>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n <mat-progress-bar mode=\"indeterminate\"\n *ngIf=\"(isPending$ | async)\"\n ></mat-progress-bar>\n <mat-progress-bar mode=\"determinate\"\n *ngIf=\"pollingState.checked && !(isPending$ | async)\"\n [value]=\"(this.countdown$ | async)\"\n ></mat-progress-bar>\n </div>\n\n <div style=\"margin-top: 1rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onGetRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(GET_error$ | async) as get_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ get_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(GET$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(GET$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">POST Request</h2>\n <div>\n <button mat-raised-button (click)=\"onCreateRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(POST_error$ | async) as post_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ post_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(POST$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(POST$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">PUT Request</h2>\n <div>\n <button mat-raised-button (click)=\"onUpdateRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(PUT_error$ | async) as put_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ put_error }}</mat-error>\n </div>\n\n <h3>Include Record ID in the RestPath</h3>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(PUT$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(PUT$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">DELETE Request</h2>\n <div>\n <button mat-raised-button (click)=\"onDeleteRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <h3>Include Record ID in the RestPath</h3>\n\n <div *ngIf=\"(DELETE_error$ | async) as delete_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ delete_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"(DELETE$ | async) as dataRecord\">\n <!-- <div [innerHTML]=\"(DELETE$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">Streaming GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <!-- <div *ngIf=\"(STREAM_error$ | async) as stream_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div> -->\n\n <div style=\"margin-top: 1rem;\">\n <div *ngIf=\"(STREAM$ | async) as data\" class=\"container\">\n <table mat-table [dataSource]=\"data\">\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> First Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.name}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"lastName\">\n <th mat-header-cell *matHeaderCellDef> Last Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.lastName}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"age\">\n <th mat-header-cell *matHeaderCellDef> Age </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.age}} </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1; padding-top: .5rem;\">AI -<span *ngIf=\"AIType === 1\">STREAMING</span> POST Request</h2>\n <div style=\"display: flex; gap: 1rem;\">\n <button mat-raised-button [matMenuTriggerFor]=\"menu\" style=\"min-width: 120px;\">\n <mat-icon>lan</mat-icon>\n <span *ngIf=\"AIType === 0; else LOCAL\">Server</span>\n <ng-template #LOCAL>\n Local\n </ng-template>\n </button>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item (click)=\"onSelectAIType(0)\">Server</button>\n <button mat-menu-item (click)=\"onSelectAIType(1)\">Local</button>\n </mat-menu>\n <div>\n <button mat-raised-button (click)=\"onStreamPostRequest()\" class=\"btn\">Ask Me</button>\n </div>\n </div>\n </div>\n\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Ask me a Question</mat-label>\n <textarea matInput placeholder=\"Why is the sky blue?\" [formControl]=\"questionControl\"></textarea>\n </mat-form-field>\n </div>\n\n <div *ngIf=\"(STREAM_AI_error$ | async) as stream_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div>\n\n <div *ngIf=\"AIType === 1; else ALTERNATIVE\" style=\"color: red;\">\n You must have Ollama active and the 'phi3:latest' model to use this feature.\n </div>\n <ng-template #ALTERNATIVE>\n <span style=\"color: gray;\">\n Define the RestPath to the API endpoint that will handle the AI request.\n Use: 'ai/chat' for server\n </span>\n </ng-template>\n\n <div>\n <div *ngIf=\"(STREAM_AI$ | async) as data\" style=\"margin-top: 1rem; font-size: 1.2rem; border-radius: 1rem; border: black 1px solid; padding: 2rem;\">\n <p style=\"margin-bottom: .5rem; white-space:pre-wrap; line-height: 1.6rem;\">{{data}}</p>\n </div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 1.5rem; margin-bottom: 1rem; line-height: 1.5rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <div>\n <div style=\"display: flex;\">\n <h2 style=\"flex:1; margin-bottom: 0; padding-top: .5rem; display: flex;\">\n <div>\n Download File\n </div>\n <div style=\"flex:1; margin-left: 1rem;\">\n <mat-slide-toggle #disable>\n <span *ngIf=\"disable.checked; else DISABLE\">\n Enable\n </span>\n <ng-template #DISABLE>Disable</ng-template>\n </mat-slide-toggle>\n </div>\n </h2>\n <div>\n <app-file-downloader\n [disabled]=\"disable.checked\"\n [delayError]=\"3\"\n [apiRequest]=\"downloadRequest\"\n (completed)=\"onDownloadCompleted()\"\n (failed)=\"onDownloadFailed($event)\"\n ></app-file-downloader>\n </div>\n </div>\n </div>\n\n</div>\n", styles: [".btn{min-width:120px}.mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor;background-color:#f5f5f5}.container{height:400px;overflow:auto}.box{padding:10px;border:1px solid #ccc}\n"] }]
3347
+ args: [{ selector: 'app-request-manager-state-demo', template: "<div style=\"margin: 2rem;\">\n\n <h2>\n HTTP Request State Manager\n </h2>\n\n <div [formGroup]=\"requestForm\" style=\"margin-top: 2rem;\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>State Data Type</mat-label>\n <mat-select formControlName=\"datatype\">\n <mat-option value=\"ARRAY\">Array</mat-option>\n <mat-option value=\"OBJECT\">Object</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Adapter (Model)</mat-label>\n <mat-select formControlName=\"adapter\" #adapterSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let adapter of sampleAdaptors\" [value]=\"adapter.value\">\n {{adapter.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Mapper (Model)</mat-label>\n <mat-select formControlName=\"mapper\" #mapperSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let mapper of sampleMappers\" [value]=\"mapper.value\">\n {{mapper.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <div style=\"display: flex; margin-bottom: 2rem;\" *ngIf=\"adapterSelect.value || mapperSelect.value\">\n <div style=\"flex:1\" class=\"box\">\n <h3>Adapter (Incoming)</h3>\n <div *ngIf=\"adapterSelect.value; else NO_ADAPTER\">\n {{ props(adapterSelect.value) | json }}\n </div>\n <ng-template #NO_ADAPTER>No Transformation</ng-template>\n </div>\n <div style=\"flex:1\" class=\"box\">\n <h3>Mapper (Outgoing)</h3>\n <div *ngIf=\"mapperSelect.value; else NO_MAPPER\">\n {{ props(mapperSelect.value) | json }}\n </div>\n <ng-template #NO_MAPPER>No Transformation</ng-template>\n </div>\n </div>\n\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>RestPath (/ delimited)</mat-label>\n <input matInput placeholder=\"clients/list\" formControlName=\"path\">\n </mat-form-field>\n </div>\n <div>\n <div formArrayName=\"headers\">\n <div *ngFor=\"let task of headers.controls; let i = index\" [formGroupName]=\"i\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Key</mat-label>\n <input matInput placeholder=\"authentication\" formControlName=\"key\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Value</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"value\">\n </mat-form-field>\n <div style=\"margin-top: .5rem;\">\n <button mat-icon-button (click)=\"removeHeader(i)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n </div>\n </div>\n <button mat-stroked-button (click)=\"addHeader()\">Add Header</button>\n </div>\n <div style=\"margin-top: 2rem; display: flex; flex-direction:column; gap: 1rem;\">\n <div>\n <mat-slide-toggle #failedState>Retry on Failed</mat-slide-toggle>\n <div *ngIf=\"failedState.checked\" style=\"display: flex; gap: .5rem; margin-top: 1rem;\" formGroupName=\"retry\">\n <mat-form-field appearance=\"outline\">\n <mat-label>#of Times</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"times\" value=\"3\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Delay Until Next</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"delay\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n <div>\n <mat-slide-toggle #pollingState>Polling</mat-slide-toggle>\n <div *ngIf=\"pollingState.checked\">\n <mat-form-field appearance=\"outline\" style=\"margin-top: 1rem\">\n <mat-label>#of Seconds</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"polling\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n <div>\n <mat-slide-toggle #DBState>Database Storage</mat-slide-toggle>\n <div *ngIf=\"DBState.checked\" style=\"display: flex; gap: .5rem; margin-top: 1rem\" formGroupName=\"database\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Table Name</mat-label>\n <input matInput placeholder=\"table\" formControlName=\"table\" value=\"\">\n </mat-form-field>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Expires In</mat-label>\n <mat-select formControlName=\"expiresIn\">\n <mat-option value=\"1m\">One Minute</mat-option>\n <mat-option value=\"1h\">One Hour</mat-option>\n <mat-option value=\"1d\">One Day</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n </div>\n <div style=\"margin-top: 1rem; display: flex;\">\n <span style=\"flex:1\"></span>\n <button mat-stroked-button (click)=\"onSetStateOptions()\" [disabled]=\"!hasChanged\">\n Set API Request Options\n </button>\n </div>\n <div>\n <mat-error *ngIf=\"(error$ | async) as error\">\n {{ error }}\n </mat-error>\n </div>\n </div>\n </div>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n <mat-progress-bar mode=\"indeterminate\"\n *ngIf=\"(isPending$ | async)\"\n ></mat-progress-bar>\n <div *ngIf=\"pollingState.checked\">\n <mat-progress-bar mode=\"determinate\"\n *ngIf=\"!(isPending$ | async) && (this.countdown$ | async) || -1 > 0\"\n [value]=\"(this.countdown$ | async)\"\n ></mat-progress-bar>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\" *ngIf=\"(GET$ | async) as data\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1;\">CURRENT STATE ({{ dataType }})</h2>\n </div>\n <div *ngIf=\"data.length > 1\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Records</mat-label>\n <mat-select [formControl]=\"selectedRecord\" [disabled]=\"data === null && data?.length === 0\">\n <mat-option [value]=\"\">None</mat-option>\n <mat-option *ngFor=\"let item of data\" [value]=\"item\">\n {{item.name || (item.first_name) | titlecase}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div *ngIf=\"(dataObservable$ | async) as dataRecord\">\n <div *ngIf=\"(selectedRecord$ | async) as record; else NO_RECORD\">\n {{ record | json }}\n </div>\n <ng-template #NO_RECORD>No Record Selected from State</ng-template>\n\n </div>\n <div style=\"margin-top: 1rem;\">\n <span *ngIf=\"data !== null && data?.length > 0; else NO_DATA\">\n State contains a Total of {{ data.length }} Records\n </span>\n <ng-template #NO_DATA>No Records</ng-template>\n <div style=\"display: flex; gap: .5rem; text-align: end; justify-content: flex-end;\">\n <button class=\"btn\" mat-stroked-button (click)=\"onClearRecords()\">Clear</button>\n <button class=\"btn\" mat-stroked-button (click)=\"onRefreshRecords()\" [disabled]=\"hasChanged\">Refresh</button>\n </div>\n </div>\n </div>\n\n\n\n\n <div style=\"margin-top: 1rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">GET Request ({{ dataType }})</h2>\n <div>\n <button mat-raised-button (click)=\"onGetRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(GET_error$ | async) as get_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ get_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(GET$ | async) | jsonv\"></div> -->\n <div *ngIf=\"(GET$ | async) as getData\">{{ getData | json }}</div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">POST Request</h2>\n <div>\n <button mat-raised-button (click)=\"onCreateRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(POST_error$ | async) as post_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ post_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(POST$ | async) | jsonv\"></div> -->\n <div *ngIf=\"(POST$ | async) as postData\">{{ postData | json }}</div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">PUT Request</h2>\n <div>\n <button mat-raised-button (click)=\"onUpdateRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(PUT_error$ | async) as put_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ put_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(PUT$ | async) | jsonv\"></div> -->\n <div *ngIf=\"(PUT$ | async) as putData\">{{ putData | json }}</div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">DELETE Request</h2>\n <div>\n <button mat-raised-button (click)=\"onDeleteRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(DELETE_error$ | async) as delete_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ delete_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(DELETE$ | async) | jsonv\"></div> -->\n <div *ngIf=\"(DELETE$ | async) as deleteData\">{{ deleteData | json }}</div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">Streaming GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\" [disabled]=\"hasChanged\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(STREAM_error$ | async) as stream_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <div *ngIf=\"(STREAM$ | async) as data\" class=\"container\">\n <table mat-table [dataSource]=\"data\">\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> First Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.name}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"lastName\">\n <th mat-header-cell *matHeaderCellDef> Last Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.lastName}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"age\">\n <th mat-header-cell *matHeaderCellDef> Age </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.age}} </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n </div>\n\n </div>\n\n</div>\n", styles: [".btn{min-width:120px}.mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor;background-color:#f5f5f5}.container{height:400px;overflow:auto}.box{padding:10px;border:1px solid #ccc}\n"] }]
2382
3348
  }], ctorParameters: () => [], propDecorators: { failedState: [{
2383
3349
  type: ViewChild,
2384
3350
  args: ["failedState", { static: true }]
@@ -2387,98 +3353,240 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
2387
3353
  args: ["pollingState", { static: true }]
2388
3354
  }] } });
2389
3355
 
2390
- class StateManagerDemoService extends HTTPManagerStateService {
2391
- constructor() {
2392
- super(ApiRequest.adapt({
2393
- server: "",
2394
- path: [],
2395
- headers: {},
2396
- adapter: ClientInfo.adapt,
2397
- mapper: ClientInfoMapper.adapt,
2398
- stream: false,
2399
- }), DataType.ARRAY, DatabaseStorage.adapt());
3356
+ class LocalStorageDemoComponent {
3357
+ get type() {
3358
+ return (this.typeControl.value) ? +this.typeControl.value : 0;
3359
+ }
3360
+ get isValid() {
3361
+ return this.newStoreForm.valid;
3362
+ }
3363
+ get isValidData() {
3364
+ return this.storageForm.valid;
3365
+ }
3366
+ constructor(configOptions) {
3367
+ this.configOptions = configOptions;
3368
+ this.fb = inject(FormBuilder);
3369
+ this.utils = inject(UtilsService);
3370
+ this.type$ = new BehaviorSubject(StorageType.GLOBAL);
3371
+ this.typeControl = this.fb.control(StorageType.GLOBAL.toString());
3372
+ this.localStorageManagerService = inject(LocalStorageManagerService);
3373
+ this.settings$ = this.localStorageManagerService.settings$;
3374
+ this.setting$ = (store) => this.localStorageManagerService.setting$(store);
3375
+ this.storageForm = this.fb.group({
3376
+ store: this.fb.control(null),
3377
+ type: 'local',
3378
+ settingType: 'local',
3379
+ encrypted: false,
3380
+ data: this.fb.control('', Validators.required),
3381
+ });
3382
+ this.newStoreForm = this.fb.group({
3383
+ name: this.fb.control(null, Validators.required),
3384
+ storage: 'local',
3385
+ encrypted: false,
3386
+ data: this.fb.control('', Validators.required),
3387
+ expiresIn: this.fb.control('0')
3388
+ });
3389
+ this.storeData$ = this.storageForm.get('store')?.valueChanges
3390
+ .pipe(switchMap((data) => {
3391
+ return data
3392
+ ? this.localStorageManagerService.store$(data.name)
3393
+ : of('');
3394
+ }), tap(data => {
3395
+ this.storageForm.get('data')?.patchValue(data, { emitEvent: false });
3396
+ }));
3397
+ this.expiresIn = (epoch) => this.utils.expiresIn(epoch);
3398
+ this.isValidJSON = (str) => {
3399
+ try {
3400
+ JSON.parse(str);
3401
+ return true;
3402
+ }
3403
+ catch (e) {
3404
+ return false;
3405
+ }
3406
+ };
3407
+ this.displayedColumns = ['name', 'id', 'encrypted', 'expires', "option"];
3408
+ this.filterData = (values) => {
3409
+ if (!values)
3410
+ return [];
3411
+ return values.filter((item) => item.options.storage === +this.type);
3412
+ };
3413
+ this.create = false;
3414
+ }
3415
+ ngOnInit() {
3416
+ this.storeProps = this.configOptions?.LocalStorageOptions;
3417
+ this.options = this.storeProps?.options;
3418
+ if (this.options?.storage) {
3419
+ this.typeControl.patchValue(this.options.storage.toString());
3420
+ this.typeControl.disable();
3421
+ }
3422
+ else {
3423
+ this.typeControl.enable();
3424
+ }
3425
+ if (this.options?.expiresIn) {
3426
+ this.newStoreForm.get('expiresIn')?.patchValue(this.options.expiresIn);
3427
+ this.newStoreForm.get('expiresIn')?.disable();
3428
+ }
3429
+ else {
3430
+ this.newStoreForm.get('expiresIn')?.enable();
3431
+ }
3432
+ if (this.options?.encrypted) {
3433
+ this.newStoreForm.get('encrypted')?.patchValue(this.options.encrypted);
3434
+ this.newStoreForm.get('encrypted')?.disable();
3435
+ }
3436
+ else {
3437
+ this.newStoreForm.get('encrypted')?.enable();
3438
+ }
3439
+ }
3440
+ onCreateStore() {
3441
+ if (!this.isValid)
3442
+ return;
3443
+ const store = this.newStoreForm.value;
3444
+ if (!store.name || store.name === '')
3445
+ return;
3446
+ const options = { storage: this.type, encrypted: store.encrypted, expiresIn: store.expiresIn };
3447
+ this.localStorageManagerService.createStore({
3448
+ name: store.name,
3449
+ data: store.data,
3450
+ options: SettingOptions.adapt(options)
3451
+ });
3452
+ this.newStoreForm.reset();
3453
+ this.create = false;
3454
+ }
3455
+ onUpdateStore(store) {
3456
+ if (!this.storageForm.valid)
3457
+ return;
3458
+ const storeData = this.storageForm.value;
3459
+ const data = JSON.parse(storeData.data || '');
3460
+ const type = (storeData.type === 'local') ? StorageType.GLOBAL : StorageType.SESSION;
3461
+ this.localStorageManagerService.updateStore({
3462
+ name: store.name,
3463
+ data
3464
+ });
3465
+ }
3466
+ onSelectedRow(store) {
3467
+ this.store = store;
3468
+ this.data$ = this.localStorageManagerService.store$(store.name).pipe(map(item => JSON.stringify(item)));
3469
+ this.create = false;
3470
+ }
3471
+ onCreate() {
3472
+ this.onCancel();
3473
+ this.create = true;
3474
+ }
3475
+ onDelete(store) {
3476
+ this.localStorageManagerService.deleteStore({
3477
+ name: store.name,
3478
+ });
3479
+ this.onCancel();
3480
+ }
3481
+ onCancel() {
3482
+ this.data$ = EMPTY;
3483
+ this.store = null;
3484
+ this.create = false;
2400
3485
  }
2401
- setAPIOptions(apiOptions, dataType, database) {
2402
- this.setApiRequestOptions(apiOptions, dataType, database);
3486
+ onUpdate(store, data) {
3487
+ this.localStorageManagerService.updateStore({
3488
+ name: store.name,
3489
+ data: JSON.parse(data)
3490
+ });
3491
+ this.onCancel();
2403
3492
  }
2404
- getClients() {
2405
- // const headers = {
2406
- // auth: "sample-auth-token"
2407
- // }
2408
- // const sampleOptions = RequestOptions.adapt({ path: ["id", 12], headers, sample: true })
2409
- this.fetchRecords();
3493
+ onReset() {
3494
+ this.localStorageManagerService.resetStore();
2410
3495
  }
2411
- createClient(data) {
2412
- // const headers = {
2413
- // auth: "sample-auth-token"
2414
- // }
2415
- // const sampleOptions = RequestOptions.adapt({ path: ["id", 12], headers, sample: true })
2416
- this.createRecord(data);
3496
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LocalStorageDemoComponent, deps: [{ token: CONFIG_SETTINGS_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
3497
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: LocalStorageDemoComponent, selector: "app-local-storage-demo", ngImport: i0, template: "<div style=\"margin: 2rem;\">\n\n <div style=\"display: flex; gap: 1rem\">\n <h2 style=\"padding-top: .5rem; display: flex;\">\n <div style=\"padding-top: .5rem;\">Local Storage Manager</div>\n <div>\n <button mat-icon-button [matMenuTriggerFor]=\"menu\">\n <mat-icon>info</mat-icon>\n </button>\n </div>\n </h2>\n <span style=\"flex:1\"></span>\n <div style=\"padding-top: .25rem;\">\n <mat-button-toggle-group name=\"storage\" [formControl]=\"typeControl\" (change)=\"onCancel()\">\n <mat-button-toggle value=\"0\">Local Storage</mat-button-toggle>\n <mat-button-toggle value=\"1\">Session Storage</mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n <div style=\"margin-top: .25rem;\">\n <button mat-icon-button (click)=\"onCreate()\">\n <mat-icon>add</mat-icon>\n </button>\n </div>\n </div>\n\n <ng-container *ngIf=\"storeProps?.storageName || storeProps?.storageSettingsName\">\n <div style=\"display: flex; gap: .5rem; flex-direction: column; border: gray solid thin; background-color: whitesmoke; padding: 1rem;\"\n >\n <div style=\"display: flex;\">\n <div *ngIf=\"storeProps?.storageSettingsName\" style=\"flex:1\">\n Database: <b>{{ storeProps?.storageSettingsName }}</b>\n </div>\n <div *ngIf=\"storeProps?.storageName\" style=\"flex:1\">\n Data: <b>{{ storeProps?.storageName }}</b>\n </div>\n </div>\n </div>\n </ng-container>\n\n <div style=\"margin-top: 2rem;\" [formGroup]=\"newStoreForm\" *ngIf=\"create; else LIST\">\n\n <h2>Create Store</h2>\n\n <div style=\"display: flex; gap: 1rem;\">\n <div style=\"flex:1\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Store Name</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"name\">\n </mat-form-field>\n </div>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Expires In...</mat-label>\n <mat-select formControlName=\"expiresIn\">\n <mat-option value=\"0\">None</mat-option>\n <mat-option value=\"30s\">30 Seconds</mat-option>\n <mat-option value=\"1mn\">1 Minute</mat-option>\n <mat-option value=\"1hr\">1 Hour</mat-option>\n <mat-option value=\"1d\">1 Day</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"margin-top: 1rem;\">\n <mat-slide-toggle formControlName=\"encrypted\">Encrypted</mat-slide-toggle>\n </div>\n </div>\n\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex: 1;\">\n <mat-label>Data (Must be an Object or Array of any)</mat-label>\n <textarea matInput placeholder=\"[]\" formControlName=\"data\" #json></textarea>\n </mat-form-field>\n </div>\n <mat-error *ngIf=\"!isValidJSON(json.value)\">Not Valid Data</mat-error>\n\n <div style=\"display: flex;\">\n <span style=\"flex:1\"></span>\n <button mat-stroked-button (click)=\"onCreateStore()\" [disabled]=\"!(isValid && isValidJSON(json.value))\">\n Save Store\n </button>\n </div>\n\n </div>\n\n <ng-template #LIST>\n\n <div style=\"margin-top: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n{{ settings$ | async | json }}\n <div *ngIf=\"filterData(settings$ | async) as data\">\n <ng-container *ngIf=\"data.length > 0; else NO_DATA\">\n <table mat-table [dataSource]=\"data\">\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> Store </th>\n <td mat-cell *matCellDef=\"let element\" style=\"font-weight: bold; text-transform: uppercase;\"> {{element.name}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"encrypted\">\n <th mat-header-cell *matHeaderCellDef style=\"text-align: center;\"> Encrypted </th>\n <td mat-cell *matCellDef=\"let element\" style=\"text-align: center;\">\n <ng-container *ngIf=\"element.options.encrypted; else NO\">YES</ng-container>\n <ng-template #NO><span style=\"color:gray\">NO</span></ng-template>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"expires\">\n <th mat-header-cell *matHeaderCellDef style=\"text-align: center;\"> Expires </th>\n <td mat-cell *matCellDef=\"let element\" style=\"text-align: center;\">\n <ng-container *ngIf=\"element.options.expires !== 0; else NO_DATA\">\n {{expiresIn(element.options.expires)}}\n </ng-container>\n <ng-template #NO_DATA>\n \u221E\n </ng-template>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"option\">\n <th mat-header-cell *matHeaderCellDef></th>\n <td mat-cell *matCellDef=\"let element\" style=\"padding-right: 0;\">\n <div style=\"display: flex;justify-content: flex-end;\">\n <button mat-icon-button color=\"warn\" (click)=\"onDelete(element); $event.stopPropagation()\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\" (click)=\"onSelectedRow(row)\"></tr>\n </table>\n </ng-container>\n\n <ng-template #NO_DATA>\n <h3 style=\"margin-top: 1rem;\">No Data</h3>\n </ng-template>\n\n <div *ngIf=\"(data$ | async) as data\" style=\"margin-top: 2rem;\">\n <div style=\"margin-bottom: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <h3>STORE: <span style=\"font-weight: bold;\">{{ store.name | uppercase }}</span></h3>\n <h3>SETTINGS: {{ setting$(store.name) | async | json }}</h3>\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex: 1;\">\n <mat-label>Data (Must be an Object or Array of any)</mat-label>\n <textarea matInput placeholder=\"[]\" #json [value]=\"data\"></textarea>\n </mat-form-field>\n </div>\n <mat-error *ngIf=\"!isValidJSON(json.value)\">Not Valid Data</mat-error>\n\n <div style=\"display: flex; gap: .5rem;\">\n <span style=\"flex:1\"></span>\n <button mat-stroked-button (click)=\"onCancel()\">Cancel</button>\n <button mat-stroked-button (click)=\"onUpdate(store, json.value)\" [disabled]=\"!(isValidJSON(json.value))\">Update</button>\n </div>\n </div>\n </div>\n\n </ng-template>\n\n <div style=\"margin-top: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <button mat-stroked-button (click)=\"onReset()\">Delete All Stores</button>\n </div>\n\n</div>\n\n\n<mat-menu #menu=\"matMenu\">\n <div style=\"padding: 1rem;\">\n <p>Please note that the LocalStorage (encryption) and management of data is dependant on initializing the APP_ID</p>\n <p>Must provide a value for <b>APP_ID</b> in AppModule->Providers</p>\n <mat-divider></mat-divider>\n <div style=\"font-size: smaller; margin-top: .5rem;\">\n <p>Example: UUID (self.crypto.randomUUID() to generate)</p>\n <div style=\"background-color: whitesmoke; padding: 1rem;\">\n &#123;<br>\n provide: APP_ID,<br>\n useValue: \"056991ac-3537-43ab-b5b9-83edf6554eff\",<br>\n &#125;\n </div>\n </div>\n </div>\n</mat-menu>\n", styles: [".mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor}.demo-row-is-clicked{font-weight:700}.mat-mdc-menu-panel.mat-mdc-menu-panel{max-width:280px!important}\n"], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { 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.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i3$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i7.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "directive", type: i7.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i9.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i9.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i9.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i9.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i9.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i9.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i9.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i9.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i9.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i10$1.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i10$1.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "component", type: i11.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "component", type: i12.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "directive", type: i13.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.UpperCasePipe, name: "uppercase" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }], encapsulation: i0.ViewEncapsulation.None }); }
3498
+ }
3499
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LocalStorageDemoComponent, decorators: [{
3500
+ type: Component,
3501
+ args: [{ selector: 'app-local-storage-demo', encapsulation: ViewEncapsulation.None, template: "<div style=\"margin: 2rem;\">\n\n <div style=\"display: flex; gap: 1rem\">\n <h2 style=\"padding-top: .5rem; display: flex;\">\n <div style=\"padding-top: .5rem;\">Local Storage Manager</div>\n <div>\n <button mat-icon-button [matMenuTriggerFor]=\"menu\">\n <mat-icon>info</mat-icon>\n </button>\n </div>\n </h2>\n <span style=\"flex:1\"></span>\n <div style=\"padding-top: .25rem;\">\n <mat-button-toggle-group name=\"storage\" [formControl]=\"typeControl\" (change)=\"onCancel()\">\n <mat-button-toggle value=\"0\">Local Storage</mat-button-toggle>\n <mat-button-toggle value=\"1\">Session Storage</mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n <div style=\"margin-top: .25rem;\">\n <button mat-icon-button (click)=\"onCreate()\">\n <mat-icon>add</mat-icon>\n </button>\n </div>\n </div>\n\n <ng-container *ngIf=\"storeProps?.storageName || storeProps?.storageSettingsName\">\n <div style=\"display: flex; gap: .5rem; flex-direction: column; border: gray solid thin; background-color: whitesmoke; padding: 1rem;\"\n >\n <div style=\"display: flex;\">\n <div *ngIf=\"storeProps?.storageSettingsName\" style=\"flex:1\">\n Database: <b>{{ storeProps?.storageSettingsName }}</b>\n </div>\n <div *ngIf=\"storeProps?.storageName\" style=\"flex:1\">\n Data: <b>{{ storeProps?.storageName }}</b>\n </div>\n </div>\n </div>\n </ng-container>\n\n <div style=\"margin-top: 2rem;\" [formGroup]=\"newStoreForm\" *ngIf=\"create; else LIST\">\n\n <h2>Create Store</h2>\n\n <div style=\"display: flex; gap: 1rem;\">\n <div style=\"flex:1\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Store Name</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"name\">\n </mat-form-field>\n </div>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Expires In...</mat-label>\n <mat-select formControlName=\"expiresIn\">\n <mat-option value=\"0\">None</mat-option>\n <mat-option value=\"30s\">30 Seconds</mat-option>\n <mat-option value=\"1mn\">1 Minute</mat-option>\n <mat-option value=\"1hr\">1 Hour</mat-option>\n <mat-option value=\"1d\">1 Day</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"margin-top: 1rem;\">\n <mat-slide-toggle formControlName=\"encrypted\">Encrypted</mat-slide-toggle>\n </div>\n </div>\n\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex: 1;\">\n <mat-label>Data (Must be an Object or Array of any)</mat-label>\n <textarea matInput placeholder=\"[]\" formControlName=\"data\" #json></textarea>\n </mat-form-field>\n </div>\n <mat-error *ngIf=\"!isValidJSON(json.value)\">Not Valid Data</mat-error>\n\n <div style=\"display: flex;\">\n <span style=\"flex:1\"></span>\n <button mat-stroked-button (click)=\"onCreateStore()\" [disabled]=\"!(isValid && isValidJSON(json.value))\">\n Save Store\n </button>\n </div>\n\n </div>\n\n <ng-template #LIST>\n\n <div style=\"margin-top: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n{{ settings$ | async | json }}\n <div *ngIf=\"filterData(settings$ | async) as data\">\n <ng-container *ngIf=\"data.length > 0; else NO_DATA\">\n <table mat-table [dataSource]=\"data\">\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> Store </th>\n <td mat-cell *matCellDef=\"let element\" style=\"font-weight: bold; text-transform: uppercase;\"> {{element.name}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"encrypted\">\n <th mat-header-cell *matHeaderCellDef style=\"text-align: center;\"> Encrypted </th>\n <td mat-cell *matCellDef=\"let element\" style=\"text-align: center;\">\n <ng-container *ngIf=\"element.options.encrypted; else NO\">YES</ng-container>\n <ng-template #NO><span style=\"color:gray\">NO</span></ng-template>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"expires\">\n <th mat-header-cell *matHeaderCellDef style=\"text-align: center;\"> Expires </th>\n <td mat-cell *matCellDef=\"let element\" style=\"text-align: center;\">\n <ng-container *ngIf=\"element.options.expires !== 0; else NO_DATA\">\n {{expiresIn(element.options.expires)}}\n </ng-container>\n <ng-template #NO_DATA>\n \u221E\n </ng-template>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"option\">\n <th mat-header-cell *matHeaderCellDef></th>\n <td mat-cell *matCellDef=\"let element\" style=\"padding-right: 0;\">\n <div style=\"display: flex;justify-content: flex-end;\">\n <button mat-icon-button color=\"warn\" (click)=\"onDelete(element); $event.stopPropagation()\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\" (click)=\"onSelectedRow(row)\"></tr>\n </table>\n </ng-container>\n\n <ng-template #NO_DATA>\n <h3 style=\"margin-top: 1rem;\">No Data</h3>\n </ng-template>\n\n <div *ngIf=\"(data$ | async) as data\" style=\"margin-top: 2rem;\">\n <div style=\"margin-bottom: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <h3>STORE: <span style=\"font-weight: bold;\">{{ store.name | uppercase }}</span></h3>\n <h3>SETTINGS: {{ setting$(store.name) | async | json }}</h3>\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex: 1;\">\n <mat-label>Data (Must be an Object or Array of any)</mat-label>\n <textarea matInput placeholder=\"[]\" #json [value]=\"data\"></textarea>\n </mat-form-field>\n </div>\n <mat-error *ngIf=\"!isValidJSON(json.value)\">Not Valid Data</mat-error>\n\n <div style=\"display: flex; gap: .5rem;\">\n <span style=\"flex:1\"></span>\n <button mat-stroked-button (click)=\"onCancel()\">Cancel</button>\n <button mat-stroked-button (click)=\"onUpdate(store, json.value)\" [disabled]=\"!(isValidJSON(json.value))\">Update</button>\n </div>\n </div>\n </div>\n\n </ng-template>\n\n <div style=\"margin-top: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <button mat-stroked-button (click)=\"onReset()\">Delete All Stores</button>\n </div>\n\n</div>\n\n\n<mat-menu #menu=\"matMenu\">\n <div style=\"padding: 1rem;\">\n <p>Please note that the LocalStorage (encryption) and management of data is dependant on initializing the APP_ID</p>\n <p>Must provide a value for <b>APP_ID</b> in AppModule->Providers</p>\n <mat-divider></mat-divider>\n <div style=\"font-size: smaller; margin-top: .5rem;\">\n <p>Example: UUID (self.crypto.randomUUID() to generate)</p>\n <div style=\"background-color: whitesmoke; padding: 1rem;\">\n &#123;<br>\n provide: APP_ID,<br>\n useValue: \"056991ac-3537-43ab-b5b9-83edf6554eff\",<br>\n &#125;\n </div>\n </div>\n </div>\n</mat-menu>\n", styles: [".mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor}.demo-row-is-clicked{font-weight:700}.mat-mdc-menu-panel.mat-mdc-menu-panel{max-width:280px!important}\n"] }]
3502
+ }], ctorParameters: () => [{ type: ConfigOptions, decorators: [{
3503
+ type: Inject,
3504
+ args: [CONFIG_SETTINGS_TOKEN]
3505
+ }] }] });
3506
+
3507
+ const PROXY_CONFIG = new InjectionToken('PROXY_CONFIG');
3508
+ class ProxyDebuggerInterceptor {
3509
+ constructor(proxyConfig) {
3510
+ this.proxyConfig = proxyConfig;
2417
3511
  }
2418
- updateClient(data) {
2419
- // const headers = {
2420
- // auth: "sample-auth-token"
2421
- // }
2422
- data.id = 1031;
2423
- const sampleOptions = RequestOptions.adapt({ path: [data.id] });
2424
- this.updateRecord(data, sampleOptions);
3512
+ intercept(req, next) {
3513
+ if (!this.proxyConfig) {
3514
+ return next.handle(req);
3515
+ }
3516
+ const headers = req.headers.keys().reduce((acc, key) => {
3517
+ acc[key] = req.headers.get(key) || '';
3518
+ return acc;
3519
+ }, {});
3520
+ for (const proxyPath in this.proxyConfig) {
3521
+ if (this.proxyConfig.hasOwnProperty(proxyPath)) {
3522
+ const proxyDetails = this.proxyConfig[proxyPath];
3523
+ const regex = new RegExp('^' + proxyPath.replace('/', '').replace('*', '(.*)'));
3524
+ if (regex.test(req.url)) {
3525
+ const target = proxyDetails.target;
3526
+ const endpoint = req.url.replace(regex, '$1');
3527
+ const actualPath = target + '/' + endpoint;
3528
+ console.log('Request Proxied:', {
3529
+ requestUrl: req.url,
3530
+ requestPayload: req.body,
3531
+ headers: headers,
3532
+ proxyPath: proxyPath,
3533
+ actualPath: actualPath,
3534
+ });
3535
+ }
3536
+ }
3537
+ }
3538
+ return next.handle(req);
2425
3539
  }
2426
- deleteClient(data) {
2427
- // const headers = {
2428
- // auth: "sample-auth-token"
2429
- // }
2430
- data.id = 1031;
2431
- const sampleOptions = RequestOptions.adapt({ path: [data.id] });
2432
- this.deleteRecord(sampleOptions);
3540
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ProxyDebuggerInterceptor, deps: [{ token: PROXY_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
3541
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ProxyDebuggerInterceptor }); }
3542
+ }
3543
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ProxyDebuggerInterceptor, decorators: [{
3544
+ type: Injectable
3545
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
3546
+ type: Optional
3547
+ }, {
3548
+ type: Inject,
3549
+ args: [PROXY_CONFIG]
3550
+ }] }] });
3551
+
3552
+ class ClientInfo {
3553
+ constructor(domain = '', service = '', id = 0, name = '') {
3554
+ this.domain = domain;
3555
+ this.service = service;
3556
+ this.id = id;
3557
+ this.name = name;
2433
3558
  }
2434
- streamRequest() {
2435
- const headers = {
2436
- auth: "sample-auth-token"
2437
- };
2438
- // const sampleOptions = RequestOptions.adapt({ path: [1], headers })
2439
- this.fetchStream();
3559
+ static adapt(item) {
3560
+ return new ClientInfo(item?.domain, item?.service, item?.id, (item?.first_name || item?.last_name) ? `${item?.first_name} ${item?.last_name}` : '');
2440
3561
  }
2441
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: StateManagerDemoService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2442
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: StateManagerDemoService, providedIn: 'root' }); }
2443
3562
  }
2444
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: StateManagerDemoService, decorators: [{
2445
- type: Injectable,
2446
- args: [{
2447
- providedIn: 'root'
2448
- }]
2449
- }], ctorParameters: () => [] });
2450
3563
 
2451
- class RequestManagerStateDemoComponent {
2452
- get dataObservable$() {
2453
- switch (this.requestType) {
2454
- case 'GET':
2455
- return this.GET$;
2456
- case 'PUT':
2457
- return this.PUT$;
2458
- case 'POST':
2459
- return this.POST$;
2460
- case 'DELETE':
2461
- return this.DELETE$;
2462
- case 'STREAM':
2463
- return this.STREAM;
2464
- case 'STREAM_AI':
2465
- return this.STREAM_AI;
2466
- default:
2467
- return this.GET$;
2468
- break;
2469
- }
3564
+ class ClientInfoMapper {
3565
+ constructor(id = 0, first_name = '', last_name = '', email = '') {
3566
+ this.id = id;
3567
+ this.first_name = first_name;
3568
+ this.last_name = last_name;
3569
+ this.email = email;
2470
3570
  }
2471
- get hasChanged() {
2472
- return this.requestForm.dirty || this.requestForm.untouched;
3571
+ static adapt(item) {
3572
+ const first_name = (item?.name) ? item.name.split(' ')[0] : '';
3573
+ const last_name = (item?.name) ? item.name.split(' ')[1] : '';
3574
+ return new ClientInfoMapper(item?.id, first_name, last_name, item?.email);
2473
3575
  }
2474
- get dataType() {
2475
- return this.requestForm.get("datatype")?.value;
3576
+ }
3577
+
3578
+ class AIPrompt {
3579
+ constructor(response = '') {
3580
+ this.response = response;
2476
3581
  }
2477
- get database() {
2478
- return this.requestForm.get("database")?.value;
3582
+ static adapt(item) {
3583
+ return new AIPrompt(item?.response);
2479
3584
  }
3585
+ }
3586
+
3587
+ class RequestSignalsManagerDemoComponent {
2480
3588
  get retry() {
2481
- return this.requestForm.get("retry")?.value;
3589
+ return this.requestForm.get('retry')?.value;
2482
3590
  }
2483
3591
  get headers() {
2484
3592
  return this.requestForm.get('headers');
@@ -2488,32 +3596,42 @@ class RequestManagerStateDemoComponent {
2488
3596
  return this.requestForm.valid;
2489
3597
  }
2490
3598
  constructor() {
2491
- this.stateManagerDemoService = inject(StateManagerDemoService);
2492
3599
  this.displayedColumns = ['id', 'name', 'lastName', 'age'];
2493
3600
  this.fb = inject(FormBuilder);
2494
- this.httpManagerService = inject(HTTPManagerService);
2495
- this.isPending$ = this.stateManagerDemoService.isPending$;
2496
- this.error$ = this.stateManagerDemoService.error$;
2497
- this.countdown$ = this.httpManagerService.countdown$;
2498
- this.GET_error$ = new BehaviorSubject('');
2499
- this.POST_error$ = new BehaviorSubject('');
2500
- this.PUT_error$ = new BehaviorSubject('');
2501
- this.DELETE_error$ = new BehaviorSubject('');
2502
- this.STREAM_error$ = new BehaviorSubject('');
2503
- this.STREAM_AI_error$ = new BehaviorSubject('');
2504
- this.GET$ = new BehaviorSubject(null);
2505
- this.POST$ = new BehaviorSubject(null);
2506
- this.PUT$ = new BehaviorSubject(null);
2507
- this.DELETE$ = new BehaviorSubject(null);
2508
- this.STREAM = new BehaviorSubject(null);
2509
- this.STREAM$ = this.STREAM.asObservable();
2510
- this.STREAM_AI = new BehaviorSubject([]);
2511
- this.STREAM_AI$ = this.STREAM_AI.asObservable()
2512
- .pipe(map$1((items) => (items) ? items.map((item) => item.response) : []), map$1((items) => items.join('\n').trim()));
3601
+ this.toastMessage = inject(ToastMessageDisplayService);
3602
+ this.httpManagerSignalsService = inject(HTTPManagerSignalsService);
3603
+ // Using signals service: signals are callable in templates (e.g. isPending())
3604
+ this.isPending = this.httpManagerSignalsService.isPending;
3605
+ this.countdown = this.httpManagerSignalsService.countdown;
3606
+ // per-operation plain results and error messages (no rxjs in component)
3607
+ this.GET_result = null;
3608
+ this.POST_result = null;
3609
+ this.PUT_result = null;
3610
+ this.DELETE_result = null;
3611
+ this.STREAM_result = null;
3612
+ this.STREAM_AI_result = null;
3613
+ this.GET_error = '';
3614
+ this.POST_error = '';
3615
+ this.PUT_error = '';
3616
+ this.DELETE_error = '';
3617
+ this.STREAM_error = '';
3618
+ this.STREAM_AI_error = '';
3619
+ this.requestParams = {
3620
+ GET: ApiRequest.adapt(),
3621
+ POST: ApiRequest.adapt(),
3622
+ PUT: ApiRequest.adapt(),
3623
+ DELETE: ApiRequest.adapt(),
3624
+ STREAM: ApiRequest.adapt(),
3625
+ };
2513
3626
  this.questionControl = this.fb.control("", [Validators.required]);
2514
- this.requestType = '';
2515
- this.prompts = [];
2516
- this.AIType = 0;
3627
+ this.downloadRequest = ApiRequest.adapt({
3628
+ server: 'assets/images',
3629
+ path: ['me.jpg'],
3630
+ // saveAs: 'john.jpg' // Optional
3631
+ });
3632
+ // downloadRequest = ApiRequest.adapt({
3633
+ // server: 'oidc/ai/file'
3634
+ // })
2517
3635
  this.sampleClientData = {
2518
3636
  id: 0,
2519
3637
  name: "Old School Dates",
@@ -2525,10 +3643,9 @@ class RequestManagerStateDemoComponent {
2525
3643
  modified: 1693003138,
2526
3644
  icon: "",
2527
3645
  imageFile: "",
3646
+ email: "wavecoders@gmail.com"
2528
3647
  };
2529
- this.selectedRecord = this.fb.control(null);
2530
3648
  this.requestForm = this.fb.group({
2531
- datatype: this.fb.control('ARRAY'),
2532
3649
  path: this.fb.control("ai/"),
2533
3650
  headers: this.fb.array([]),
2534
3651
  adapter: [null],
@@ -2538,64 +3655,49 @@ class RequestManagerStateDemoComponent {
2538
3655
  delay: [3],
2539
3656
  }),
2540
3657
  polling: [3],
2541
- database: this.fb.group({
2542
- table: [''],
2543
- expiresIn: ['1m'],
2544
- })
2545
3658
  });
3659
+ this.AIType = 0;
2546
3660
  this.sampleAdaptors = [
2547
3661
  { label: "ClientInfo Basic", value: ClientInfo.adapt },
2548
3662
  { label: "AI Prompt", value: AIPrompt.adapt },
2549
- ];
2550
- this.sampleMappers = [
2551
- { label: "Mapper Basic", value: ClientInfoMapper.adapt },
2552
- { label: "AI Prompt", value: AIPrompt.adapt },
2553
- ];
2554
- // server = `http://sample-endpoint/as/authorization.oauth2`
2555
- this.arrayObjectsToObjects = (arr) => {
2556
- return Array.isArray(arr) ? arr.reduce((obj, item) => Object.assign(obj, { [item.key]: item.value }), {}) : {};
2557
- };
2558
- this.props = (adapter) => {
2559
- return (adapter) ? adapter() : null;
2560
- };
2561
- }
2562
- ngOnInit() {
2563
- this.stateManagerDemoService.data$.pipe(tap$1((data) => console.log("API STREAM_AI response", data)));
2564
- this.error$.pipe(tap$1((data) => {
2565
- debugger;
2566
- console.log("API STREAM response", data);
2567
- }), catchError$1(error => {
2568
- return throwError(() => this.errorHandling(error, 'STREAM'));
2569
- }));
2570
- this.selectedRecord.valueChanges
2571
- .subscribe((data) => {
2572
- this.selectedRecord$ = (data) ? this.stateManagerDemoService.selectRecord$(data.id) : EMPTY;
2573
- });
2574
- this.stateManagerDemoService.data$
2575
- .pipe(tap$1((data) => {
2576
- switch (this.requestType) {
2577
- case 'GET':
2578
- this.GET$.next(data);
2579
- break;
2580
- case 'PUT':
2581
- this.PUT$.next(data);
2582
- break;
2583
- case 'POST':
2584
- this.POST$.next(data);
2585
- break;
2586
- case 'DELETE':
2587
- this.DELETE$.next(data);
2588
- break;
2589
- case 'STREAM':
2590
- this.STREAM.next(data);
2591
- break;
2592
- case 'STREAM_AI':
2593
- this.STREAM_AI.next(data);
2594
- break;
2595
- default:
2596
- break;
2597
- }
2598
- })).subscribe();
3663
+ ];
3664
+ this.sampleMappers = [
3665
+ { label: "Mapper Basic", value: ClientInfoMapper.adapt },
3666
+ { label: "AI Prompt", value: AIPrompt.adapt },
3667
+ ];
3668
+ this.hasId = (arr) => {
3669
+ if (arr.length === 0)
3670
+ return false;
3671
+ return !isNaN(arr[arr.length - 1]);
3672
+ };
3673
+ this.props = (adapter) => {
3674
+ return (adapter) ? adapter() : null;
3675
+ };
3676
+ // server = `http://sample-endpoint/as/authorization.oauth2`
3677
+ this.arrayObjectsToObjects = (arr) => {
3678
+ return Array.isArray(arr) ? arr.reduce((obj, item) => Object.assign(obj, { [item.key]: item.value }), {}) : {};
3679
+ };
3680
+ }
3681
+ ngOnInit() {
3682
+ // const reqGet2 = ApiRequest.adapt({
3683
+ // server,
3684
+ // path: ['clients'],
3685
+ // headers: { authentication: "Bearer <KEY>" },
3686
+ // adapter: ClientInfo,
3687
+ // dataType: DataType.OBJECT,
3688
+ // // concurrent: false,
3689
+ // // polling: 3, //seconds
3690
+ // })
3691
+ // const req2 = [1024,1025,1026].map(item => {
3692
+ // return this.httpManagerService.getRequest<ClientInfo[]>(reqGet2, [item])
3693
+ // .pipe(
3694
+ // catchError(error => {
3695
+ // return throwError(() => new Error(error.error.message))
3696
+ // })
3697
+ // )
3698
+ // })
3699
+ // forkJoin(req2)
3700
+ // .subscribe(res => console.log(res))
2599
3701
  }
2600
3702
  addHeader() {
2601
3703
  const header = this.fb.group({
@@ -2616,77 +3718,178 @@ class RequestManagerStateDemoComponent {
2616
3718
  if (!this.failedState.checked) {
2617
3719
  requestParams.retry = { times: 0, delay: 0 };
2618
3720
  }
2619
- const currentOptions = ApiRequest.adapt(requestParams);
2620
- currentOptions.path = [];
2621
- const apiOptions = ApiRequest.adapt({ ...currentOptions, path: pathReq });
3721
+ const apiOptions = ApiRequest.adapt(requestParams);
3722
+ apiOptions.path = [];
2622
3723
  return { apiOptions: apiOptions, path: pathReq };
2623
3724
  }
2624
- onSetStateOptions() {
3725
+ onGetRequest() {
2625
3726
  if (!this.isValid)
2626
3727
  return;
2627
3728
  const reqParams = this.compileRequest();
2628
- const db = DatabaseStorage.adapt(this.database);
2629
- const type = this.dataType === "ARRAY" ? DataType.ARRAY : DataType.OBJECT;
2630
- this.stateManagerDemoService.setAPIOptions(reqParams.apiOptions, type, db);
2631
- this.requestForm.markAsPristine();
2632
- }
2633
- onClearRecords() {
2634
- this.stateManagerDemoService.clearRecords();
2635
- }
2636
- onRefreshRecords() {
2637
- this.stateManagerDemoService.refreshData();
2638
- }
2639
- onGetRequest() {
2640
- this.requestType = 'GET';
2641
- this.stateManagerDemoService.getClients();
3729
+ this.requestParams.GET = reqParams.apiOptions;
3730
+ // reset local state
3731
+ this.GET_result = null;
3732
+ this.GET_error = '';
3733
+ this.httpManagerSignalsService.getRequest(reqParams.apiOptions, reqParams.path)
3734
+ .subscribe({
3735
+ next: (res) => this.GET_result = res,
3736
+ error: (err) => this.errorHandling(err, 'GET')
3737
+ });
2642
3738
  }
2643
3739
  onCreateRequest() {
2644
- this.requestType = 'POST';
2645
- this.stateManagerDemoService.createClient(this.sampleClientData);
3740
+ if (!this.isValid)
3741
+ return;
3742
+ const reqParams = this.compileRequest();
3743
+ this.requestParams.POST = reqParams.apiOptions;
3744
+ // reset local state
3745
+ this.POST_result = null;
3746
+ this.POST_error = '';
3747
+ this.httpManagerSignalsService.postRequest(this.sampleClientData, reqParams.apiOptions, reqParams.path)
3748
+ .subscribe({
3749
+ next: (res) => this.POST_result = res,
3750
+ error: (err) => this.errorHandling(err, 'POST')
3751
+ });
2646
3752
  }
2647
3753
  onUpdateRequest() {
2648
- this.requestType = 'PUT';
2649
- this.stateManagerDemoService.updateClient(this.sampleClientData);
3754
+ if (!this.isValid)
3755
+ return;
3756
+ const reqParams = this.compileRequest();
3757
+ if (!this.hasId(reqParams.path)) {
3758
+ console.log("Missing ID");
3759
+ return;
3760
+ }
3761
+ this.sampleClientData.id = parseInt(reqParams.path[reqParams.path.length - 1]);
3762
+ this.requestParams.PUT = reqParams.apiOptions;
3763
+ // reset local state
3764
+ this.PUT_result = null;
3765
+ this.PUT_error = '';
3766
+ this.httpManagerSignalsService.putRequest(this.sampleClientData, reqParams.apiOptions, reqParams.path)
3767
+ .subscribe({
3768
+ next: (res) => this.PUT_result = res,
3769
+ error: (err) => this.errorHandling(err, 'PUT')
3770
+ });
2650
3771
  }
2651
3772
  onDeleteRequest() {
2652
- this.requestType = 'DELETE';
2653
- this.stateManagerDemoService.deleteClient(this.sampleClientData);
3773
+ if (!this.isValid)
3774
+ return;
3775
+ const reqParams = this.compileRequest();
3776
+ this.requestParams.DELETE = reqParams.apiOptions;
3777
+ if (!this.hasId(reqParams.path)) {
3778
+ console.log("Missing ID");
3779
+ return;
3780
+ }
3781
+ this.sampleClientData.id = parseInt(reqParams.path[reqParams.path.length - 1]);
3782
+ this.requestParams.DELETE = reqParams.apiOptions;
3783
+ // reset local state
3784
+ this.DELETE_result = null;
3785
+ this.DELETE_error = '';
3786
+ this.httpManagerSignalsService.deleteRequest(reqParams.apiOptions, reqParams.path)
3787
+ .subscribe({
3788
+ next: (res) => this.DELETE_result = res,
3789
+ error: (err) => this.errorHandling(err, 'DELETE')
3790
+ });
3791
+ }
3792
+ onStreamPostRequest() {
3793
+ if (!this.isValid)
3794
+ return;
3795
+ const reqParams = this.compileRequest();
3796
+ let payload = {};
3797
+ let apiPath = reqParams.path;
3798
+ let apiOptions = reqParams.apiOptions;
3799
+ let responseMapper = (items) => items.response;
3800
+ if (this.AIType === 0) {
3801
+ // API request
3802
+ payload = { prompt: this.questionControl.value };
3803
+ }
3804
+ else {
3805
+ // Local Ollama request
3806
+ apiOptions.server = "api";
3807
+ apiPath = ["generate"];
3808
+ apiOptions.stream = true;
3809
+ payload = {
3810
+ model: "phi3:latest",
3811
+ prompt: this.questionControl.value,
3812
+ stream: true,
3813
+ };
3814
+ responseMapper = (items) => items.map((word) => word.response).flat().join('');
3815
+ }
3816
+ this.requestParams.STREAM = apiOptions;
3817
+ this.STREAM_AI_result = null;
3818
+ this.STREAM_AI_error = '';
3819
+ this.httpManagerSignalsService.postRequest(payload, apiOptions, apiPath)
3820
+ .subscribe({
3821
+ next: (res) => {
3822
+ try {
3823
+ this.STREAM_AI_result = responseMapper(res);
3824
+ }
3825
+ catch (e) {
3826
+ this.STREAM_AI_result = res;
3827
+ }
3828
+ this.questionControl.reset();
3829
+ },
3830
+ error: (err) => this.errorHandling(err, 'STREAM')
3831
+ });
2654
3832
  }
2655
3833
  onStreamRequest() {
2656
3834
  if (!this.isValid)
2657
3835
  return;
2658
3836
  const reqParams = this.compileRequest();
2659
3837
  reqParams.apiOptions.stream = true;
2660
- this.requestType = 'STREAM';
2661
- this.stateManagerDemoService.streamRequest();
3838
+ this.requestParams.GET = reqParams.apiOptions;
3839
+ this.STREAM_result = null;
3840
+ this.STREAM_error = '';
3841
+ this.httpManagerSignalsService.getRequest(reqParams.apiOptions, reqParams.path)
3842
+ .subscribe({
3843
+ next: (res) => this.STREAM_result = res,
3844
+ error: (err) => this.errorHandling(err, 'STREAM')
3845
+ });
3846
+ }
3847
+ onDownloadCompleted() {
3848
+ const message = "Download Completed";
3849
+ const display = ToastDisplay.adapt({
3850
+ message,
3851
+ action: 'Ok',
3852
+ color: ToastColors.SUCCESS,
3853
+ icon: 'sentiment_satisfied_alt',
3854
+ });
3855
+ this.toastMessage.toastMessage(display);
3856
+ }
3857
+ onDownloadFailed(err) {
3858
+ const message = "Download Failed";
3859
+ const display = ToastDisplay.adapt({
3860
+ message,
3861
+ action: 'Ok',
3862
+ color: ToastColors.ERROR,
3863
+ icon: 'warning',
3864
+ });
3865
+ this.toastMessage.toastMessage(display);
2662
3866
  }
2663
3867
  errorHandling(err, type) {
2664
- console.log(err, type);
3868
+ const message = err?.message || String(err);
3869
+ // set local error state
2665
3870
  if (type === 'GET')
2666
- this.GET_error$.next(err.message);
3871
+ this.GET_error = message;
2667
3872
  if (type === 'POST')
2668
- this.POST_error$.next(err.message);
3873
+ this.POST_error = message;
2669
3874
  if (type === 'PUT')
2670
- this.PUT_error$.next(err.message);
3875
+ this.PUT_error = message;
2671
3876
  if (type === 'DELETE')
2672
- this.DELETE_error$.next(err.message);
3877
+ this.DELETE_error = message;
2673
3878
  if (type === 'STREAM')
2674
- this.STREAM_error$.next(err.message);
2675
- if (type === 'STREAM_AI')
2676
- this.STREAM_AI_error$.next(err.message);
3879
+ this.STREAM_error = message;
3880
+ // also set the shared service error signal
3881
+ this.httpManagerSignalsService.error.set(message);
3882
+ return err;
2677
3883
  }
2678
3884
  onSelectAIType(type) {
2679
3885
  this.AIType = type;
2680
3886
  }
2681
- onClearHistory() {
2682
- this.prompts = [];
2683
- }
2684
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RequestManagerStateDemoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2685
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: RequestManagerStateDemoComponent, selector: "app-request-manager-state-demo", viewQueries: [{ propertyName: "failedState", first: true, predicate: ["failedState"], descendants: true, static: true }, { propertyName: "pollingState", first: true, predicate: ["pollingState"], descendants: true, static: true }], ngImport: i0, template: "<div style=\"margin: 2rem;\">\n\n <h2>\n HTTP Request State Manager\n </h2>\n\n <div [formGroup]=\"requestForm\" style=\"margin-top: 2rem;\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>State Data Type</mat-label>\n <mat-select formControlName=\"datatype\">\n <mat-option value=\"ARRAY\">Array</mat-option>\n <mat-option value=\"OBJECT\">Object</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Adapter (Model)</mat-label>\n <mat-select formControlName=\"adapter\" #adapterSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let adapter of sampleAdaptors\" [value]=\"adapter.value\">\n {{adapter.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Mapper (Model)</mat-label>\n <mat-select formControlName=\"mapper\" #mapperSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let mapper of sampleMappers\" [value]=\"mapper.value\">\n {{mapper.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <div style=\"display: flex; margin-bottom: 2rem;\" *ngIf=\"adapterSelect.value || mapperSelect.value\">\n <div style=\"flex:1\" class=\"box\">\n <h3>Adapter (Incoming)</h3>\n <div *ngIf=\"adapterSelect.value; else NO_ADAPTER\">\n {{ props(adapterSelect.value) | json }}\n </div>\n <ng-template #NO_ADAPTER>No Transformation</ng-template>\n </div>\n <div style=\"flex:1\" class=\"box\">\n <h3>Mapper (Outgoing)</h3>\n <div *ngIf=\"mapperSelect.value; else NO_MAPPER\">\n {{ props(mapperSelect.value) | json }}\n </div>\n <ng-template #NO_MAPPER>No Transformation</ng-template>\n </div>\n </div>\n\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>RestPath (/ delimited)</mat-label>\n <input matInput placeholder=\"clients/list\" formControlName=\"path\">\n </mat-form-field>\n </div>\n <div>\n <div formArrayName=\"headers\">\n <div *ngFor=\"let task of headers.controls; let i = index\" [formGroupName]=\"i\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Key</mat-label>\n <input matInput placeholder=\"authentication\" formControlName=\"key\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Value</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"value\">\n </mat-form-field>\n <div style=\"margin-top: .5rem;\">\n <button mat-icon-button (click)=\"removeHeader(i)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n </div>\n </div>\n <button mat-stroked-button (click)=\"addHeader()\">Add Header</button>\n </div>\n <div style=\"margin-top: 2rem; display: flex; flex-direction:column; gap: 1rem;\">\n <div>\n <mat-slide-toggle #failedState>Retry on Failed</mat-slide-toggle>\n <div *ngIf=\"failedState.checked\" style=\"display: flex; gap: .5rem; margin-top: 1rem;\" formGroupName=\"retry\">\n <mat-form-field appearance=\"outline\">\n <mat-label>#of Times</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"times\" value=\"3\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Delay Until Next</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"delay\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n <div>\n <mat-slide-toggle #pollingState>Polling</mat-slide-toggle>\n <div *ngIf=\"pollingState.checked\">\n <mat-form-field appearance=\"outline\" style=\"margin-top: 1rem\">\n <mat-label>#of Seconds</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"polling\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n <div>\n <mat-slide-toggle #DBState>Database Storage</mat-slide-toggle>\n <div *ngIf=\"DBState.checked\" style=\"display: flex; gap: .5rem; margin-top: 1rem\" formGroupName=\"database\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Table Name</mat-label>\n <input matInput placeholder=\"table\" formControlName=\"table\" value=\"\">\n </mat-form-field>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Expires In</mat-label>\n <mat-select formControlName=\"expiresIn\">\n <mat-option value=\"1m\">One Minute</mat-option>\n <mat-option value=\"1h\">One Hour</mat-option>\n <mat-option value=\"1d\">One Day</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n </div>\n <div style=\"margin-top: 1rem; display: flex;\">\n <span style=\"flex:1\"></span>\n <button mat-stroked-button (click)=\"onSetStateOptions()\" [disabled]=\"!hasChanged\">\n Set API Request Options\n </button>\n </div>\n <div>\n <mat-error *ngIf=\"(error$ | async) as error\">\n {{ error }}\n </mat-error>\n </div>\n </div>\n </div>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n <mat-progress-bar mode=\"indeterminate\"\n *ngIf=\"(isPending$ | async)\"\n ></mat-progress-bar>\n <div *ngIf=\"pollingState.checked\">\n <mat-progress-bar mode=\"determinate\"\n *ngIf=\"!(isPending$ | async) && (this.countdown$ | async) || -1 > 0\"\n [value]=\"(this.countdown$ | async)\"\n ></mat-progress-bar>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\" *ngIf=\"(GET$ | async) as data\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1;\">CURRENT STATE ({{ dataType }})</h2>\n </div>\n <div *ngIf=\"data.length > 1\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Records</mat-label>\n <mat-select [formControl]=\"selectedRecord\" [disabled]=\"data === null && data?.length === 0\">\n <mat-option [value]=\"\">None</mat-option>\n <mat-option *ngFor=\"let item of data\" [value]=\"item\">\n {{item.name || (item.first_name) | titlecase}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div *ngIf=\"(dataObservable$ | async) as dataRecord\">\n <div *ngIf=\"(selectedRecord$ | async) as record; else NO_RECORD\">\n {{ record | json }}\n </div>\n <ng-template #NO_RECORD>No Record Selected from State</ng-template>\n\n </div>\n <div style=\"margin-top: 1rem;\">\n <span *ngIf=\"data !== null && data?.length > 0; else NO_DATA\">\n State contains a Total of {{ data.length }} Records\n </span>\n <ng-template #NO_DATA>No Records</ng-template>\n <div style=\"display: flex; gap: .5rem; text-align: end; justify-content: flex-end;\">\n <button class=\"btn\" mat-stroked-button (click)=\"onClearRecords()\">Clear</button>\n <button class=\"btn\" mat-stroked-button (click)=\"onRefreshRecords()\" [disabled]=\"hasChanged\">Refresh</button>\n </div>\n </div>\n </div>\n\n\n\n\n <div style=\"margin-top: 1rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">GET Request ({{ dataType }})</h2>\n <div>\n <button mat-raised-button (click)=\"onGetRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(GET_error$ | async) as get_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ get_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(GET$ | async) | jsonv\"></div> -->\n <div *ngIf=\"(GET$ | async) as getData\">{{ getData | json }}</div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">POST Request</h2>\n <div>\n <button mat-raised-button (click)=\"onCreateRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(POST_error$ | async) as post_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ post_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(POST$ | async) | jsonv\"></div> -->\n <div *ngIf=\"(POST$ | async) as postData\">{{ postData | json }}</div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">PUT Request</h2>\n <div>\n <button mat-raised-button (click)=\"onUpdateRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(PUT_error$ | async) as put_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ put_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(PUT$ | async) | jsonv\"></div> -->\n <div *ngIf=\"(PUT$ | async) as putData\">{{ putData | json }}</div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">DELETE Request</h2>\n <div>\n <button mat-raised-button (click)=\"onDeleteRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(DELETE_error$ | async) as delete_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ delete_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(DELETE$ | async) | jsonv\"></div> -->\n <div *ngIf=\"(DELETE$ | async) as deleteData\">{{ deleteData | json }}</div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">Streaming GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\" [disabled]=\"hasChanged\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(STREAM_error$ | async) as stream_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <div *ngIf=\"(STREAM$ | async) as data\" class=\"container\">\n <table mat-table [dataSource]=\"data\">\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> First Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.name}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"lastName\">\n <th mat-header-cell *matHeaderCellDef> Last Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.lastName}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"age\">\n <th mat-header-cell *matHeaderCellDef> Age </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.age}} </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n </div>\n\n </div>\n\n</div>\n", styles: [".btn{min-width:120px}.mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor;background-color:#f5f5f5}.container{height:400px;overflow:auto}.box{padding:10px;border:1px solid #ccc}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { 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.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i2.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i2.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "component", type: i3$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i9.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i9.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i9.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i9.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i9.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i9.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i9.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i9.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i9.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i10.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "component", type: i11.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "component", type: i12.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "directive", type: i13.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }, { kind: "pipe", type: i1$1.TitleCasePipe, name: "titlecase" }] }); }
3887
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RequestSignalsManagerDemoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3888
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: RequestSignalsManagerDemoComponent, selector: "app-request-signals-manager-demo", viewQueries: [{ propertyName: "failedState", first: true, predicate: ["failedState"], descendants: true, static: true }, { propertyName: "pollingState", first: true, predicate: ["pollingState"], descendants: true, static: true }], ngImport: i0, template: "<div style=\"margin: 2rem;\">\n\n <h2>\n <span>HTTP Request Signals Manager</span>\n <span style=\"margin-left: .5rem;\">\n <mat-icon color=\"accent\">fiber_new</mat-icon>\n </span>\n </h2>\n\n <div [formGroup]=\"requestForm\" style=\"margin-top: 2rem;\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Adapter (Model)</mat-label>\n <mat-select formControlName=\"adapter\" #adapterSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let adapter of sampleAdaptors\" [value]=\"adapter.value\">\n {{adapter.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Mapper (Model)</mat-label>\n <mat-select formControlName=\"mapper\" #mapperSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let mapper of sampleMappers\" [value]=\"mapper.value\">\n {{mapper.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"display: flex; margin-bottom: 2rem;\" *ngIf=\"adapterSelect.value || mapperSelect.value\">\n <div style=\"flex:1\" class=\"box\">\n <h3>Adapter (Incoming)</h3>\n <div *ngIf=\"adapterSelect.value; else NO_ADAPTER\">\n {{ props(adapterSelect.value) | json }}\n </div>\n <ng-template #NO_ADAPTER>No Transformation</ng-template>\n </div>\n <div style=\"flex:1\" class=\"box\">\n <h3>Mapper (Outgoing)</h3>\n <div *ngIf=\"mapperSelect.value; else NO_MAPPER\">\n {{ props(mapperSelect.value) | json }}\n </div>\n <ng-template #NO_MAPPER>No Transformation</ng-template>\n </div>\n </div>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>RestPath (/ delimited)</mat-label>\n <input matInput placeholder=\"clients/list\" formControlName=\"path\">\n </mat-form-field>\n </div>\n <div>\n <div formArrayName=\"headers\">\n <div *ngFor=\"let task of headers.controls; let i = index\" [formGroupName]=\"i\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Key</mat-label>\n <input matInput placeholder=\"authentication\" formControlName=\"key\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Value</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"value\">\n </mat-form-field>\n <div style=\"margin-top: .5rem;\">\n <button mat-icon-button (click)=\"removeHeader(i)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n </div>\n </div>\n <button mat-stroked-button (click)=\"addHeader()\" class=\"btn\">Add Header</button>\n </div>\n <div style=\"margin-top: 2rem; display: flex; flex-direction:column; gap: 1rem;\">\n <div>\n <mat-slide-toggle #failedState>Retry on Failed</mat-slide-toggle>\n <div *ngIf=\"failedState.checked\" style=\"display: flex; gap: .5rem; margin-top: 1rem;\" formGroupName=\"retry\">\n <mat-form-field appearance=\"outline\">\n <mat-label>#of Times</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"times\" value=\"3\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Delay Until Next</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"delay\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n <div>\n <mat-slide-toggle #pollingState>Polling</mat-slide-toggle>\n <div *ngIf=\"pollingState.checked\">\n <mat-form-field appearance=\"outline\" style=\"margin-top: 1rem\">\n <mat-label>#of Seconds</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"polling\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n </div>\n </div>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n <mat-progress-bar mode=\"indeterminate\"\n *ngIf=\"isPending()\"\n ></mat-progress-bar>\n <mat-progress-bar mode=\"determinate\"\n *ngIf=\"pollingState.checked && !isPending()\"\n [value]=\"countdown()\"\n ></mat-progress-bar>\n </div>\n\n <div style=\"margin-top: 1rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onGetRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"GET_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ GET_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"GET_result\">\n {{ GET_result | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">POST Request</h2>\n <div>\n <button mat-raised-button (click)=\"onCreateRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"POST_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ POST_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"POST_result\">\n {{ POST_result | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">PUT Request</h2>\n <div>\n <button mat-raised-button (click)=\"onUpdateRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"PUT_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ PUT_error }}</mat-error>\n </div>\n\n <h3>Include Record ID in the RestPath</h3>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"PUT_result\">\n {{ PUT_result | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">DELETE Request</h2>\n <div>\n <button mat-raised-button (click)=\"onDeleteRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <h3>Include Record ID in the RestPath</h3>\n\n <div *ngIf=\"DELETE_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ DELETE_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"DELETE_result\">\n {{ DELETE_result | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">Streaming GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <!-- <div *ngIf=\"(STREAM_error$ | async) as stream_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div> -->\n\n <div style=\"margin-top: 1rem;\">\n <div *ngIf=\"STREAM_result as data\" class=\"container\">\n <table mat-table [dataSource]=\"data\">\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> First Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.name}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"lastName\">\n <th mat-header-cell *matHeaderCellDef> Last Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.lastName}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"age\">\n <th mat-header-cell *matHeaderCellDef> Age </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.age}} </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1; padding-top: .5rem;\">AI -<span *ngIf=\"AIType === 1\">STREAMING</span> POST Request</h2>\n <div style=\"display: flex; gap: 1rem;\">\n <button mat-raised-button [matMenuTriggerFor]=\"menu\" style=\"min-width: 120px;\">\n <mat-icon>lan</mat-icon>\n <span *ngIf=\"AIType === 0; else LOCAL\">Server</span>\n <ng-template #LOCAL>\n Local\n </ng-template>\n </button>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item (click)=\"onSelectAIType(0)\">Server</button>\n <button mat-menu-item (click)=\"onSelectAIType(1)\">Local</button>\n </mat-menu>\n <div>\n <button mat-raised-button (click)=\"onStreamPostRequest()\" class=\"btn\">Ask Me</button>\n </div>\n </div>\n </div>\n\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Ask me a Question</mat-label>\n <textarea matInput placeholder=\"Why is the sky blue?\" [formControl]=\"questionControl\"></textarea>\n </mat-form-field>\n </div>\n\n <div *ngIf=\"STREAM_AI_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ STREAM_AI_error }}</mat-error>\n </div>\n\n <div *ngIf=\"AIType === 1; else ALTERNATIVE\" style=\"color: red;\">\n You must have Ollama active and the 'phi3:latest' model to use this feature.\n </div>\n <ng-template #ALTERNATIVE>\n <span style=\"color: gray;\">\n Define the RestPath to the API endpoint that will handle the AI request.\n Use: 'ai/chat' for server\n </span>\n </ng-template>\n\n <div>\n <div *ngIf=\"STREAM_AI_result\" style=\"margin-top: 1rem; font-size: 1.2rem; border-radius: 1rem; border: black 1px solid; padding: 2rem;\">\n <p style=\"margin-bottom: .5rem; white-space:pre-wrap; line-height: 1.6rem;\">{{STREAM_AI_result}}</p>\n </div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 1.5rem; margin-bottom: 1rem; line-height: 1.5rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <div>\n <div style=\"display: flex;\">\n <h2 style=\"flex:1; margin-bottom: 0; padding-top: .5rem; display: flex;\">\n <div>\n Download File\n </div>\n <div style=\"flex:1; margin-left: 1rem;\">\n <mat-slide-toggle #disable>\n <span *ngIf=\"disable.checked; else DISABLE\">\n Enable\n </span>\n <ng-template #DISABLE>Disable</ng-template>\n </mat-slide-toggle>\n </div>\n </h2>\n <div>\n <app-file-downloader\n [disabled]=\"disable.checked\"\n [delayError]=\"3\"\n [apiRequest]=\"downloadRequest\"\n (completed)=\"onDownloadCompleted()\"\n (failed)=\"onDownloadFailed($event)\"\n ></app-file-downloader>\n </div>\n </div>\n </div>\n\n</div>\n", styles: [".btn{min-width:120px}.mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor;background-color:#f5f5f5}.container{height:400px;overflow:auto}.box{padding:10px;border:1px solid #ccc}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { 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.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i2.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i2.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "component", type: i3$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i7.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i7.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i7.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i9.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i9.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i9.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i9.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i9.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i9.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i9.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i9.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i9.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i10.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "component", type: i11.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "component", type: i12.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "directive", type: i13.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: FileDownloaderComponent, selector: "app-file-downloader", inputs: ["delayError", "apiRequest", "displayErrorMessage", "saveFileAs", "labels", "disabled"], outputs: ["completed", "failed"] }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }] }); }
2686
3889
  }
2687
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RequestManagerStateDemoComponent, decorators: [{
3890
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RequestSignalsManagerDemoComponent, decorators: [{
2688
3891
  type: Component,
2689
- args: [{ selector: 'app-request-manager-state-demo', template: "<div style=\"margin: 2rem;\">\n\n <h2>\n HTTP Request State Manager\n </h2>\n\n <div [formGroup]=\"requestForm\" style=\"margin-top: 2rem;\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>State Data Type</mat-label>\n <mat-select formControlName=\"datatype\">\n <mat-option value=\"ARRAY\">Array</mat-option>\n <mat-option value=\"OBJECT\">Object</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Adapter (Model)</mat-label>\n <mat-select formControlName=\"adapter\" #adapterSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let adapter of sampleAdaptors\" [value]=\"adapter.value\">\n {{adapter.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Mapper (Model)</mat-label>\n <mat-select formControlName=\"mapper\" #mapperSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let mapper of sampleMappers\" [value]=\"mapper.value\">\n {{mapper.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <div style=\"display: flex; margin-bottom: 2rem;\" *ngIf=\"adapterSelect.value || mapperSelect.value\">\n <div style=\"flex:1\" class=\"box\">\n <h3>Adapter (Incoming)</h3>\n <div *ngIf=\"adapterSelect.value; else NO_ADAPTER\">\n {{ props(adapterSelect.value) | json }}\n </div>\n <ng-template #NO_ADAPTER>No Transformation</ng-template>\n </div>\n <div style=\"flex:1\" class=\"box\">\n <h3>Mapper (Outgoing)</h3>\n <div *ngIf=\"mapperSelect.value; else NO_MAPPER\">\n {{ props(mapperSelect.value) | json }}\n </div>\n <ng-template #NO_MAPPER>No Transformation</ng-template>\n </div>\n </div>\n\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>RestPath (/ delimited)</mat-label>\n <input matInput placeholder=\"clients/list\" formControlName=\"path\">\n </mat-form-field>\n </div>\n <div>\n <div formArrayName=\"headers\">\n <div *ngFor=\"let task of headers.controls; let i = index\" [formGroupName]=\"i\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Key</mat-label>\n <input matInput placeholder=\"authentication\" formControlName=\"key\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Value</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"value\">\n </mat-form-field>\n <div style=\"margin-top: .5rem;\">\n <button mat-icon-button (click)=\"removeHeader(i)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n </div>\n </div>\n <button mat-stroked-button (click)=\"addHeader()\">Add Header</button>\n </div>\n <div style=\"margin-top: 2rem; display: flex; flex-direction:column; gap: 1rem;\">\n <div>\n <mat-slide-toggle #failedState>Retry on Failed</mat-slide-toggle>\n <div *ngIf=\"failedState.checked\" style=\"display: flex; gap: .5rem; margin-top: 1rem;\" formGroupName=\"retry\">\n <mat-form-field appearance=\"outline\">\n <mat-label>#of Times</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"times\" value=\"3\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Delay Until Next</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"delay\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n <div>\n <mat-slide-toggle #pollingState>Polling</mat-slide-toggle>\n <div *ngIf=\"pollingState.checked\">\n <mat-form-field appearance=\"outline\" style=\"margin-top: 1rem\">\n <mat-label>#of Seconds</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"polling\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n <div>\n <mat-slide-toggle #DBState>Database Storage</mat-slide-toggle>\n <div *ngIf=\"DBState.checked\" style=\"display: flex; gap: .5rem; margin-top: 1rem\" formGroupName=\"database\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Table Name</mat-label>\n <input matInput placeholder=\"table\" formControlName=\"table\" value=\"\">\n </mat-form-field>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Expires In</mat-label>\n <mat-select formControlName=\"expiresIn\">\n <mat-option value=\"1m\">One Minute</mat-option>\n <mat-option value=\"1h\">One Hour</mat-option>\n <mat-option value=\"1d\">One Day</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n </div>\n <div style=\"margin-top: 1rem; display: flex;\">\n <span style=\"flex:1\"></span>\n <button mat-stroked-button (click)=\"onSetStateOptions()\" [disabled]=\"!hasChanged\">\n Set API Request Options\n </button>\n </div>\n <div>\n <mat-error *ngIf=\"(error$ | async) as error\">\n {{ error }}\n </mat-error>\n </div>\n </div>\n </div>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n <mat-progress-bar mode=\"indeterminate\"\n *ngIf=\"(isPending$ | async)\"\n ></mat-progress-bar>\n <div *ngIf=\"pollingState.checked\">\n <mat-progress-bar mode=\"determinate\"\n *ngIf=\"!(isPending$ | async) && (this.countdown$ | async) || -1 > 0\"\n [value]=\"(this.countdown$ | async)\"\n ></mat-progress-bar>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\" *ngIf=\"(GET$ | async) as data\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1;\">CURRENT STATE ({{ dataType }})</h2>\n </div>\n <div *ngIf=\"data.length > 1\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Records</mat-label>\n <mat-select [formControl]=\"selectedRecord\" [disabled]=\"data === null && data?.length === 0\">\n <mat-option [value]=\"\">None</mat-option>\n <mat-option *ngFor=\"let item of data\" [value]=\"item\">\n {{item.name || (item.first_name) | titlecase}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div *ngIf=\"(dataObservable$ | async) as dataRecord\">\n <div *ngIf=\"(selectedRecord$ | async) as record; else NO_RECORD\">\n {{ record | json }}\n </div>\n <ng-template #NO_RECORD>No Record Selected from State</ng-template>\n\n </div>\n <div style=\"margin-top: 1rem;\">\n <span *ngIf=\"data !== null && data?.length > 0; else NO_DATA\">\n State contains a Total of {{ data.length }} Records\n </span>\n <ng-template #NO_DATA>No Records</ng-template>\n <div style=\"display: flex; gap: .5rem; text-align: end; justify-content: flex-end;\">\n <button class=\"btn\" mat-stroked-button (click)=\"onClearRecords()\">Clear</button>\n <button class=\"btn\" mat-stroked-button (click)=\"onRefreshRecords()\" [disabled]=\"hasChanged\">Refresh</button>\n </div>\n </div>\n </div>\n\n\n\n\n <div style=\"margin-top: 1rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">GET Request ({{ dataType }})</h2>\n <div>\n <button mat-raised-button (click)=\"onGetRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(GET_error$ | async) as get_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ get_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(GET$ | async) | jsonv\"></div> -->\n <div *ngIf=\"(GET$ | async) as getData\">{{ getData | json }}</div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">POST Request</h2>\n <div>\n <button mat-raised-button (click)=\"onCreateRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(POST_error$ | async) as post_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ post_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(POST$ | async) | jsonv\"></div> -->\n <div *ngIf=\"(POST$ | async) as postData\">{{ postData | json }}</div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">PUT Request</h2>\n <div>\n <button mat-raised-button (click)=\"onUpdateRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(PUT_error$ | async) as put_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ put_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(PUT$ | async) | jsonv\"></div> -->\n <div *ngIf=\"(PUT$ | async) as putData\">{{ putData | json }}</div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">DELETE Request</h2>\n <div>\n <button mat-raised-button (click)=\"onDeleteRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(DELETE_error$ | async) as delete_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ delete_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(DELETE$ | async) | jsonv\"></div> -->\n <div *ngIf=\"(DELETE$ | async) as deleteData\">{{ deleteData | json }}</div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">Streaming GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\" [disabled]=\"hasChanged\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"(STREAM_error$ | async) as stream_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <div *ngIf=\"(STREAM$ | async) as data\" class=\"container\">\n <table mat-table [dataSource]=\"data\">\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> First Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.name}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"lastName\">\n <th mat-header-cell *matHeaderCellDef> Last Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.lastName}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"age\">\n <th mat-header-cell *matHeaderCellDef> Age </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.age}} </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n </div>\n\n </div>\n\n</div>\n", styles: [".btn{min-width:120px}.mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor;background-color:#f5f5f5}.container{height:400px;overflow:auto}.box{padding:10px;border:1px solid #ccc}\n"] }]
3892
+ args: [{ selector: 'app-request-signals-manager-demo', template: "<div style=\"margin: 2rem;\">\n\n <h2>\n <span>HTTP Request Signals Manager</span>\n <span style=\"margin-left: .5rem;\">\n <mat-icon color=\"accent\">fiber_new</mat-icon>\n </span>\n </h2>\n\n <div [formGroup]=\"requestForm\" style=\"margin-top: 2rem;\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Adapter (Model)</mat-label>\n <mat-select formControlName=\"adapter\" #adapterSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let adapter of sampleAdaptors\" [value]=\"adapter.value\">\n {{adapter.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Mapper (Model)</mat-label>\n <mat-select formControlName=\"mapper\" #mapperSelect>\n <mat-option>None</mat-option>\n <mat-option *ngFor=\"let mapper of sampleMappers\" [value]=\"mapper.value\">\n {{mapper.label}}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"display: flex; margin-bottom: 2rem;\" *ngIf=\"adapterSelect.value || mapperSelect.value\">\n <div style=\"flex:1\" class=\"box\">\n <h3>Adapter (Incoming)</h3>\n <div *ngIf=\"adapterSelect.value; else NO_ADAPTER\">\n {{ props(adapterSelect.value) | json }}\n </div>\n <ng-template #NO_ADAPTER>No Transformation</ng-template>\n </div>\n <div style=\"flex:1\" class=\"box\">\n <h3>Mapper (Outgoing)</h3>\n <div *ngIf=\"mapperSelect.value; else NO_MAPPER\">\n {{ props(mapperSelect.value) | json }}\n </div>\n <ng-template #NO_MAPPER>No Transformation</ng-template>\n </div>\n </div>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>RestPath (/ delimited)</mat-label>\n <input matInput placeholder=\"clients/list\" formControlName=\"path\">\n </mat-form-field>\n </div>\n <div>\n <div formArrayName=\"headers\">\n <div *ngFor=\"let task of headers.controls; let i = index\" [formGroupName]=\"i\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Key</mat-label>\n <input matInput placeholder=\"authentication\" formControlName=\"key\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Value</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"value\">\n </mat-form-field>\n <div style=\"margin-top: .5rem;\">\n <button mat-icon-button (click)=\"removeHeader(i)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n </div>\n </div>\n <button mat-stroked-button (click)=\"addHeader()\" class=\"btn\">Add Header</button>\n </div>\n <div style=\"margin-top: 2rem; display: flex; flex-direction:column; gap: 1rem;\">\n <div>\n <mat-slide-toggle #failedState>Retry on Failed</mat-slide-toggle>\n <div *ngIf=\"failedState.checked\" style=\"display: flex; gap: .5rem; margin-top: 1rem;\" formGroupName=\"retry\">\n <mat-form-field appearance=\"outline\">\n <mat-label>#of Times</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"times\" value=\"3\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Delay Until Next</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"delay\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n <div>\n <mat-slide-toggle #pollingState>Polling</mat-slide-toggle>\n <div *ngIf=\"pollingState.checked\">\n <mat-form-field appearance=\"outline\" style=\"margin-top: 1rem\">\n <mat-label>#of Seconds</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"polling\" value=\"3\">\n </mat-form-field>\n </div>\n </div>\n </div>\n </div>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n <mat-progress-bar mode=\"indeterminate\"\n *ngIf=\"isPending()\"\n ></mat-progress-bar>\n <mat-progress-bar mode=\"determinate\"\n *ngIf=\"pollingState.checked && !isPending()\"\n [value]=\"countdown()\"\n ></mat-progress-bar>\n </div>\n\n <div style=\"margin-top: 1rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onGetRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"GET_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ GET_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"GET_result\">\n {{ GET_result | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">POST Request</h2>\n <div>\n <button mat-raised-button (click)=\"onCreateRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"POST_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ POST_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"POST_result\">\n {{ POST_result | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">PUT Request</h2>\n <div>\n <button mat-raised-button (click)=\"onUpdateRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <div *ngIf=\"PUT_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ PUT_error }}</mat-error>\n </div>\n\n <h3>Include Record ID in the RestPath</h3>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"PUT_result\">\n {{ PUT_result | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">DELETE Request</h2>\n <div>\n <button mat-raised-button (click)=\"onDeleteRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <h3>Include Record ID in the RestPath</h3>\n\n <div *ngIf=\"DELETE_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ DELETE_error }}</mat-error>\n </div>\n\n <div style=\"margin-top: 1rem;\" *ngIf=\"DELETE_result\">\n {{ DELETE_result | json }}\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">Streaming GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <!-- <div *ngIf=\"(STREAM_error$ | async) as stream_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div> -->\n\n <div style=\"margin-top: 1rem;\">\n <div *ngIf=\"STREAM_result as data\" class=\"container\">\n <table mat-table [dataSource]=\"data\">\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> First Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.name}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"lastName\">\n <th mat-header-cell *matHeaderCellDef> Last Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.lastName}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"age\">\n <th mat-header-cell *matHeaderCellDef> Age </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.age}} </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1; padding-top: .5rem;\">AI -<span *ngIf=\"AIType === 1\">STREAMING</span> POST Request</h2>\n <div style=\"display: flex; gap: 1rem;\">\n <button mat-raised-button [matMenuTriggerFor]=\"menu\" style=\"min-width: 120px;\">\n <mat-icon>lan</mat-icon>\n <span *ngIf=\"AIType === 0; else LOCAL\">Server</span>\n <ng-template #LOCAL>\n Local\n </ng-template>\n </button>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item (click)=\"onSelectAIType(0)\">Server</button>\n <button mat-menu-item (click)=\"onSelectAIType(1)\">Local</button>\n </mat-menu>\n <div>\n <button mat-raised-button (click)=\"onStreamPostRequest()\" class=\"btn\">Ask Me</button>\n </div>\n </div>\n </div>\n\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Ask me a Question</mat-label>\n <textarea matInput placeholder=\"Why is the sky blue?\" [formControl]=\"questionControl\"></textarea>\n </mat-form-field>\n </div>\n\n <div *ngIf=\"STREAM_AI_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ STREAM_AI_error }}</mat-error>\n </div>\n\n <div *ngIf=\"AIType === 1; else ALTERNATIVE\" style=\"color: red;\">\n You must have Ollama active and the 'phi3:latest' model to use this feature.\n </div>\n <ng-template #ALTERNATIVE>\n <span style=\"color: gray;\">\n Define the RestPath to the API endpoint that will handle the AI request.\n Use: 'ai/chat' for server\n </span>\n </ng-template>\n\n <div>\n <div *ngIf=\"STREAM_AI_result\" style=\"margin-top: 1rem; font-size: 1.2rem; border-radius: 1rem; border: black 1px solid; padding: 2rem;\">\n <p style=\"margin-bottom: .5rem; white-space:pre-wrap; line-height: 1.6rem;\">{{STREAM_AI_result}}</p>\n </div>\n </div>\n\n </div>\n\n <div style=\"margin-top: 1.5rem; margin-bottom: 1rem; line-height: 1.5rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <div>\n <div style=\"display: flex;\">\n <h2 style=\"flex:1; margin-bottom: 0; padding-top: .5rem; display: flex;\">\n <div>\n Download File\n </div>\n <div style=\"flex:1; margin-left: 1rem;\">\n <mat-slide-toggle #disable>\n <span *ngIf=\"disable.checked; else DISABLE\">\n Enable\n </span>\n <ng-template #DISABLE>Disable</ng-template>\n </mat-slide-toggle>\n </div>\n </h2>\n <div>\n <app-file-downloader\n [disabled]=\"disable.checked\"\n [delayError]=\"3\"\n [apiRequest]=\"downloadRequest\"\n (completed)=\"onDownloadCompleted()\"\n (failed)=\"onDownloadFailed($event)\"\n ></app-file-downloader>\n </div>\n </div>\n </div>\n\n</div>\n", styles: [".btn{min-width:120px}.mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor;background-color:#f5f5f5}.container{height:400px;overflow:auto}.box{padding:10px;border:1px solid #ccc}\n"] }]
2690
3893
  }], ctorParameters: () => [], propDecorators: { failedState: [{
2691
3894
  type: ViewChild,
2692
3895
  args: ["failedState", { static: true }]
@@ -2695,7 +3898,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
2695
3898
  args: ["pollingState", { static: true }]
2696
3899
  }] } });
2697
3900
 
2698
- class LocalStorageDemoComponent {
3901
+ class LocalStorageSignalsDemoComponent {
3902
+ // store type is driven by the form control
2699
3903
  get type() {
2700
3904
  return (this.typeControl.value) ? +this.typeControl.value : 0;
2701
3905
  }
@@ -2705,15 +3909,33 @@ class LocalStorageDemoComponent {
2705
3909
  get isValidData() {
2706
3910
  return this.storageForm.valid;
2707
3911
  }
3912
+ settingFor(name) {
3913
+ const c = this.localStorageManagerService.setting(name);
3914
+ return c ? c() : null;
3915
+ }
2708
3916
  constructor(configOptions) {
2709
3917
  this.configOptions = configOptions;
2710
3918
  this.fb = inject(FormBuilder);
2711
3919
  this.utils = inject(UtilsService);
2712
- this.type$ = new BehaviorSubject(StorageType.GLOBAL);
2713
3920
  this.typeControl = this.fb.control(StorageType.GLOBAL.toString());
2714
- this.localStorageManagerService = inject(LocalStorageManagerService);
2715
- this.settings$ = this.localStorageManagerService.settings$;
2716
- this.setting$ = (store) => this.localStorageManagerService.setting$(store);
3921
+ this.localStorageManagerService = inject(LocalStorageSignalsManagerService);
3922
+ // Use signals directly and computed values for template binding
3923
+ this.settings = computed(() => this.localStorageManagerService.settings());
3924
+ // selected store (signal) and its JSON data for template
3925
+ this.storeSelected = signal(null);
3926
+ this.selectedStoreData = computed(() => {
3927
+ const s = this.storeSelected();
3928
+ if (!s)
3929
+ return '';
3930
+ const computed = this.localStorageManagerService.store(s.name);
3931
+ return computed ? JSON.stringify(computed()) : '';
3932
+ });
3933
+ // filtered settings by selected type
3934
+ this.selectedType = signal(StorageType.GLOBAL);
3935
+ this.filteredSettings = computed(() => {
3936
+ const values = this.settings() || [];
3937
+ return values.filter((item) => item.options.storage === +this.selectedType());
3938
+ });
2717
3939
  this.storageForm = this.fb.group({
2718
3940
  store: this.fb.control(null),
2719
3941
  type: 'local',
@@ -2728,14 +3950,7 @@ class LocalStorageDemoComponent {
2728
3950
  data: this.fb.control('', Validators.required),
2729
3951
  expiresIn: this.fb.control('0')
2730
3952
  });
2731
- this.storeData$ = this.storageForm.get('store')?.valueChanges
2732
- .pipe(switchMap((data) => {
2733
- return data
2734
- ? this.localStorageManagerService.store$(data.name)
2735
- : of('');
2736
- }), tap(data => {
2737
- this.storageForm.get('data')?.patchValue(data, { emitEvent: false });
2738
- }));
3953
+ // no RxJS Observables here; template uses signals/computed directly
2739
3954
  this.expiresIn = (epoch) => this.utils.expiresIn(epoch);
2740
3955
  this.isValidJSON = (str) => {
2741
3956
  try {
@@ -2750,7 +3965,7 @@ class LocalStorageDemoComponent {
2750
3965
  this.filterData = (values) => {
2751
3966
  if (!values)
2752
3967
  return [];
2753
- return values.filter((item) => item.options.storage === +this.type);
3968
+ return values.filter((item) => item.options && item.options.storage === +this.type);
2754
3969
  };
2755
3970
  this.create = false;
2756
3971
  }
@@ -2778,6 +3993,7 @@ class LocalStorageDemoComponent {
2778
3993
  else {
2779
3994
  this.newStoreForm.get('encrypted')?.enable();
2780
3995
  }
3996
+ // nothing to synchronize - templates read computed signals directly
2781
3997
  }
2782
3998
  onCreateStore() {
2783
3999
  if (!this.isValid)
@@ -2807,7 +4023,7 @@ class LocalStorageDemoComponent {
2807
4023
  }
2808
4024
  onSelectedRow(store) {
2809
4025
  this.store = store;
2810
- this.data$ = this.localStorageManagerService.store$(store.name).pipe(map(item => JSON.stringify(item)));
4026
+ this.storeSelected.set(store);
2811
4027
  this.create = false;
2812
4028
  }
2813
4029
  onCreate() {
@@ -2821,7 +4037,7 @@ class LocalStorageDemoComponent {
2821
4037
  this.onCancel();
2822
4038
  }
2823
4039
  onCancel() {
2824
- this.data$ = EMPTY;
4040
+ this.storeSelected.set(null);
2825
4041
  this.store = null;
2826
4042
  this.create = false;
2827
4043
  }
@@ -2835,70 +4051,27 @@ class LocalStorageDemoComponent {
2835
4051
  onReset() {
2836
4052
  this.localStorageManagerService.resetStore();
2837
4053
  }
2838
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LocalStorageDemoComponent, deps: [{ token: CONFIG_SETTINGS_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
2839
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: LocalStorageDemoComponent, selector: "app-local-storage-demo", ngImport: i0, template: "<div style=\"margin: 2rem;\">\n\n <div style=\"display: flex; gap: 1rem\">\n <h2 style=\"padding-top: .5rem; display: flex;\">\n <div style=\"padding-top: .5rem;\">Local Storage Manager</div>\n <div>\n <button mat-icon-button [matMenuTriggerFor]=\"menu\">\n <mat-icon>info</mat-icon>\n </button>\n </div>\n </h2>\n <span style=\"flex:1\"></span>\n <div style=\"padding-top: .25rem;\">\n <mat-button-toggle-group name=\"storage\" [formControl]=\"typeControl\" (change)=\"onCancel()\">\n <mat-button-toggle value=\"0\">Local Storage</mat-button-toggle>\n <mat-button-toggle value=\"1\">Session Storage</mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n <div style=\"margin-top: .25rem;\">\n <button mat-icon-button (click)=\"onCreate()\">\n <mat-icon>add</mat-icon>\n </button>\n </div>\n </div>\n\n <ng-container *ngIf=\"storeProps?.storageName || storeProps?.storageSettingsName\">\n <div style=\"display: flex; gap: .5rem; flex-direction: column; border: gray solid thin; background-color: whitesmoke; padding: 1rem;\"\n >\n <div style=\"display: flex;\">\n <div *ngIf=\"storeProps?.storageSettingsName\" style=\"flex:1\">\n Database: <b>{{ storeProps?.storageSettingsName }}</b>\n </div>\n <div *ngIf=\"storeProps?.storageName\" style=\"flex:1\">\n Data: <b>{{ storeProps?.storageName }}</b>\n </div>\n </div>\n </div>\n </ng-container>\n\n <div style=\"margin-top: 2rem;\" [formGroup]=\"newStoreForm\" *ngIf=\"create; else LIST\">\n\n <h2>Create Store</h2>\n\n <div style=\"display: flex; gap: 1rem;\">\n <div style=\"flex:1\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Store Name</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"name\">\n </mat-form-field>\n </div>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Expires In...</mat-label>\n <mat-select formControlName=\"expiresIn\">\n <mat-option value=\"0\">None</mat-option>\n <mat-option value=\"30s\">30 Seconds</mat-option>\n <mat-option value=\"1mn\">1 Minute</mat-option>\n <mat-option value=\"1hr\">1 Hour</mat-option>\n <mat-option value=\"1d\">1 Day</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"margin-top: 1rem;\">\n <mat-slide-toggle formControlName=\"encrypted\">Encrypted</mat-slide-toggle>\n </div>\n </div>\n\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex: 1;\">\n <mat-label>Data (Must be an Object or Array of any)</mat-label>\n <textarea matInput placeholder=\"[]\" formControlName=\"data\" #json></textarea>\n </mat-form-field>\n </div>\n <mat-error *ngIf=\"!isValidJSON(json.value)\">Not Valid Data</mat-error>\n\n <div style=\"display: flex;\">\n <span style=\"flex:1\"></span>\n <button mat-stroked-button (click)=\"onCreateStore()\" [disabled]=\"!(isValid && isValidJSON(json.value))\">\n Save Store\n </button>\n </div>\n\n </div>\n\n <ng-template #LIST>\n\n <div style=\"margin-top: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n{{ settings$ | async | json }}\n <div *ngIf=\"filterData(settings$ | async) as data\">\n <ng-container *ngIf=\"data.length > 0; else NO_DATA\">\n <table mat-table [dataSource]=\"data\">\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> Store </th>\n <td mat-cell *matCellDef=\"let element\" style=\"font-weight: bold; text-transform: uppercase;\"> {{element.name}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"encrypted\">\n <th mat-header-cell *matHeaderCellDef style=\"text-align: center;\"> Encrypted </th>\n <td mat-cell *matCellDef=\"let element\" style=\"text-align: center;\">\n <ng-container *ngIf=\"element.options.encrypted; else NO\">YES</ng-container>\n <ng-template #NO><span style=\"color:gray\">NO</span></ng-template>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"expires\">\n <th mat-header-cell *matHeaderCellDef style=\"text-align: center;\"> Expires </th>\n <td mat-cell *matCellDef=\"let element\" style=\"text-align: center;\">\n <ng-container *ngIf=\"element.options.expires !== 0; else NO_DATA\">\n {{expiresIn(element.options.expires)}}\n </ng-container>\n <ng-template #NO_DATA>\n \u221E\n </ng-template>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"option\">\n <th mat-header-cell *matHeaderCellDef></th>\n <td mat-cell *matCellDef=\"let element\" style=\"padding-right: 0;\">\n <div style=\"display: flex;justify-content: flex-end;\">\n <button mat-icon-button color=\"warn\" (click)=\"onDelete(element); $event.stopPropagation()\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\" (click)=\"onSelectedRow(row)\"></tr>\n </table>\n </ng-container>\n\n <ng-template #NO_DATA>\n <h3 style=\"margin-top: 1rem;\">No Data</h3>\n </ng-template>\n\n <div *ngIf=\"(data$ | async) as data\" style=\"margin-top: 2rem;\">\n <div style=\"margin-bottom: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <h3>STORE: <span style=\"font-weight: bold;\">{{ store.name | uppercase }}</span></h3>\n <h3>SETTINGS: {{ setting$(store.name) | async | json }}</h3>\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex: 1;\">\n <mat-label>Data (Must be an Object or Array of any)</mat-label>\n <textarea matInput placeholder=\"[]\" #json [value]=\"data\"></textarea>\n </mat-form-field>\n </div>\n <mat-error *ngIf=\"!isValidJSON(json.value)\">Not Valid Data</mat-error>\n\n <div style=\"display: flex; gap: .5rem;\">\n <span style=\"flex:1\"></span>\n <button mat-stroked-button (click)=\"onCancel()\">Cancel</button>\n <button mat-stroked-button (click)=\"onUpdate(store, json.value)\" [disabled]=\"!(isValidJSON(json.value))\">Update</button>\n </div>\n </div>\n </div>\n\n </ng-template>\n\n <div style=\"margin-top: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <button mat-stroked-button (click)=\"onReset()\">Delete All Stores</button>\n </div>\n\n</div>\n\n\n<mat-menu #menu=\"matMenu\">\n <div style=\"padding: 1rem;\">\n <p>Please note that the LocalStorage (encryption) and management of data is dependant on initializing the APP_ID</p>\n <p>Must provide a value for <b>APP_ID</b> in AppModule->Providers</p>\n <mat-divider></mat-divider>\n <div style=\"font-size: smaller; margin-top: .5rem;\">\n <p>Example: UUID (self.crypto.randomUUID() to generate)</p>\n <div style=\"background-color: whitesmoke; padding: 1rem;\">\n &#123;<br>\n provide: APP_ID,<br>\n useValue: \"056991ac-3537-43ab-b5b9-83edf6554eff\",<br>\n &#125;\n </div>\n </div>\n </div>\n</mat-menu>\n", styles: [".mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor}.demo-row-is-clicked{font-weight:700}.mat-mdc-menu-panel.mat-mdc-menu-panel{max-width:280px!important}\n"], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { 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.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i3$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i7.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "directive", type: i7.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i9.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i9.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i9.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i9.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i9.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i9.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i9.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i9.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i9.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i10$1.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i10$1.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "component", type: i11.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "component", type: i12.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "directive", type: i13.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.UpperCasePipe, name: "uppercase" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }], encapsulation: i0.ViewEncapsulation.None }); }
4054
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LocalStorageSignalsDemoComponent, deps: [{ token: CONFIG_SETTINGS_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
4055
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: LocalStorageSignalsDemoComponent, selector: "app-local-storage-signals-demo", ngImport: i0, template: "<div style=\"margin: 2rem;\">\n\n <div style=\"display: flex; gap: 1rem\">\n <h2 style=\"padding-top: .5rem; display: flex;\">\n <div style=\"padding-top: .5rem;\">\n <span>Local Storage Manager</span>\n <span style=\"margin-left: .5rem;\">\n <mat-icon color=\"accent\">fiber_new</mat-icon>\n </span>\n </div>\n <div>\n <button mat-icon-button [matMenuTriggerFor]=\"menu\">\n <mat-icon>info</mat-icon>\n </button>\n </div>\n </h2>\n <span style=\"flex:1\"></span>\n <div style=\"padding-top: .25rem;\">\n <mat-button-toggle-group name=\"storage\" [formControl]=\"typeControl\" (change)=\"onCancel()\">\n <mat-button-toggle value=\"0\">Local Storage</mat-button-toggle>\n <mat-button-toggle value=\"1\">Session Storage</mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n <div style=\"margin-top: .25rem;\">\n <button mat-icon-button (click)=\"onCreate()\">\n <mat-icon>add</mat-icon>\n </button>\n </div>\n </div>\n\n <ng-container *ngIf=\"storeProps?.storageName || storeProps?.storageSettingsName\">\n <div style=\"display: flex; gap: .5rem; flex-direction: column; border: gray solid thin; background-color: whitesmoke; padding: 1rem;\"\n >\n <div style=\"display: flex;\">\n <div *ngIf=\"storeProps?.storageSettingsName\" style=\"flex:1\">\n Database: <b>{{ storeProps?.storageSettingsName }}</b>\n </div>\n <div *ngIf=\"storeProps?.storageName\" style=\"flex:1\">\n Data: <b>{{ storeProps?.storageName }}</b>\n </div>\n </div>\n </div>\n </ng-container>\n\n <div style=\"margin-top: 2rem;\" [formGroup]=\"newStoreForm\" *ngIf=\"create; else LIST\">\n\n <h2>Create Store</h2>\n\n <div style=\"display: flex; gap: 1rem;\">\n <div style=\"flex:1\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Store Name</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"name\">\n </mat-form-field>\n </div>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Expires In...</mat-label>\n <mat-select formControlName=\"expiresIn\">\n <mat-option value=\"0\">None</mat-option>\n <mat-option value=\"30s\">30 Seconds</mat-option>\n <mat-option value=\"1mn\">1 Minute</mat-option>\n <mat-option value=\"1hr\">1 Hour</mat-option>\n <mat-option value=\"1d\">1 Day</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"margin-top: 1rem;\">\n <mat-slide-toggle formControlName=\"encrypted\">Encrypted</mat-slide-toggle>\n </div>\n </div>\n\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex: 1;\">\n <mat-label>Data (Must be an Object or Array of any)</mat-label>\n <textarea matInput placeholder=\"[]\" formControlName=\"data\" #json></textarea>\n </mat-form-field>\n </div>\n <mat-error *ngIf=\"!isValidJSON(json.value)\">Not Valid Data</mat-error>\n\n <div style=\"display: flex;\">\n <span style=\"flex:1\"></span>\n <button mat-stroked-button (click)=\"onCreateStore()\" [disabled]=\"!(isValid && isValidJSON(json.value))\">\n Save Store\n </button>\n </div>\n\n </div>\n\n <ng-template #LIST>\n\n <div style=\"margin-top: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n{{ settings() | json }}\n <div *ngIf=\"filterData(settings()) as data\">\n <ng-container *ngIf=\"data.length > 0; else NO_DATA\">\n <table mat-table [dataSource]=\"data\">\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> Store </th>\n <td mat-cell *matCellDef=\"let element\" style=\"font-weight: bold; text-transform: uppercase;\"> {{element.name}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"encrypted\">\n <th mat-header-cell *matHeaderCellDef style=\"text-align: center;\"> Encrypted </th>\n <td mat-cell *matCellDef=\"let element\" style=\"text-align: center;\">\n <ng-container *ngIf=\"element.options.encrypted; else NO\">YES</ng-container>\n <ng-template #NO><span style=\"color:gray\">NO</span></ng-template>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"expires\">\n <th mat-header-cell *matHeaderCellDef style=\"text-align: center;\"> Expires </th>\n <td mat-cell *matCellDef=\"let element\" style=\"text-align: center;\">\n <ng-container *ngIf=\"element.options.expires !== 0; else NO_DATA\">\n {{expiresIn(element.options.expires)}}\n </ng-container>\n <ng-template #NO_DATA>\n \u221E\n </ng-template>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"option\">\n <th mat-header-cell *matHeaderCellDef></th>\n <td mat-cell *matCellDef=\"let element\" style=\"padding-right: 0;\">\n <div style=\"display: flex;justify-content: flex-end;\">\n <button mat-icon-button color=\"warn\" (click)=\"onDelete(element); $event.stopPropagation()\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\" (click)=\"onSelectedRow(row)\"></tr>\n </table>\n </ng-container>\n\n <ng-template #NO_DATA>\n <h3 style=\"margin-top: 1rem;\">No Data</h3>\n </ng-template>\n\n <div *ngIf=\"storeSelected() as selectedStore\" style=\"margin-top: 2rem;\">\n <div style=\"margin-bottom: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <h3>STORE: <span style=\"font-weight: bold;\">{{ selectedStore.name | uppercase }}</span></h3>\n <h3>SETTINGS: {{ localStorageManagerService.setting(selectedStore.name) ? (localStorageManagerService.setting(selectedStore.name)() | json) : '{}' }}</h3>\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex: 1;\">\n <mat-label>Data (Must be an Object or Array of any)</mat-label>\n <textarea matInput placeholder=\"[]\" #json [value]=\"selectedStoreData()\"></textarea>\n </mat-form-field>\n </div>\n <mat-error *ngIf=\"!isValidJSON(json.value)\">Not Valid Data</mat-error>\n\n <div style=\"display: flex; gap: .5rem;\">\n <span style=\"flex:1\"></span>\n <button mat-stroked-button (click)=\"onCancel()\">Cancel</button>\n <button mat-stroked-button (click)=\"onUpdate(selectedStore, json.value)\" [disabled]=\"!(isValidJSON(json.value))\">Update</button>\n </div>\n </div>\n </div>\n\n </ng-template>\n\n <div style=\"margin-top: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <button mat-stroked-button (click)=\"onReset()\">Delete All Stores</button>\n </div>\n\n</div>\n\n\n<mat-menu #menu=\"matMenu\">\n <div style=\"padding: 1rem;\">\n <p>Please note that the LocalStorage (encryption) and management of data is dependant on initializing the APP_ID</p>\n <p>Must provide a value for <b>APP_ID</b> in AppModule->Providers</p>\n <mat-divider></mat-divider>\n <div style=\"font-size: smaller; margin-top: .5rem;\">\n <p>Example: UUID (self.crypto.randomUUID() to generate)</p>\n <div style=\"background-color: whitesmoke; padding: 1rem;\">\n &#123;<br>\n provide: APP_ID,<br>\n useValue: \"056991ac-3537-43ab-b5b9-83edf6554eff\",<br>\n &#125;\n </div>\n </div>\n </div>\n</mat-menu>\n", styles: [".mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor}.demo-row-is-clicked{font-weight:700}.mat-mdc-menu-panel.mat-mdc-menu-panel{max-width:280px!important}\n"], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { 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.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i3$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i7.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "directive", type: i7.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i9.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i9.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i9.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i9.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i9.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i9.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i9.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i9.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i9.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i10$1.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i10$1.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "component", type: i11.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "component", type: i12.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "directive", type: i13.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "pipe", type: i1$1.UpperCasePipe, name: "uppercase" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }], encapsulation: i0.ViewEncapsulation.None }); }
2840
4056
  }
2841
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LocalStorageDemoComponent, decorators: [{
4057
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LocalStorageSignalsDemoComponent, decorators: [{
2842
4058
  type: Component,
2843
- args: [{ selector: 'app-local-storage-demo', encapsulation: ViewEncapsulation.None, template: "<div style=\"margin: 2rem;\">\n\n <div style=\"display: flex; gap: 1rem\">\n <h2 style=\"padding-top: .5rem; display: flex;\">\n <div style=\"padding-top: .5rem;\">Local Storage Manager</div>\n <div>\n <button mat-icon-button [matMenuTriggerFor]=\"menu\">\n <mat-icon>info</mat-icon>\n </button>\n </div>\n </h2>\n <span style=\"flex:1\"></span>\n <div style=\"padding-top: .25rem;\">\n <mat-button-toggle-group name=\"storage\" [formControl]=\"typeControl\" (change)=\"onCancel()\">\n <mat-button-toggle value=\"0\">Local Storage</mat-button-toggle>\n <mat-button-toggle value=\"1\">Session Storage</mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n <div style=\"margin-top: .25rem;\">\n <button mat-icon-button (click)=\"onCreate()\">\n <mat-icon>add</mat-icon>\n </button>\n </div>\n </div>\n\n <ng-container *ngIf=\"storeProps?.storageName || storeProps?.storageSettingsName\">\n <div style=\"display: flex; gap: .5rem; flex-direction: column; border: gray solid thin; background-color: whitesmoke; padding: 1rem;\"\n >\n <div style=\"display: flex;\">\n <div *ngIf=\"storeProps?.storageSettingsName\" style=\"flex:1\">\n Database: <b>{{ storeProps?.storageSettingsName }}</b>\n </div>\n <div *ngIf=\"storeProps?.storageName\" style=\"flex:1\">\n Data: <b>{{ storeProps?.storageName }}</b>\n </div>\n </div>\n </div>\n </ng-container>\n\n <div style=\"margin-top: 2rem;\" [formGroup]=\"newStoreForm\" *ngIf=\"create; else LIST\">\n\n <h2>Create Store</h2>\n\n <div style=\"display: flex; gap: 1rem;\">\n <div style=\"flex:1\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Store Name</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"name\">\n </mat-form-field>\n </div>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Expires In...</mat-label>\n <mat-select formControlName=\"expiresIn\">\n <mat-option value=\"0\">None</mat-option>\n <mat-option value=\"30s\">30 Seconds</mat-option>\n <mat-option value=\"1mn\">1 Minute</mat-option>\n <mat-option value=\"1hr\">1 Hour</mat-option>\n <mat-option value=\"1d\">1 Day</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"margin-top: 1rem;\">\n <mat-slide-toggle formControlName=\"encrypted\">Encrypted</mat-slide-toggle>\n </div>\n </div>\n\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex: 1;\">\n <mat-label>Data (Must be an Object or Array of any)</mat-label>\n <textarea matInput placeholder=\"[]\" formControlName=\"data\" #json></textarea>\n </mat-form-field>\n </div>\n <mat-error *ngIf=\"!isValidJSON(json.value)\">Not Valid Data</mat-error>\n\n <div style=\"display: flex;\">\n <span style=\"flex:1\"></span>\n <button mat-stroked-button (click)=\"onCreateStore()\" [disabled]=\"!(isValid && isValidJSON(json.value))\">\n Save Store\n </button>\n </div>\n\n </div>\n\n <ng-template #LIST>\n\n <div style=\"margin-top: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n{{ settings$ | async | json }}\n <div *ngIf=\"filterData(settings$ | async) as data\">\n <ng-container *ngIf=\"data.length > 0; else NO_DATA\">\n <table mat-table [dataSource]=\"data\">\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> Store </th>\n <td mat-cell *matCellDef=\"let element\" style=\"font-weight: bold; text-transform: uppercase;\"> {{element.name}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"encrypted\">\n <th mat-header-cell *matHeaderCellDef style=\"text-align: center;\"> Encrypted </th>\n <td mat-cell *matCellDef=\"let element\" style=\"text-align: center;\">\n <ng-container *ngIf=\"element.options.encrypted; else NO\">YES</ng-container>\n <ng-template #NO><span style=\"color:gray\">NO</span></ng-template>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"expires\">\n <th mat-header-cell *matHeaderCellDef style=\"text-align: center;\"> Expires </th>\n <td mat-cell *matCellDef=\"let element\" style=\"text-align: center;\">\n <ng-container *ngIf=\"element.options.expires !== 0; else NO_DATA\">\n {{expiresIn(element.options.expires)}}\n </ng-container>\n <ng-template #NO_DATA>\n \u221E\n </ng-template>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"option\">\n <th mat-header-cell *matHeaderCellDef></th>\n <td mat-cell *matCellDef=\"let element\" style=\"padding-right: 0;\">\n <div style=\"display: flex;justify-content: flex-end;\">\n <button mat-icon-button color=\"warn\" (click)=\"onDelete(element); $event.stopPropagation()\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\" (click)=\"onSelectedRow(row)\"></tr>\n </table>\n </ng-container>\n\n <ng-template #NO_DATA>\n <h3 style=\"margin-top: 1rem;\">No Data</h3>\n </ng-template>\n\n <div *ngIf=\"(data$ | async) as data\" style=\"margin-top: 2rem;\">\n <div style=\"margin-bottom: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <h3>STORE: <span style=\"font-weight: bold;\">{{ store.name | uppercase }}</span></h3>\n <h3>SETTINGS: {{ setting$(store.name) | async | json }}</h3>\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex: 1;\">\n <mat-label>Data (Must be an Object or Array of any)</mat-label>\n <textarea matInput placeholder=\"[]\" #json [value]=\"data\"></textarea>\n </mat-form-field>\n </div>\n <mat-error *ngIf=\"!isValidJSON(json.value)\">Not Valid Data</mat-error>\n\n <div style=\"display: flex; gap: .5rem;\">\n <span style=\"flex:1\"></span>\n <button mat-stroked-button (click)=\"onCancel()\">Cancel</button>\n <button mat-stroked-button (click)=\"onUpdate(store, json.value)\" [disabled]=\"!(isValidJSON(json.value))\">Update</button>\n </div>\n </div>\n </div>\n\n </ng-template>\n\n <div style=\"margin-top: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <button mat-stroked-button (click)=\"onReset()\">Delete All Stores</button>\n </div>\n\n</div>\n\n\n<mat-menu #menu=\"matMenu\">\n <div style=\"padding: 1rem;\">\n <p>Please note that the LocalStorage (encryption) and management of data is dependant on initializing the APP_ID</p>\n <p>Must provide a value for <b>APP_ID</b> in AppModule->Providers</p>\n <mat-divider></mat-divider>\n <div style=\"font-size: smaller; margin-top: .5rem;\">\n <p>Example: UUID (self.crypto.randomUUID() to generate)</p>\n <div style=\"background-color: whitesmoke; padding: 1rem;\">\n &#123;<br>\n provide: APP_ID,<br>\n useValue: \"056991ac-3537-43ab-b5b9-83edf6554eff\",<br>\n &#125;\n </div>\n </div>\n </div>\n</mat-menu>\n", styles: [".mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor}.demo-row-is-clicked{font-weight:700}.mat-mdc-menu-panel.mat-mdc-menu-panel{max-width:280px!important}\n"] }]
4059
+ args: [{ selector: 'app-local-storage-signals-demo', encapsulation: ViewEncapsulation.None, template: "<div style=\"margin: 2rem;\">\n\n <div style=\"display: flex; gap: 1rem\">\n <h2 style=\"padding-top: .5rem; display: flex;\">\n <div style=\"padding-top: .5rem;\">\n <span>Local Storage Manager</span>\n <span style=\"margin-left: .5rem;\">\n <mat-icon color=\"accent\">fiber_new</mat-icon>\n </span>\n </div>\n <div>\n <button mat-icon-button [matMenuTriggerFor]=\"menu\">\n <mat-icon>info</mat-icon>\n </button>\n </div>\n </h2>\n <span style=\"flex:1\"></span>\n <div style=\"padding-top: .25rem;\">\n <mat-button-toggle-group name=\"storage\" [formControl]=\"typeControl\" (change)=\"onCancel()\">\n <mat-button-toggle value=\"0\">Local Storage</mat-button-toggle>\n <mat-button-toggle value=\"1\">Session Storage</mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n <div style=\"margin-top: .25rem;\">\n <button mat-icon-button (click)=\"onCreate()\">\n <mat-icon>add</mat-icon>\n </button>\n </div>\n </div>\n\n <ng-container *ngIf=\"storeProps?.storageName || storeProps?.storageSettingsName\">\n <div style=\"display: flex; gap: .5rem; flex-direction: column; border: gray solid thin; background-color: whitesmoke; padding: 1rem;\"\n >\n <div style=\"display: flex;\">\n <div *ngIf=\"storeProps?.storageSettingsName\" style=\"flex:1\">\n Database: <b>{{ storeProps?.storageSettingsName }}</b>\n </div>\n <div *ngIf=\"storeProps?.storageName\" style=\"flex:1\">\n Data: <b>{{ storeProps?.storageName }}</b>\n </div>\n </div>\n </div>\n </ng-container>\n\n <div style=\"margin-top: 2rem;\" [formGroup]=\"newStoreForm\" *ngIf=\"create; else LIST\">\n\n <h2>Create Store</h2>\n\n <div style=\"display: flex; gap: 1rem;\">\n <div style=\"flex:1\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Store Name</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"name\">\n </mat-form-field>\n </div>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Expires In...</mat-label>\n <mat-select formControlName=\"expiresIn\">\n <mat-option value=\"0\">None</mat-option>\n <mat-option value=\"30s\">30 Seconds</mat-option>\n <mat-option value=\"1mn\">1 Minute</mat-option>\n <mat-option value=\"1hr\">1 Hour</mat-option>\n <mat-option value=\"1d\">1 Day</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"margin-top: 1rem;\">\n <mat-slide-toggle formControlName=\"encrypted\">Encrypted</mat-slide-toggle>\n </div>\n </div>\n\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex: 1;\">\n <mat-label>Data (Must be an Object or Array of any)</mat-label>\n <textarea matInput placeholder=\"[]\" formControlName=\"data\" #json></textarea>\n </mat-form-field>\n </div>\n <mat-error *ngIf=\"!isValidJSON(json.value)\">Not Valid Data</mat-error>\n\n <div style=\"display: flex;\">\n <span style=\"flex:1\"></span>\n <button mat-stroked-button (click)=\"onCreateStore()\" [disabled]=\"!(isValid && isValidJSON(json.value))\">\n Save Store\n </button>\n </div>\n\n </div>\n\n <ng-template #LIST>\n\n <div style=\"margin-top: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n{{ settings() | json }}\n <div *ngIf=\"filterData(settings()) as data\">\n <ng-container *ngIf=\"data.length > 0; else NO_DATA\">\n <table mat-table [dataSource]=\"data\">\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> Store </th>\n <td mat-cell *matCellDef=\"let element\" style=\"font-weight: bold; text-transform: uppercase;\"> {{element.name}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n\n <ng-container matColumnDef=\"encrypted\">\n <th mat-header-cell *matHeaderCellDef style=\"text-align: center;\"> Encrypted </th>\n <td mat-cell *matCellDef=\"let element\" style=\"text-align: center;\">\n <ng-container *ngIf=\"element.options.encrypted; else NO\">YES</ng-container>\n <ng-template #NO><span style=\"color:gray\">NO</span></ng-template>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"expires\">\n <th mat-header-cell *matHeaderCellDef style=\"text-align: center;\"> Expires </th>\n <td mat-cell *matCellDef=\"let element\" style=\"text-align: center;\">\n <ng-container *ngIf=\"element.options.expires !== 0; else NO_DATA\">\n {{expiresIn(element.options.expires)}}\n </ng-container>\n <ng-template #NO_DATA>\n \u221E\n </ng-template>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"option\">\n <th mat-header-cell *matHeaderCellDef></th>\n <td mat-cell *matCellDef=\"let element\" style=\"padding-right: 0;\">\n <div style=\"display: flex;justify-content: flex-end;\">\n <button mat-icon-button color=\"warn\" (click)=\"onDelete(element); $event.stopPropagation()\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\" (click)=\"onSelectedRow(row)\"></tr>\n </table>\n </ng-container>\n\n <ng-template #NO_DATA>\n <h3 style=\"margin-top: 1rem;\">No Data</h3>\n </ng-template>\n\n <div *ngIf=\"storeSelected() as selectedStore\" style=\"margin-top: 2rem;\">\n <div style=\"margin-bottom: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <h3>STORE: <span style=\"font-weight: bold;\">{{ selectedStore.name | uppercase }}</span></h3>\n <h3>SETTINGS: {{ localStorageManagerService.setting(selectedStore.name) ? (localStorageManagerService.setting(selectedStore.name)() | json) : '{}' }}</h3>\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex: 1;\">\n <mat-label>Data (Must be an Object or Array of any)</mat-label>\n <textarea matInput placeholder=\"[]\" #json [value]=\"selectedStoreData()\"></textarea>\n </mat-form-field>\n </div>\n <mat-error *ngIf=\"!isValidJSON(json.value)\">Not Valid Data</mat-error>\n\n <div style=\"display: flex; gap: .5rem;\">\n <span style=\"flex:1\"></span>\n <button mat-stroked-button (click)=\"onCancel()\">Cancel</button>\n <button mat-stroked-button (click)=\"onUpdate(selectedStore, json.value)\" [disabled]=\"!(isValidJSON(json.value))\">Update</button>\n </div>\n </div>\n </div>\n\n </ng-template>\n\n <div style=\"margin-top: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <button mat-stroked-button (click)=\"onReset()\">Delete All Stores</button>\n </div>\n\n</div>\n\n\n<mat-menu #menu=\"matMenu\">\n <div style=\"padding: 1rem;\">\n <p>Please note that the LocalStorage (encryption) and management of data is dependant on initializing the APP_ID</p>\n <p>Must provide a value for <b>APP_ID</b> in AppModule->Providers</p>\n <mat-divider></mat-divider>\n <div style=\"font-size: smaller; margin-top: .5rem;\">\n <p>Example: UUID (self.crypto.randomUUID() to generate)</p>\n <div style=\"background-color: whitesmoke; padding: 1rem;\">\n &#123;<br>\n provide: APP_ID,<br>\n useValue: \"056991ac-3537-43ab-b5b9-83edf6554eff\",<br>\n &#125;\n </div>\n </div>\n </div>\n</mat-menu>\n", styles: [".mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor}.demo-row-is-clicked{font-weight:700}.mat-mdc-menu-panel.mat-mdc-menu-panel{max-width:280px!important}\n"] }]
2844
4060
  }], ctorParameters: () => [{ type: ConfigOptions, decorators: [{
2845
4061
  type: Inject,
2846
4062
  args: [CONFIG_SETTINGS_TOKEN]
2847
4063
  }] }] });
2848
4064
 
2849
- const PROXY_CONFIG = new InjectionToken('PROXY_CONFIG');
2850
- class ProxyDebuggerInterceptor {
2851
- constructor(proxyConfig) {
2852
- this.proxyConfig = proxyConfig;
2853
- }
2854
- intercept(req, next) {
2855
- if (!this.proxyConfig) {
2856
- return next.handle(req);
2857
- }
2858
- const headers = req.headers.keys().reduce((acc, key) => {
2859
- acc[key] = req.headers.get(key) || '';
2860
- return acc;
2861
- }, {});
2862
- for (const proxyPath in this.proxyConfig) {
2863
- if (this.proxyConfig.hasOwnProperty(proxyPath)) {
2864
- const proxyDetails = this.proxyConfig[proxyPath];
2865
- const regex = new RegExp('^' + proxyPath.replace('/', '').replace('*', '(.*)'));
2866
- if (regex.test(req.url)) {
2867
- const target = proxyDetails.target;
2868
- const endpoint = req.url.replace(regex, '$1');
2869
- const actualPath = target + '/' + endpoint;
2870
- console.log('Request Proxied:', {
2871
- requestUrl: req.url,
2872
- requestPayload: req.body,
2873
- headers: headers,
2874
- proxyPath: proxyPath,
2875
- actualPath: actualPath,
2876
- });
2877
- }
2878
- }
2879
- }
2880
- return next.handle(req);
2881
- }
2882
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ProxyDebuggerInterceptor, deps: [{ token: PROXY_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
2883
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ProxyDebuggerInterceptor }); }
2884
- }
2885
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ProxyDebuggerInterceptor, decorators: [{
2886
- type: Injectable
2887
- }], ctorParameters: () => [{ type: undefined, decorators: [{
2888
- type: Optional
2889
- }, {
2890
- type: Inject,
2891
- args: [PROXY_CONFIG]
2892
- }] }] });
2893
-
2894
4065
  class HttpRequestServicesDemoComponent {
2895
4066
  constructor(configOptions) {
2896
4067
  this.configOptions = configOptions;
2897
4068
  this.requestTypes = [
2898
4069
  { name: "Http Service", value: 'http_service' },
4070
+ { name: "Http Signals Service", value: 'http_signals_service', new: true },
2899
4071
  { name: "Http State Service", value: 'http_state_service' },
2900
4072
  { name: "Database Service", value: 'database_service', divider: true, disabled: true },
2901
4073
  { name: "Local Storage Service", value: 'local_storage_service' },
4074
+ { name: "Local Signals Storage Service", value: 'local_storage_signals_service', new: true },
2902
4075
  ];
2903
4076
  this.selectedService = this.requestTypes[0].value;
2904
4077
  }
@@ -2910,11 +4083,11 @@ class HttpRequestServicesDemoComponent {
2910
4083
  this.selectedService = this.requestTypes[type].value;
2911
4084
  }
2912
4085
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: HttpRequestServicesDemoComponent, deps: [{ token: CONFIG_SETTINGS_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
2913
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: HttpRequestServicesDemoComponent, selector: "app-http-request-services-demo", ngImport: i0, template: "<mat-toolbar style=\"display:flex\">\n <div>Http Request Manager Services</div>\n <div style=\"flex:1\"></div>\n <button mat-stroked-button [matMenuTriggerFor]=\"menu\">Services</button>\n <mat-menu #menu=\"matMenu\">\n <ng-container *ngFor=\"let type of requestTypes; index as i\">\n <div\n *ngIf=\"type?.divider\"\n style=\"margin-top: .5rem; margin-bottom: .5rem;\"\n >\n <mat-divider></mat-divider>\n </div>\n <button\n mat-menu-item\n (click)=\"onSelected(i)\"\n [disabled]=\"type.disabled\"\n >\n {{ type.name }}\n </button>\n </ng-container>\n\n </mat-menu>\n</mat-toolbar>\n\n<span [ngSwitch]=\"selectedService\">\n <p *ngSwitchCase=\"'http_service'\">\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-manager-demo></app-request-manager-demo>\n </p>\n <p *ngSwitchCase=\"'http_state_service'\">\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-manager-state-demo></app-request-manager-state-demo>\n </p>\n <p *ngSwitchCase=\"'database_service'\">\n <!-- <app-database-data-demo></app-database-data-demo> -->\n </p>\n <p *ngSwitchCase=\"'local_storage_service'\">\n <ng-container *ngTemplateOutlet=\"LOCAL_OPTIONS\"></ng-container>\n <app-local-storage-demo></app-local-storage-demo>\n </p>\n <p *ngSwitchDefault>\n Other\n </p>\n</span>\n\n<ng-template #HTTP_OPTIONS>\n <ng-container *ngIf=\"injectionOptions?.httpRequestOptions\">\n <div class=\"box\">\n <h3 style=\"font-weight: bold;\">Injection Token Detected - HTTP Options</h3>\n {{ injectionOptions?.httpRequestOptions| json }}\n </div>\n </ng-container>\n</ng-template>\n\n<ng-template #LOCAL_OPTIONS>\n <ng-container class=\"box\" *ngIf=\"injectionOptions?.LocalStorageOptions\">\n <div class=\"box\">\n <h3 style=\"font-weight: bold;\">Injection Token Detected - LocalStorage Options</h3>\n {{ injectionOptions?.LocalStorageOptions| json }}\n </div>\n </ng-container>\n</ng-template>\n\n\n", styles: [".box{padding:1rem;background-color:#f5f5f5;border:thin gray solid;margin-top:1rem}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1$1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "component", type: i3$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i7.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i7.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i7.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: i12.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "component", type: i5$1.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "component", type: RequestManagerStateDemoComponent, selector: "app-request-manager-state-demo" }, { kind: "component", type: RequestManagerDemoComponent, selector: "app-request-manager-demo" }, { kind: "component", type: LocalStorageDemoComponent, selector: "app-local-storage-demo" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }] }); }
4086
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: HttpRequestServicesDemoComponent, selector: "app-http-request-services-demo", ngImport: i0, template: "<mat-toolbar style=\"display:flex\">\n <div>Http Request Manager Services</div>\n <div style=\"flex:1\"></div>\n <button mat-stroked-button [matMenuTriggerFor]=\"menu\">Services</button>\n <mat-menu #menu=\"matMenu\">\n <ng-container *ngFor=\"let type of requestTypes; index as i\">\n <div\n *ngIf=\"type?.divider\"\n style=\"margin-top: .5rem; margin-bottom: .5rem;\"\n >\n <mat-divider></mat-divider>\n </div>\n <button\n mat-menu-item\n (click)=\"onSelected(i)\"\n [disabled]=\"type.disabled\"\n >\n {{ type.name }}\n </button>\n </ng-container>\n\n </mat-menu>\n</mat-toolbar>\n\n<span [ngSwitch]=\"selectedService\">\n <p *ngSwitchCase=\"'http_service'\">\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-manager-demo></app-request-manager-demo>\n </p>\n <p *ngSwitchCase=\"'http_signals_service'\">\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-signals-manager-demo></app-request-signals-manager-demo>\n </p>\n <p *ngSwitchCase=\"'http_state_service'\">\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-manager-state-demo></app-request-manager-state-demo>\n </p>\n <p *ngSwitchCase=\"'database_service'\">\n <!-- <app-database-data-demo></app-database-data-demo> -->\n </p>\n <p *ngSwitchCase=\"'local_storage_service'\">\n <ng-container *ngTemplateOutlet=\"LOCAL_OPTIONS\"></ng-container>\n <app-local-storage-demo></app-local-storage-demo>\n </p>\n <p *ngSwitchCase=\"'local_storage_signals_service'\">\n <ng-container *ngTemplateOutlet=\"LOCAL_OPTIONS\"></ng-container>\n <app-local-storage-signals-demo></app-local-storage-signals-demo>\n </p>\n <p *ngSwitchDefault>\n Other\n </p>\n</span>\n\n<ng-template #HTTP_OPTIONS>\n <ng-container *ngIf=\"injectionOptions?.httpRequestOptions\">\n <div class=\"box\">\n <h3 style=\"font-weight: bold;\">Injection Token Detected - HTTP Options</h3>\n {{ injectionOptions?.httpRequestOptions| json }}\n </div>\n </ng-container>\n</ng-template>\n\n<ng-template #LOCAL_OPTIONS>\n <ng-container class=\"box\" *ngIf=\"injectionOptions?.LocalStorageOptions\">\n <div class=\"box\">\n <h3 style=\"font-weight: bold;\">Injection Token Detected - LocalStorage Options</h3>\n {{ injectionOptions?.LocalStorageOptions| json }}\n </div>\n </ng-container>\n</ng-template>\n\n\n", styles: [".box{padding:1rem;background-color:#f5f5f5;border:thin gray solid;margin-top:1rem}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1$1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "component", type: i3$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i7.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i7.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i7.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: i12.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "component", type: i5$1.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "component", type: RequestManagerStateDemoComponent, selector: "app-request-manager-state-demo" }, { kind: "component", type: RequestManagerDemoComponent, selector: "app-request-manager-demo" }, { kind: "component", type: RequestSignalsManagerDemoComponent, selector: "app-request-signals-manager-demo" }, { kind: "component", type: LocalStorageDemoComponent, selector: "app-local-storage-demo" }, { kind: "component", type: LocalStorageSignalsDemoComponent, selector: "app-local-storage-signals-demo" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }] }); }
2914
4087
  }
2915
4088
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: HttpRequestServicesDemoComponent, decorators: [{
2916
4089
  type: Component,
2917
- args: [{ selector: 'app-http-request-services-demo', template: "<mat-toolbar style=\"display:flex\">\n <div>Http Request Manager Services</div>\n <div style=\"flex:1\"></div>\n <button mat-stroked-button [matMenuTriggerFor]=\"menu\">Services</button>\n <mat-menu #menu=\"matMenu\">\n <ng-container *ngFor=\"let type of requestTypes; index as i\">\n <div\n *ngIf=\"type?.divider\"\n style=\"margin-top: .5rem; margin-bottom: .5rem;\"\n >\n <mat-divider></mat-divider>\n </div>\n <button\n mat-menu-item\n (click)=\"onSelected(i)\"\n [disabled]=\"type.disabled\"\n >\n {{ type.name }}\n </button>\n </ng-container>\n\n </mat-menu>\n</mat-toolbar>\n\n<span [ngSwitch]=\"selectedService\">\n <p *ngSwitchCase=\"'http_service'\">\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-manager-demo></app-request-manager-demo>\n </p>\n <p *ngSwitchCase=\"'http_state_service'\">\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-manager-state-demo></app-request-manager-state-demo>\n </p>\n <p *ngSwitchCase=\"'database_service'\">\n <!-- <app-database-data-demo></app-database-data-demo> -->\n </p>\n <p *ngSwitchCase=\"'local_storage_service'\">\n <ng-container *ngTemplateOutlet=\"LOCAL_OPTIONS\"></ng-container>\n <app-local-storage-demo></app-local-storage-demo>\n </p>\n <p *ngSwitchDefault>\n Other\n </p>\n</span>\n\n<ng-template #HTTP_OPTIONS>\n <ng-container *ngIf=\"injectionOptions?.httpRequestOptions\">\n <div class=\"box\">\n <h3 style=\"font-weight: bold;\">Injection Token Detected - HTTP Options</h3>\n {{ injectionOptions?.httpRequestOptions| json }}\n </div>\n </ng-container>\n</ng-template>\n\n<ng-template #LOCAL_OPTIONS>\n <ng-container class=\"box\" *ngIf=\"injectionOptions?.LocalStorageOptions\">\n <div class=\"box\">\n <h3 style=\"font-weight: bold;\">Injection Token Detected - LocalStorage Options</h3>\n {{ injectionOptions?.LocalStorageOptions| json }}\n </div>\n </ng-container>\n</ng-template>\n\n\n", styles: [".box{padding:1rem;background-color:#f5f5f5;border:thin gray solid;margin-top:1rem}\n"] }]
4090
+ args: [{ selector: 'app-http-request-services-demo', template: "<mat-toolbar style=\"display:flex\">\n <div>Http Request Manager Services</div>\n <div style=\"flex:1\"></div>\n <button mat-stroked-button [matMenuTriggerFor]=\"menu\">Services</button>\n <mat-menu #menu=\"matMenu\">\n <ng-container *ngFor=\"let type of requestTypes; index as i\">\n <div\n *ngIf=\"type?.divider\"\n style=\"margin-top: .5rem; margin-bottom: .5rem;\"\n >\n <mat-divider></mat-divider>\n </div>\n <button\n mat-menu-item\n (click)=\"onSelected(i)\"\n [disabled]=\"type.disabled\"\n >\n {{ type.name }}\n </button>\n </ng-container>\n\n </mat-menu>\n</mat-toolbar>\n\n<span [ngSwitch]=\"selectedService\">\n <p *ngSwitchCase=\"'http_service'\">\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-manager-demo></app-request-manager-demo>\n </p>\n <p *ngSwitchCase=\"'http_signals_service'\">\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-signals-manager-demo></app-request-signals-manager-demo>\n </p>\n <p *ngSwitchCase=\"'http_state_service'\">\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-manager-state-demo></app-request-manager-state-demo>\n </p>\n <p *ngSwitchCase=\"'database_service'\">\n <!-- <app-database-data-demo></app-database-data-demo> -->\n </p>\n <p *ngSwitchCase=\"'local_storage_service'\">\n <ng-container *ngTemplateOutlet=\"LOCAL_OPTIONS\"></ng-container>\n <app-local-storage-demo></app-local-storage-demo>\n </p>\n <p *ngSwitchCase=\"'local_storage_signals_service'\">\n <ng-container *ngTemplateOutlet=\"LOCAL_OPTIONS\"></ng-container>\n <app-local-storage-signals-demo></app-local-storage-signals-demo>\n </p>\n <p *ngSwitchDefault>\n Other\n </p>\n</span>\n\n<ng-template #HTTP_OPTIONS>\n <ng-container *ngIf=\"injectionOptions?.httpRequestOptions\">\n <div class=\"box\">\n <h3 style=\"font-weight: bold;\">Injection Token Detected - HTTP Options</h3>\n {{ injectionOptions?.httpRequestOptions| json }}\n </div>\n </ng-container>\n</ng-template>\n\n<ng-template #LOCAL_OPTIONS>\n <ng-container class=\"box\" *ngIf=\"injectionOptions?.LocalStorageOptions\">\n <div class=\"box\">\n <h3 style=\"font-weight: bold;\">Injection Token Detected - LocalStorage Options</h3>\n {{ injectionOptions?.LocalStorageOptions| json }}\n </div>\n </ng-container>\n</ng-template>\n\n\n", styles: [".box{padding:1rem;background-color:#f5f5f5;border:thin gray solid;margin-top:1rem}\n"] }]
2918
4091
  }], ctorParameters: () => [{ type: ConfigOptions, decorators: [{
2919
4092
  type: Inject,
2920
4093
  args: [CONFIG_SETTINGS_TOKEN]
@@ -2993,9 +4166,9 @@ class HttpRequestManagerModule {
2993
4166
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: HttpRequestManagerModule, declarations: [HttpRequestServicesDemoComponent,
2994
4167
  RequestManagerStateDemoComponent,
2995
4168
  RequestManagerDemoComponent,
2996
- LocalStorageDemoComponent
2997
- // DatabaseDataDemoComponent,
2998
- ], imports: [CommonModule,
4169
+ RequestSignalsManagerDemoComponent,
4170
+ LocalStorageDemoComponent,
4171
+ LocalStorageSignalsDemoComponent], imports: [CommonModule,
2999
4172
  ToastMessageDisplayModule,
3000
4173
  FormsModule,
3001
4174
  ReactiveFormsModule,
@@ -3014,7 +4187,19 @@ class HttpRequestManagerModule {
3014
4187
  MatInputModule,
3015
4188
  MatToolbarModule,
3016
4189
  MatSlideToggleModule, i1.TranslateModule, MatSidenavModule,
3017
- FileDownloaderModule], exports: [HttpRequestServicesDemoComponent] }); }
4190
+ FileDownloaderModule], exports: [HttpRequestServicesDemoComponent,
4191
+ // Re-export commonly used modules so demo component templates have access to
4192
+ // Angular common pipes and Material components when the library is imported.
4193
+ CommonModule,
4194
+ MatButtonToggleModule,
4195
+ MatFormFieldModule,
4196
+ MatSelectModule,
4197
+ MatInputModule,
4198
+ MatIconModule,
4199
+ MatTableModule,
4200
+ MatSlideToggleModule,
4201
+ MatProgressBarModule,
4202
+ MatDividerModule] }); }
3018
4203
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: HttpRequestManagerModule, providers: [
3019
4204
  { provide: HTTP_INTERCEPTORS, useClass: WithCredentialsInterceptor, multi: true },
3020
4205
  { provide: HTTP_INTERCEPTORS, useClass: RequestHeadersInterceptor, multi: true },
@@ -3042,7 +4227,19 @@ class HttpRequestManagerModule {
3042
4227
  MatSlideToggleModule,
3043
4228
  TranslateModule.forRoot(),
3044
4229
  MatSidenavModule,
3045
- FileDownloaderModule] }); }
4230
+ FileDownloaderModule,
4231
+ // Re-export commonly used modules so demo component templates have access to
4232
+ // Angular common pipes and Material components when the library is imported.
4233
+ CommonModule,
4234
+ MatButtonToggleModule,
4235
+ MatFormFieldModule,
4236
+ MatSelectModule,
4237
+ MatInputModule,
4238
+ MatIconModule,
4239
+ MatTableModule,
4240
+ MatSlideToggleModule,
4241
+ MatProgressBarModule,
4242
+ MatDividerModule] }); }
3046
4243
  }
3047
4244
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: HttpRequestManagerModule, decorators: [{
3048
4245
  type: NgModule,
@@ -3075,11 +4272,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
3075
4272
  HttpRequestServicesDemoComponent,
3076
4273
  RequestManagerStateDemoComponent,
3077
4274
  RequestManagerDemoComponent,
3078
- LocalStorageDemoComponent
4275
+ RequestSignalsManagerDemoComponent,
4276
+ LocalStorageDemoComponent,
4277
+ LocalStorageSignalsDemoComponent,
3079
4278
  // DatabaseDataDemoComponent,
3080
4279
  ],
3081
4280
  exports: [
3082
4281
  HttpRequestServicesDemoComponent,
4282
+ // Re-export commonly used modules so demo component templates have access to
4283
+ // Angular common pipes and Material components when the library is imported.
4284
+ CommonModule,
4285
+ MatButtonToggleModule,
4286
+ MatFormFieldModule,
4287
+ MatSelectModule,
4288
+ MatInputModule,
4289
+ MatIconModule,
4290
+ MatTableModule,
4291
+ MatSlideToggleModule,
4292
+ MatProgressBarModule,
4293
+ MatDividerModule,
3083
4294
  ],
3084
4295
  providers: [
3085
4296
  { provide: HTTP_INTERCEPTORS, useClass: WithCredentialsInterceptor, multi: true },
@@ -3440,5 +4651,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
3440
4651
  * Generated bundle index. Do not edit.
3441
4652
  */
3442
4653
 
3443
- export { ApiRequest, AppService, AsymmetricalEncryptionService, ConfigHTTPOptions, ConfigOptions, DataType, DatabaseDataDemoComponent, DatabaseStorage, ErrorDisplaySettings, GlobalStoreOptions, HTTPManagerService, HTTPManagerStateService, HeadersService, HttpRequestManagerModule, HttpRequestServicesDemoComponent, LocalStorageDemoComponent, LocalStorageManagerService, LocalStorageOptions, PathQueryService, Random, RandomNumber, RandomNumbers, RandomNumbersUnique, RandomSignature, RandomStr, RequestErrorInterceptor, RequestHeadersInterceptor, RequestManagerDemoComponent, RequestManagerStateDemoComponent, RequestOptions, RetryOptions, SettingOptions, StorageData, StorageOption, StorageType, SymmetricalEncryptionService, UUID, UtilsService, WithCredentialsInterceptor, countdown, delayedRetry, requestPolling, requestStreaming };
4654
+ export { ApiRequest, AppService, AsymmetricalEncryptionService, ConfigHTTPOptions, ConfigOptions, DataType, DatabaseDataDemoComponent, DatabaseStorage, ErrorDisplaySettings, GlobalStoreOptions, HTTPManagerService, HTTPManagerSignalsService, HTTPManagerStateService, HeadersService, HttpRequestManagerModule, HttpRequestServicesDemoComponent, LocalStorageDemoComponent, LocalStorageManagerService, LocalStorageOptions, LocalStorageSignalsManagerService, PathQueryService, Random, RandomNumber, RandomNumbers, RandomNumbersUnique, RandomSignature, RandomStr, RequestErrorInterceptor, RequestHeadersInterceptor, RequestManagerDemoComponent, RequestManagerStateDemoComponent, RequestOptions, RetryOptions, SettingOptions, StorageData, StorageOption, StorageType, SymmetricalEncryptionService, UUID, UtilsService, WithCredentialsInterceptor, countdown, delayedRetry, requestPolling, requestStreaming };
3444
4655
  //# sourceMappingURL=http-request-manager.mjs.map