http-request-manager 18.13.0 → 18.13.1
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { inject, Injectable, APP_ID, Inject, InjectionToken, isDevMode, signal, effect, computed, Injector, Optional, EventEmitter, Input, Output, ViewEncapsulation, Component, NgModule, ViewChild } from '@angular/core';
|
|
3
3
|
import { ComponentStore } from '@ngrx/component-store';
|
|
4
|
-
import { map, catchError, filter, tap, finalize, takeWhile, retry, startWith, mergeMap, takeUntil, concatMap, toArray, withLatestFrom, switchMap, delay,
|
|
4
|
+
import { map, catchError, take, filter, tap, finalize, takeWhile, retry, startWith, mergeMap, takeUntil, concatMap, toArray, withLatestFrom, switchMap, delay, scan, distinctUntilChanged } from 'rxjs/operators';
|
|
5
5
|
import { HttpClient, HttpHeaders, HttpEventType, HttpHeaderResponse, HttpErrorResponse, HTTP_INTERCEPTORS } from '@angular/common/http';
|
|
6
6
|
import * as CryptoJS from 'crypto-js';
|
|
7
7
|
import { from, BehaviorSubject, EMPTY, throwError, defer, interval, timer, Subject, of, merge, Subscription, take as take$1, catchError as catchError$1, map as map$1, tap as tap$1, switchMap as switchMap$1, startWith as startWith$1, distinctUntilChanged as distinctUntilChanged$1, combineLatest, filter as filter$1, takeUntil as takeUntil$1, ReplaySubject } from 'rxjs';
|
|
@@ -875,6 +875,369 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
875
875
|
}]
|
|
876
876
|
}], ctorParameters: () => [] });
|
|
877
877
|
|
|
878
|
+
class PathTrackerStateModel {
|
|
879
|
+
constructor(baselineQuery, consumedValuesByKey = {}, watchExpiresAt) {
|
|
880
|
+
this.baselineQuery = baselineQuery;
|
|
881
|
+
this.consumedValuesByKey = consumedValuesByKey;
|
|
882
|
+
this.watchExpiresAt = watchExpiresAt;
|
|
883
|
+
}
|
|
884
|
+
static adapt(item) {
|
|
885
|
+
return new PathTrackerStateModel(item?.baselineQuery, item?.consumedValuesByKey, item?.watchExpiresAt);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
class QueryTrackerStateModel {
|
|
890
|
+
constructor(paths = {}) {
|
|
891
|
+
this.paths = paths;
|
|
892
|
+
}
|
|
893
|
+
static adapt(item) {
|
|
894
|
+
return new QueryTrackerStateModel(item?.paths
|
|
895
|
+
? Object.keys(item.paths).reduce((acc, key) => {
|
|
896
|
+
acc[key] = PathTrackerStateModel.adapt(item.paths[key]);
|
|
897
|
+
return acc;
|
|
898
|
+
}, {})
|
|
899
|
+
: {});
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
const TRACKER_STORE_NAME = 'query_params_tracker';
|
|
904
|
+
const TRACKER_SESSION_INIT_KEY = 'query_params_tracker_initialized';
|
|
905
|
+
const DEFAULT_TRACKER_OPTIONS = SettingOptions.adapt({
|
|
906
|
+
storage: StorageType.GLOBAL,
|
|
907
|
+
encrypted: false,
|
|
908
|
+
});
|
|
909
|
+
class QueryParamsTrackerService {
|
|
910
|
+
constructor() {
|
|
911
|
+
this.state = { paths: {} };
|
|
912
|
+
this.stateRestored = false;
|
|
913
|
+
this.localStorageManager = inject(LocalStorageManagerService);
|
|
914
|
+
}
|
|
915
|
+
clearTracking(resetSessionInit = false) {
|
|
916
|
+
this.state = { paths: {} };
|
|
917
|
+
this.localStorageManager.deleteStore({ name: TRACKER_STORE_NAME });
|
|
918
|
+
if (resetSessionInit && this.hasSessionStorage()) {
|
|
919
|
+
sessionStorage.removeItem(TRACKER_SESSION_INIT_KEY);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
checkRequestOptions(requestOptions = [], options = {}) {
|
|
923
|
+
this.ensureStateRestored();
|
|
924
|
+
const normalized = this.normalizeRequestOptions(requestOptions);
|
|
925
|
+
if (!normalized.pathKey)
|
|
926
|
+
return false;
|
|
927
|
+
this.cleanupExpiredEntries();
|
|
928
|
+
if (!normalized.hasQuery) {
|
|
929
|
+
this.ensurePathState(normalized.pathKey);
|
|
930
|
+
this.persistState();
|
|
931
|
+
return true;
|
|
932
|
+
}
|
|
933
|
+
if (options.mode === 'exact') {
|
|
934
|
+
return this.checkExact(normalized, options);
|
|
935
|
+
}
|
|
936
|
+
return this.checkVariation(normalized, options);
|
|
937
|
+
}
|
|
938
|
+
matchesPath(requestOptions = [], expectedPathOptions = []) {
|
|
939
|
+
this.ensureStateRestored();
|
|
940
|
+
const requestPath = this.normalizeRequestOptions(requestOptions).pathKey;
|
|
941
|
+
const expectedPath = this.normalizeRequestOptions(expectedPathOptions).pathKey;
|
|
942
|
+
return Boolean(requestPath) && requestPath === expectedPath;
|
|
943
|
+
}
|
|
944
|
+
checkExact(normalized, options) {
|
|
945
|
+
const pathState = this.ensurePathState(normalized.pathKey);
|
|
946
|
+
const filteredQuery = normalized.query;
|
|
947
|
+
if (Object.keys(filteredQuery).length === 0) {
|
|
948
|
+
return false;
|
|
949
|
+
}
|
|
950
|
+
if (!pathState.baselineQuery) {
|
|
951
|
+
pathState.baselineQuery = filteredQuery;
|
|
952
|
+
this.persistState();
|
|
953
|
+
return true;
|
|
954
|
+
}
|
|
955
|
+
return this.stringifyQuery(pathState.baselineQuery) === this.stringifyQuery(filteredQuery);
|
|
956
|
+
}
|
|
957
|
+
checkVariation(normalized, options) {
|
|
958
|
+
const pathState = this.ensurePathState(normalized.pathKey);
|
|
959
|
+
const filteredQuery = normalized.query;
|
|
960
|
+
const keys = Object.keys(filteredQuery);
|
|
961
|
+
if (keys.length === 0) {
|
|
962
|
+
return false;
|
|
963
|
+
}
|
|
964
|
+
this.resetPathStateIfExpired(pathState);
|
|
965
|
+
let accepted = false;
|
|
966
|
+
keys.forEach((key) => {
|
|
967
|
+
const currentValue = filteredQuery[key];
|
|
968
|
+
const consumed = pathState.consumedValuesByKey[key] || [];
|
|
969
|
+
if (!consumed.includes(currentValue)) {
|
|
970
|
+
pathState.consumedValuesByKey[key] = [...consumed, currentValue];
|
|
971
|
+
accepted = true;
|
|
972
|
+
}
|
|
973
|
+
});
|
|
974
|
+
if (accepted) {
|
|
975
|
+
if (!pathState.baselineQuery) {
|
|
976
|
+
pathState.baselineQuery = filteredQuery;
|
|
977
|
+
}
|
|
978
|
+
pathState.watchExpiresAt = this.buildExpiryEpoch(typeof options.watchExpiresAt !== 'undefined' ? options.watchExpiresAt : options.watchParamsExpire);
|
|
979
|
+
this.persistState();
|
|
980
|
+
}
|
|
981
|
+
return accepted;
|
|
982
|
+
}
|
|
983
|
+
normalizeRequestOptions(requestOptions) {
|
|
984
|
+
const params = Array.isArray(requestOptions) ? requestOptions : [];
|
|
985
|
+
const pathSegments = [];
|
|
986
|
+
const queryObjects = [];
|
|
987
|
+
params.forEach((item) => {
|
|
988
|
+
if (this.isPlainObject(item)) {
|
|
989
|
+
queryObjects.push(item);
|
|
990
|
+
}
|
|
991
|
+
else if (typeof item !== 'undefined' && item !== null) {
|
|
992
|
+
const parsed = this.parsePathSegment(String(item));
|
|
993
|
+
if (parsed.pathSegment) {
|
|
994
|
+
pathSegments.push(parsed.pathSegment);
|
|
995
|
+
}
|
|
996
|
+
if (Object.keys(parsed.queryObject).length > 0) {
|
|
997
|
+
queryObjects.push(parsed.queryObject);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
const query = queryObjects.reduce((acc, current) => {
|
|
1002
|
+
Object.keys(current).forEach((key) => {
|
|
1003
|
+
const normalizedKey = this.normalizeParamKey(key);
|
|
1004
|
+
const value = current[key];
|
|
1005
|
+
if (!normalizedKey || typeof value === 'undefined' || value === null || value === '') {
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
acc[normalizedKey] = this.normalizeParamValue(value);
|
|
1009
|
+
});
|
|
1010
|
+
return acc;
|
|
1011
|
+
}, {});
|
|
1012
|
+
return {
|
|
1013
|
+
pathKey: this.normalizePath(pathSegments),
|
|
1014
|
+
query,
|
|
1015
|
+
hasQuery: Object.keys(query).length > 0,
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
parsePathSegment(rawSegment) {
|
|
1019
|
+
const [pathPart, ...queryParts] = String(rawSegment).split('?');
|
|
1020
|
+
const queryString = queryParts.join('?');
|
|
1021
|
+
const queryObject = {};
|
|
1022
|
+
if (queryString) {
|
|
1023
|
+
queryString.split('&').forEach((pair) => {
|
|
1024
|
+
if (!pair)
|
|
1025
|
+
return;
|
|
1026
|
+
const [rawKey, ...rawValueParts] = pair.split('=');
|
|
1027
|
+
const key = this.safeDecode(rawKey).trim();
|
|
1028
|
+
const value = this.safeDecode(rawValueParts.join('=')).trim();
|
|
1029
|
+
if (!key)
|
|
1030
|
+
return;
|
|
1031
|
+
queryObject[key] = value;
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
return {
|
|
1035
|
+
pathSegment: pathPart,
|
|
1036
|
+
queryObject,
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
safeDecode(value) {
|
|
1040
|
+
if (typeof value === 'undefined') {
|
|
1041
|
+
return '';
|
|
1042
|
+
}
|
|
1043
|
+
try {
|
|
1044
|
+
return decodeURIComponent(value);
|
|
1045
|
+
}
|
|
1046
|
+
catch {
|
|
1047
|
+
return String(value);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
normalizePath(pathSegments) {
|
|
1051
|
+
return pathSegments
|
|
1052
|
+
.map((segment) => String(segment).trim())
|
|
1053
|
+
.filter((segment) => segment.length > 0)
|
|
1054
|
+
.join('/')
|
|
1055
|
+
.replace(/([^:]\/+)\/+/g, '$1')
|
|
1056
|
+
.replace(/^\//, '')
|
|
1057
|
+
.replace(/\/$/, '');
|
|
1058
|
+
}
|
|
1059
|
+
normalizeParamKey(key) {
|
|
1060
|
+
return String(key).trim().toLowerCase();
|
|
1061
|
+
}
|
|
1062
|
+
normalizeParamValue(value) {
|
|
1063
|
+
if (Array.isArray(value)) {
|
|
1064
|
+
return value.map((item) => this.normalizeParamValue(item)).join(',');
|
|
1065
|
+
}
|
|
1066
|
+
if (this.isPlainObject(value)) {
|
|
1067
|
+
return this.stringifyQuery(Object.keys(value).reduce((acc, key) => {
|
|
1068
|
+
acc[this.normalizeParamKey(key)] = this.normalizeParamValue(value[key]);
|
|
1069
|
+
return acc;
|
|
1070
|
+
}, {}));
|
|
1071
|
+
}
|
|
1072
|
+
return String(value).trim().toLowerCase();
|
|
1073
|
+
}
|
|
1074
|
+
buildExpiryEpoch(expireIn) {
|
|
1075
|
+
if (typeof expireIn === 'undefined' || expireIn === null || expireIn === '') {
|
|
1076
|
+
return undefined;
|
|
1077
|
+
}
|
|
1078
|
+
if (typeof expireIn === 'number' && Number.isFinite(expireIn) && expireIn > 0) {
|
|
1079
|
+
return Math.floor(Date.now() / 1000) + Math.floor(expireIn);
|
|
1080
|
+
}
|
|
1081
|
+
const raw = String(expireIn).trim().toLowerCase().replace(/\s+/g, '');
|
|
1082
|
+
const parsed = raw.match(/^(\d+)(y|w|d|hr|h|mn|min|m|s)$/);
|
|
1083
|
+
if (!parsed) {
|
|
1084
|
+
return undefined;
|
|
1085
|
+
}
|
|
1086
|
+
const value = Number(parsed[1]);
|
|
1087
|
+
const unit = parsed[2];
|
|
1088
|
+
const toSeconds = {
|
|
1089
|
+
y: 31556926,
|
|
1090
|
+
w: 604800,
|
|
1091
|
+
d: 86400,
|
|
1092
|
+
hr: 3600,
|
|
1093
|
+
h: 3600,
|
|
1094
|
+
mn: 60,
|
|
1095
|
+
min: 60,
|
|
1096
|
+
m: 60,
|
|
1097
|
+
s: 1,
|
|
1098
|
+
};
|
|
1099
|
+
const seconds = toSeconds[unit];
|
|
1100
|
+
if (!seconds) {
|
|
1101
|
+
return undefined;
|
|
1102
|
+
}
|
|
1103
|
+
return Math.floor(Date.now() / 1000) + value * seconds;
|
|
1104
|
+
}
|
|
1105
|
+
resetPathStateIfExpired(pathState) {
|
|
1106
|
+
const now = Math.floor(Date.now() / 1000);
|
|
1107
|
+
if (pathState.watchExpiresAt && pathState.watchExpiresAt <= now) {
|
|
1108
|
+
pathState.consumedValuesByKey = {};
|
|
1109
|
+
pathState.watchExpiresAt = undefined;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
cleanupExpiredEntries() {
|
|
1113
|
+
const now = Math.floor(Date.now() / 1000);
|
|
1114
|
+
Object.keys(this.state.paths).forEach((pathKey) => {
|
|
1115
|
+
const pathState = this.state.paths[pathKey];
|
|
1116
|
+
if (pathState.watchExpiresAt && pathState.watchExpiresAt <= now) {
|
|
1117
|
+
pathState.consumedValuesByKey = {};
|
|
1118
|
+
pathState.watchExpiresAt = undefined;
|
|
1119
|
+
}
|
|
1120
|
+
const hasBaseline = Boolean(pathState.baselineQuery && Object.keys(pathState.baselineQuery).length > 0);
|
|
1121
|
+
const hasTrackedValues = Object.keys(pathState.consumedValuesByKey).some((key) => (pathState.consumedValuesByKey[key] || []).length > 0);
|
|
1122
|
+
if (!hasBaseline && !hasTrackedValues) {
|
|
1123
|
+
delete this.state.paths[pathKey];
|
|
1124
|
+
}
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
ensurePathState(pathKey) {
|
|
1128
|
+
if (!this.state.paths[pathKey]) {
|
|
1129
|
+
this.state.paths[pathKey] = {
|
|
1130
|
+
consumedValuesByKey: {},
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1133
|
+
return this.state.paths[pathKey];
|
|
1134
|
+
}
|
|
1135
|
+
ensureStateRestored() {
|
|
1136
|
+
if (this.stateRestored) {
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
this.stateRestored = true;
|
|
1140
|
+
this.initializeTrackingForSession();
|
|
1141
|
+
this.restoreState();
|
|
1142
|
+
}
|
|
1143
|
+
initializeTrackingForSession() {
|
|
1144
|
+
if (!this.hasSessionStorage()) {
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
const initialized = sessionStorage.getItem(TRACKER_SESSION_INIT_KEY);
|
|
1148
|
+
if (initialized === '1') {
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
this.clearTracking();
|
|
1152
|
+
sessionStorage.setItem(TRACKER_SESSION_INIT_KEY, '1');
|
|
1153
|
+
}
|
|
1154
|
+
restoreState() {
|
|
1155
|
+
this.localStorageManager.store$(TRACKER_STORE_NAME)
|
|
1156
|
+
.pipe(take(1))
|
|
1157
|
+
.subscribe({
|
|
1158
|
+
next: (storedState) => {
|
|
1159
|
+
if (this.isTrackerState(storedState)) {
|
|
1160
|
+
this.state = QueryTrackerStateModel.adapt(storedState);
|
|
1161
|
+
this.cleanupExpiredEntries();
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
this.state = { paths: {} };
|
|
1165
|
+
},
|
|
1166
|
+
error: () => {
|
|
1167
|
+
this.state = { paths: {} };
|
|
1168
|
+
}
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
persistState() {
|
|
1172
|
+
this.localStorageManager.storeExists$(TRACKER_STORE_NAME)
|
|
1173
|
+
.pipe(take(1))
|
|
1174
|
+
.subscribe((exists) => {
|
|
1175
|
+
if (exists) {
|
|
1176
|
+
this.localStorageManager.updateStore({ name: TRACKER_STORE_NAME, data: this.state });
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1179
|
+
this.localStorageManager.createStore({
|
|
1180
|
+
name: TRACKER_STORE_NAME,
|
|
1181
|
+
data: this.state,
|
|
1182
|
+
options: DEFAULT_TRACKER_OPTIONS,
|
|
1183
|
+
});
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
1186
|
+
stringifyQuery(query) {
|
|
1187
|
+
return JSON.stringify(Object.keys(query)
|
|
1188
|
+
.sort()
|
|
1189
|
+
.reduce((acc, key) => {
|
|
1190
|
+
acc[key] = query[key];
|
|
1191
|
+
return acc;
|
|
1192
|
+
}, {}));
|
|
1193
|
+
}
|
|
1194
|
+
isTrackerState(value) {
|
|
1195
|
+
return this.isPlainObject(value) && this.isPlainObject(value.paths);
|
|
1196
|
+
}
|
|
1197
|
+
isPlainObject(value) {
|
|
1198
|
+
return Object.prototype.toString.call(value) === '[object Object]';
|
|
1199
|
+
}
|
|
1200
|
+
hasSessionStorage() {
|
|
1201
|
+
try {
|
|
1202
|
+
return typeof sessionStorage !== 'undefined';
|
|
1203
|
+
}
|
|
1204
|
+
catch {
|
|
1205
|
+
return false;
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: QueryParamsTrackerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1209
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: QueryParamsTrackerService, providedIn: 'root' }); }
|
|
1210
|
+
}
|
|
1211
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: QueryParamsTrackerService, decorators: [{
|
|
1212
|
+
type: Injectable,
|
|
1213
|
+
args: [{
|
|
1214
|
+
providedIn: 'root'
|
|
1215
|
+
}]
|
|
1216
|
+
}] });
|
|
1217
|
+
|
|
1218
|
+
class NormalizedRequestOptionsModel {
|
|
1219
|
+
constructor(pathKey = '', query = {}, hasQuery = false) {
|
|
1220
|
+
this.pathKey = pathKey;
|
|
1221
|
+
this.query = query;
|
|
1222
|
+
this.hasQuery = hasQuery;
|
|
1223
|
+
}
|
|
1224
|
+
static adapt(item) {
|
|
1225
|
+
return new NormalizedRequestOptionsModel(item?.pathKey, item?.query, item?.hasQuery);
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
class QueryParamsTrackerOptionsModel {
|
|
1230
|
+
constructor(mode, watchParams, watchExpiresAt, watchParamsExpire) {
|
|
1231
|
+
this.mode = mode;
|
|
1232
|
+
this.watchParams = watchParams;
|
|
1233
|
+
this.watchExpiresAt = watchExpiresAt;
|
|
1234
|
+
this.watchParamsExpire = watchParamsExpire;
|
|
1235
|
+
}
|
|
1236
|
+
static adapt(item) {
|
|
1237
|
+
return new QueryParamsTrackerOptionsModel(item?.mode, Array.isArray(item?.watchParams) ? item.watchParams : [], item?.watchExpiresAt, item?.watchParamsExpire);
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
|
|
878
1241
|
/**
|
|
879
1242
|
* Stateful processor for managing streaming data and events
|
|
880
1243
|
*/
|
|
@@ -883,28 +1246,66 @@ class StreamingProcessor {
|
|
|
883
1246
|
this.buffer = '';
|
|
884
1247
|
this.contentType = '';
|
|
885
1248
|
this.maxBufferSize = 10 * 1024 * 1024; // 10MB limit
|
|
1249
|
+
this.lastParsedLength = 0; // Track parsed position to avoid duplicates
|
|
886
1250
|
this.streamConfig = config || { streamType: StreamType.AI_STREAMING };
|
|
887
1251
|
}
|
|
888
1252
|
/**
|
|
889
|
-
* Process HTTP events and extract streaming data
|
|
1253
|
+
* Process HTTP events and extract streaming data with progress
|
|
890
1254
|
*/
|
|
891
1255
|
process(event) {
|
|
892
1256
|
switch (event.type) {
|
|
893
1257
|
case HttpEventType.ResponseHeader:
|
|
894
1258
|
this.contentType = event.headers?.get('content-type') || '';
|
|
1259
|
+
// Read total from response header if configured
|
|
1260
|
+
if (this.streamConfig.totalHeader && event.headers) {
|
|
1261
|
+
const totalVal = event.headers.get(this.streamConfig.totalHeader);
|
|
1262
|
+
if (totalVal !== undefined && totalVal !== null) {
|
|
1263
|
+
const parsed = parseInt(totalVal, 10);
|
|
1264
|
+
if (!isNaN(parsed)) {
|
|
1265
|
+
this.totalFromHeader = parsed;
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
895
1269
|
return null;
|
|
896
1270
|
case HttpEventType.DownloadProgress:
|
|
897
1271
|
if (event.partialText) {
|
|
898
1272
|
this.appendToBuffer(event.partialText);
|
|
899
1273
|
const parsedData = this.parseBuffer();
|
|
900
|
-
return
|
|
1274
|
+
// Only return NEW items since last parse (progressive updates)
|
|
1275
|
+
const newItems = parsedData.slice(this.lastParsedLength);
|
|
1276
|
+
this.lastParsedLength = parsedData.length;
|
|
1277
|
+
// Calculate progress
|
|
1278
|
+
const progress = {
|
|
1279
|
+
received: this.lastParsedLength,
|
|
1280
|
+
total: this.totalFromHeader,
|
|
1281
|
+
percent: this.totalFromHeader
|
|
1282
|
+
? Math.round((this.lastParsedLength / this.totalFromHeader) * 100)
|
|
1283
|
+
: 0,
|
|
1284
|
+
stage: 'streaming'
|
|
1285
|
+
};
|
|
1286
|
+
return {
|
|
1287
|
+
data: newItems.length > 0 ? newItems : [],
|
|
1288
|
+
progress
|
|
1289
|
+
};
|
|
901
1290
|
}
|
|
902
1291
|
return null;
|
|
903
1292
|
case HttpEventType.Response:
|
|
904
1293
|
if (event.body) {
|
|
905
1294
|
this.appendToBuffer(event.body);
|
|
906
1295
|
const parsedData = this.parseBuffer();
|
|
907
|
-
|
|
1296
|
+
// Calculate final progress
|
|
1297
|
+
const progress = {
|
|
1298
|
+
received: parsedData.length,
|
|
1299
|
+
total: this.totalFromHeader,
|
|
1300
|
+
percent: this.totalFromHeader
|
|
1301
|
+
? Math.round((parsedData.length / this.totalFromHeader) * 100)
|
|
1302
|
+
: 100,
|
|
1303
|
+
stage: 'complete'
|
|
1304
|
+
};
|
|
1305
|
+
return {
|
|
1306
|
+
data: parsedData.length > 0 ? parsedData : [],
|
|
1307
|
+
progress
|
|
1308
|
+
};
|
|
908
1309
|
}
|
|
909
1310
|
return null;
|
|
910
1311
|
default:
|
|
@@ -943,6 +1344,8 @@ class StreamingProcessor {
|
|
|
943
1344
|
reset() {
|
|
944
1345
|
this.buffer = '';
|
|
945
1346
|
this.contentType = '';
|
|
1347
|
+
this.lastParsedLength = 0;
|
|
1348
|
+
this.totalFromHeader = undefined;
|
|
946
1349
|
}
|
|
947
1350
|
/**
|
|
948
1351
|
* Update configuration
|
|
@@ -2793,19 +3196,6 @@ class UploadValidationErrorModel {
|
|
|
2793
3196
|
}
|
|
2794
3197
|
}
|
|
2795
3198
|
|
|
2796
|
-
class UserData {
|
|
2797
|
-
constructor(ldap = '', name = '', email = '', color = RandomPaletteColor()) {
|
|
2798
|
-
this.ldap = ldap;
|
|
2799
|
-
this.name = name;
|
|
2800
|
-
this.email = email;
|
|
2801
|
-
this.color = color;
|
|
2802
|
-
}
|
|
2803
|
-
static adapt(item) {
|
|
2804
|
-
const userName = `${item?.first_name} ${item?.last_name}`;
|
|
2805
|
-
return new UserData(item?.ldap, userName, item?.email, item?.color);
|
|
2806
|
-
}
|
|
2807
|
-
}
|
|
2808
|
-
|
|
2809
3199
|
class RequestService extends WebsocketService {
|
|
2810
3200
|
constructor() {
|
|
2811
3201
|
super(...arguments);
|
|
@@ -2816,6 +3206,13 @@ class RequestService extends WebsocketService {
|
|
|
2816
3206
|
this.isPending$ = this.isPending.asObservable();
|
|
2817
3207
|
this.progress = new BehaviorSubject(0);
|
|
2818
3208
|
this.progress$ = this.progress.asObservable();
|
|
3209
|
+
this.streamProgress = new BehaviorSubject({
|
|
3210
|
+
received: 0,
|
|
3211
|
+
total: undefined,
|
|
3212
|
+
percent: 0,
|
|
3213
|
+
stage: 'connecting'
|
|
3214
|
+
});
|
|
3215
|
+
this.streamProgress$ = this.streamProgress.asObservable();
|
|
2819
3216
|
}
|
|
2820
3217
|
// Implementation
|
|
2821
3218
|
getRecordRequest(options) {
|
|
@@ -2884,7 +3281,12 @@ class RequestService extends WebsocketService {
|
|
|
2884
3281
|
}
|
|
2885
3282
|
requestStreaming(options) {
|
|
2886
3283
|
return (source$) => {
|
|
2887
|
-
return source$.pipe(
|
|
3284
|
+
return source$.pipe(tap(output => {
|
|
3285
|
+
// Update progress from stream output
|
|
3286
|
+
this.progress.next(output.progress.received);
|
|
3287
|
+
this.streamProgress.next(output.progress);
|
|
3288
|
+
}), map(output => {
|
|
3289
|
+
const data = output.data;
|
|
2888
3290
|
if (!data || (Array.isArray(data) && data.length === 0)) {
|
|
2889
3291
|
return data;
|
|
2890
3292
|
}
|
|
@@ -3586,6 +3988,7 @@ class HTTPManagerService extends RequestService {
|
|
|
3586
3988
|
this.data$ = this.data.asObservable();
|
|
3587
3989
|
this.polling$ = new Subject();
|
|
3588
3990
|
this.config = ApiRequest.adapt();
|
|
3991
|
+
this.streamProgress$ = this.streamProgress.asObservable();
|
|
3589
3992
|
this.config = (configOptions) ? ApiRequest.adapt(configOptions.httpRequestOptions) : this.config;
|
|
3590
3993
|
}
|
|
3591
3994
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -3722,6 +4125,12 @@ class HTTPManagerService extends RequestService {
|
|
|
3722
4125
|
getRequest(options, params) {
|
|
3723
4126
|
this.isPending.next(true);
|
|
3724
4127
|
this.data.next(null);
|
|
4128
|
+
this.streamProgress.next({
|
|
4129
|
+
received: 0,
|
|
4130
|
+
total: undefined,
|
|
4131
|
+
percent: 0,
|
|
4132
|
+
stage: 'connecting'
|
|
4133
|
+
});
|
|
3725
4134
|
const updatedOptions = this.defineReqOptions(options, params);
|
|
3726
4135
|
const func = this.getRecordRequest;
|
|
3727
4136
|
const requests = this.createRequest(func, updatedOptions);
|
|
@@ -3730,9 +4139,19 @@ class HTTPManagerService extends RequestService {
|
|
|
3730
4139
|
this.data.next(data);
|
|
3731
4140
|
if (updatedOptions.displaySuccess)
|
|
3732
4141
|
this.handleSuccessWithSnackBar(updatedOptions.successMessage);
|
|
3733
|
-
})
|
|
4142
|
+
}), finalize(() => {
|
|
4143
|
+
this.streamProgress.next({
|
|
4144
|
+
...this.streamProgress.value,
|
|
4145
|
+
stage: 'complete'
|
|
4146
|
+
});
|
|
4147
|
+
this.isPending.next(false);
|
|
4148
|
+
}), catchError((err) => {
|
|
3734
4149
|
if (updatedOptions.displayError)
|
|
3735
4150
|
this.handleErrorWithSnackBar(err, updatedOptions.errorMessage || err?.message);
|
|
4151
|
+
this.streamProgress.next({
|
|
4152
|
+
...this.streamProgress.value,
|
|
4153
|
+
stage: 'error'
|
|
4154
|
+
});
|
|
3736
4155
|
this.isPending.next(false);
|
|
3737
4156
|
return this.handleError(err);
|
|
3738
4157
|
}));
|
|
@@ -4154,7 +4573,8 @@ class RequestSignalsService extends WebsocketService {
|
|
|
4154
4573
|
}
|
|
4155
4574
|
requestStreamingOperator(options) {
|
|
4156
4575
|
return (source$) => {
|
|
4157
|
-
return source$.pipe(map(
|
|
4576
|
+
return source$.pipe(map(output => {
|
|
4577
|
+
const data = output.data;
|
|
4158
4578
|
if (!data || (Array.isArray(data) && data.length === 0)) {
|
|
4159
4579
|
return data;
|
|
4160
4580
|
}
|
|
@@ -4891,12 +5311,15 @@ class ApiRequest {
|
|
|
4891
5311
|
}
|
|
4892
5312
|
|
|
4893
5313
|
class RequestOptions {
|
|
4894
|
-
constructor(path = [], headers = {}) {
|
|
5314
|
+
constructor(path = [], headers = {}, forceRefresh, watchParams, watchExpiresAt) {
|
|
4895
5315
|
this.path = path;
|
|
4896
5316
|
this.headers = headers;
|
|
5317
|
+
this.forceRefresh = forceRefresh;
|
|
5318
|
+
this.watchParams = watchParams;
|
|
5319
|
+
this.watchExpiresAt = watchExpiresAt;
|
|
4897
5320
|
}
|
|
4898
5321
|
static adapt(item) {
|
|
4899
|
-
return new RequestOptions(item?.path, item?.headers);
|
|
5322
|
+
return new RequestOptions(item?.path, item?.headers, item?.forceRefresh, Array.isArray(item?.watchParams) ? item.watchParams : [], item?.watchExpiresAt);
|
|
4900
5323
|
}
|
|
4901
5324
|
}
|
|
4902
5325
|
|
|
@@ -5691,8 +6114,8 @@ class DbService extends Dexie {
|
|
|
5691
6114
|
console.warn('Database upgrade blocked! Please close other tabs/windows of this app.');
|
|
5692
6115
|
});
|
|
5693
6116
|
this.on('versionchange', () => {
|
|
5694
|
-
console.warn('Database version changed in another tab.
|
|
5695
|
-
|
|
6117
|
+
console.warn('Database version changed in another tab. Closing current database connection to allow upgrade.');
|
|
6118
|
+
this.close();
|
|
5696
6119
|
});
|
|
5697
6120
|
this.dbReady = this.init();
|
|
5698
6121
|
}
|
|
@@ -5772,9 +6195,12 @@ class DbService extends Dexie {
|
|
|
5772
6195
|
await this.dbReady;
|
|
5773
6196
|
const safeTableName = this.cleanTableName(tableName);
|
|
5774
6197
|
const safeSchema = schema.trim();
|
|
5775
|
-
if (this.tableExists(safeTableName))
|
|
5776
|
-
return;
|
|
5777
6198
|
const currentSchema = this.getCurrentSchema();
|
|
6199
|
+
const existingSchema = currentSchema[safeTableName]?.trim();
|
|
6200
|
+
// No-op only when table already exists and schema is identical.
|
|
6201
|
+
if (this.tableExists(safeTableName) && existingSchema === safeSchema) {
|
|
6202
|
+
return;
|
|
6203
|
+
}
|
|
5778
6204
|
console.log('Current Schema before update:', currentSchema);
|
|
5779
6205
|
currentSchema[safeTableName] = safeSchema;
|
|
5780
6206
|
const nextVersion = this.verno + 1;
|
|
@@ -5788,6 +6214,7 @@ class DbService extends Dexie {
|
|
|
5788
6214
|
const created = this.tables.some(t => t.name === safeTableName);
|
|
5789
6215
|
if (!created) {
|
|
5790
6216
|
console.error(`CRITICAL: Table ${safeTableName} was NOT created after upgrade! Tables found:`, this.tables.map(t => t.name));
|
|
6217
|
+
throw new Error(`Table '${safeTableName}' was not created after schema upgrade`);
|
|
5791
6218
|
}
|
|
5792
6219
|
else {
|
|
5793
6220
|
console.log(`Database opened successfully version ${this.verno}. Table ${safeTableName} verified.`);
|
|
@@ -5795,15 +6222,24 @@ class DbService extends Dexie {
|
|
|
5795
6222
|
}
|
|
5796
6223
|
catch (err) {
|
|
5797
6224
|
console.error('Error opening database after schema update:', err);
|
|
6225
|
+
throw err;
|
|
5798
6226
|
}
|
|
5799
6227
|
}
|
|
5800
6228
|
async DBOpened() {
|
|
6229
|
+
try {
|
|
6230
|
+
await this.dbReady;
|
|
6231
|
+
}
|
|
6232
|
+
catch (err) {
|
|
6233
|
+
console.error('DBOpened: init failed', err);
|
|
6234
|
+
return false;
|
|
6235
|
+
}
|
|
5801
6236
|
if (!this.isOpen()) {
|
|
5802
6237
|
try {
|
|
5803
6238
|
await this.open();
|
|
5804
6239
|
return true;
|
|
5805
6240
|
}
|
|
5806
6241
|
catch (err) {
|
|
6242
|
+
console.error('DBOpened: open failed', err);
|
|
5807
6243
|
return false;
|
|
5808
6244
|
}
|
|
5809
6245
|
}
|
|
@@ -5898,7 +6334,11 @@ class DatabaseManagerService extends DbService {
|
|
|
5898
6334
|
}));
|
|
5899
6335
|
}
|
|
5900
6336
|
createDatabaseTable(tableDef) {
|
|
5901
|
-
|
|
6337
|
+
const tableName = this.cleanTableName(tableDef.table);
|
|
6338
|
+
return from(this.createTable(tableDef.table, tableDef.schema)).pipe(switchMap(() => from(this.DBOpened())), map((opened) => opened && this.tableExists(tableName)), catchError((error) => {
|
|
6339
|
+
console.error(`createDatabaseTable: failed for table '${tableName}'`, error);
|
|
6340
|
+
return of(false);
|
|
6341
|
+
}));
|
|
5902
6342
|
}
|
|
5903
6343
|
updateDatabaseTableSchema(tableDef) {
|
|
5904
6344
|
return from(this.createTable(tableDef.table, tableDef.schema)).pipe(switchMap(() => this.getDatabaseTable(tableDef.table)));
|
|
@@ -5945,26 +6385,23 @@ class DatabaseManagerService extends DbService {
|
|
|
5945
6385
|
}
|
|
5946
6386
|
createTableRecords(table, records) {
|
|
5947
6387
|
const tableName = this.cleanTableName(table);
|
|
5948
|
-
return from(this.DBOpened()).pipe(switchMap(() => {
|
|
6388
|
+
return from(this.DBOpened()).pipe(switchMap((opened) => {
|
|
6389
|
+
if (!opened) {
|
|
6390
|
+
console.error(`createTableRecords: DB not open. Cannot write to '${tableName}'.`);
|
|
6391
|
+
return EMPTY;
|
|
6392
|
+
}
|
|
5949
6393
|
if (!this.tables.some(t => t.name === tableName)) {
|
|
5950
6394
|
console.error(`createTableRecords: Table '${tableName}' does not exist in DB version ${this.verno}. Available:`, this.tables.map(t => t.name));
|
|
5951
6395
|
return EMPTY;
|
|
5952
6396
|
}
|
|
5953
6397
|
const tableInstance = this.table(tableName);
|
|
5954
|
-
//
|
|
5955
|
-
const schema = tableInstance.schema;
|
|
5956
|
-
const validFields = [
|
|
5957
|
-
schema.primKey.name,
|
|
5958
|
-
...schema.indexes.map(idx => idx.name)
|
|
5959
|
-
];
|
|
6398
|
+
// Keep full object payload; Dexie stores non-indexed properties as well.
|
|
5960
6399
|
const insertRecords = records.map((record) => {
|
|
5961
|
-
const
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
});
|
|
5967
|
-
return objectFromSchema;
|
|
6400
|
+
const payload = { ...(record || {}) };
|
|
6401
|
+
if (payload.id === undefined || payload.id === null || payload.id === '') {
|
|
6402
|
+
delete payload.id;
|
|
6403
|
+
}
|
|
6404
|
+
return payload;
|
|
5968
6405
|
});
|
|
5969
6406
|
console.log(`createTableRecords: Bulk putting ${insertRecords.length} records into ${tableName}`);
|
|
5970
6407
|
return from(tableInstance.bulkPut(insertRecords)).pipe(map(() => insertRecords));
|
|
@@ -5978,17 +6415,14 @@ class DatabaseManagerService extends DbService {
|
|
|
5978
6415
|
updateTableRecords(table, records) {
|
|
5979
6416
|
const tableName = this.cleanTableName(table);
|
|
5980
6417
|
return this.getDatabaseTable(tableName).pipe(switchMap((tableData) => {
|
|
5981
|
-
|
|
5982
|
-
const
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
});
|
|
5990
|
-
return from(tableData.bulkPut(insertRecords)).pipe(map(() => insertRecords));
|
|
5991
|
-
}));
|
|
6418
|
+
const insertRecords = records.map((record) => {
|
|
6419
|
+
const payload = { ...(record || {}) };
|
|
6420
|
+
if (payload.id === undefined || payload.id === null || payload.id === '') {
|
|
6421
|
+
delete payload.id;
|
|
6422
|
+
}
|
|
6423
|
+
return payload;
|
|
6424
|
+
});
|
|
6425
|
+
return from(tableData.bulkPut(insertRecords)).pipe(map(() => insertRecords));
|
|
5992
6426
|
}));
|
|
5993
6427
|
}
|
|
5994
6428
|
deleteTableRecord(table, id) {
|
|
@@ -6090,6 +6524,7 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6090
6524
|
this.httpManagerService = inject(HTTPManagerService);
|
|
6091
6525
|
this.dbManagerService = inject(DatabaseManagerService);
|
|
6092
6526
|
this.localStorageManagerService = inject(LocalStorageManagerService);
|
|
6527
|
+
this.queryParamsTrackerService = inject(QueryParamsTrackerService);
|
|
6093
6528
|
this.utils = inject(UtilsService);
|
|
6094
6529
|
this.logger = inject(LoggerService);
|
|
6095
6530
|
this.error$ = this.httpManagerService.error$;
|
|
@@ -6106,6 +6541,16 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6106
6541
|
this.hasDatabase = false;
|
|
6107
6542
|
this.streamedResponse = [];
|
|
6108
6543
|
this.shouldRetry = true;
|
|
6544
|
+
this.volatileHeaders = new Set([
|
|
6545
|
+
'authorization',
|
|
6546
|
+
'x-request-id',
|
|
6547
|
+
'x-correlation-id',
|
|
6548
|
+
'x-trace-id',
|
|
6549
|
+
'x-amzn-trace-id',
|
|
6550
|
+
'date',
|
|
6551
|
+
'if-none-match'
|
|
6552
|
+
]);
|
|
6553
|
+
this.requestSignatureCache = {};
|
|
6109
6554
|
this.wsRetryAttempts = new BehaviorSubject(0);
|
|
6110
6555
|
this.wsRetryAttempts$ = this.wsRetryAttempts.asObservable();
|
|
6111
6556
|
this.messages = new BehaviorSubject([]);
|
|
@@ -6375,22 +6820,25 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6375
6820
|
}
|
|
6376
6821
|
}))))));
|
|
6377
6822
|
this.initDBStorage = this.effect((trigger$) => trigger$.pipe(tap(() => {
|
|
6823
|
+
console.log('[initDBStorage effect] Triggered, checking conditions:', {
|
|
6824
|
+
dataType: this.dataType,
|
|
6825
|
+
isARRAY: this.dataType === DataType.ARRAY,
|
|
6826
|
+
hasAdapter: !!this.apiOptions?.adapter,
|
|
6827
|
+
hasTable: !!this.databaseOptions?.table,
|
|
6828
|
+
tableValue: this.databaseOptions?.table
|
|
6829
|
+
});
|
|
6378
6830
|
if (this.dataType !== DataType.ARRAY)
|
|
6379
6831
|
console.warn('Database storage requires dataType to be ARRAY');
|
|
6380
6832
|
if (!this.apiOptions.adapter)
|
|
6381
|
-
console.warn('Database storage
|
|
6833
|
+
console.warn('Database storage adapter missing, using minimal or inferred schema');
|
|
6382
6834
|
if (this.databaseOptions && this.databaseOptions?.table === '')
|
|
6383
6835
|
console.warn('Database storage requires a table name');
|
|
6384
|
-
}), filter(() =>
|
|
6385
|
-
const
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
if (otherKeys.length > 0) {
|
|
6391
|
-
schema += ', ' + otherKeys.join(', ');
|
|
6392
|
-
}
|
|
6393
|
-
}
|
|
6836
|
+
}), filter(() => {
|
|
6837
|
+
const shouldProceed = this.dataType === DataType.ARRAY && !!this.databaseOptions?.table;
|
|
6838
|
+
console.log('[initDBStorage effect] Filter result:', shouldProceed);
|
|
6839
|
+
return shouldProceed;
|
|
6840
|
+
}), switchMap(() => {
|
|
6841
|
+
const schema = this.buildSchemaFromAdapter();
|
|
6394
6842
|
const tableDef = TableSchemaDef.adapt({
|
|
6395
6843
|
table: this.databaseOptions?.table,
|
|
6396
6844
|
schema: schema
|
|
@@ -6479,6 +6927,7 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6479
6927
|
}
|
|
6480
6928
|
}), concatMap(() => {
|
|
6481
6929
|
if (this.hasDatabase && this.databaseOptions?.table) {
|
|
6930
|
+
this.clearRequestCacheMetadata(this.databaseOptions.table);
|
|
6482
6931
|
const currentData = this.get()?.data;
|
|
6483
6932
|
const idsToDelete = Array.isArray(currentData) ? currentData.map((r) => r.id) : [];
|
|
6484
6933
|
if (idsToDelete.length > 0) {
|
|
@@ -6493,35 +6942,81 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6493
6942
|
this.fetchRecords = (options) => this.effect(() => of(RequestOptions.adapt(options)).pipe(switchMap(() => {
|
|
6494
6943
|
this.streamedResponse = [];
|
|
6495
6944
|
const requestOptions = this.updateRequestOptions(options?.headers);
|
|
6945
|
+
const effectiveParams = this.getEffectiveParams(options?.path);
|
|
6946
|
+
const requestSignature = this.buildRequestSignature('GET', requestOptions, effectiveParams);
|
|
6496
6947
|
const fetchFromAPI = () => {
|
|
6497
|
-
|
|
6498
|
-
|
|
6948
|
+
if (this.hasDatabase && this.databaseOptions?.table) {
|
|
6949
|
+
this.setCachedRequestSignature(this.databaseOptions.table, 'GET', requestSignature);
|
|
6950
|
+
}
|
|
6951
|
+
return this.httpManagerService.getRequest(requestOptions, effectiveParams).pipe(tap((data) => {
|
|
6952
|
+
// Extract array from paginated response if needed
|
|
6953
|
+
const arrayData = (data?.results && Array.isArray(data.results)) ? data.results : data;
|
|
6954
|
+
data = (!arrayData) ? (this.dataType === DataType.ARRAY) ? [] : {} : arrayData;
|
|
6499
6955
|
this.setData$(data);
|
|
6500
6956
|
}), concatMap((data) => {
|
|
6501
|
-
|
|
6957
|
+
// Extract array from paginated response for database storage
|
|
6958
|
+
const dbData = (data?.results && Array.isArray(data.results)) ? data.results : data;
|
|
6959
|
+
if (this.hasDatabase && this.databaseOptions?.table && Array.isArray(dbData) && dbData.length > 0) {
|
|
6960
|
+
const tableName = this.databaseOptions.table;
|
|
6502
6961
|
this.localStorageManagerService.updateStore({
|
|
6503
|
-
name:
|
|
6962
|
+
name: tableName,
|
|
6504
6963
|
data: { ...this.databaseOptions, ...{ expires: this.utils.expires(this.databaseOptions.expiresIn) } }
|
|
6505
6964
|
});
|
|
6506
|
-
|
|
6965
|
+
const schema = this.buildSchemaFromSample(dbData[0]);
|
|
6966
|
+
const tableDef = TableSchemaDef.adapt({ table: tableName, schema });
|
|
6967
|
+
const schemaSignature = this.buildSchemaSignature(schema);
|
|
6968
|
+
// Always ensure table exists immediately before writing to avoid stale schema/store races.
|
|
6969
|
+
return this.localStorageManagerService.store$(tableName).pipe(take(1), switchMap((storeData) => {
|
|
6970
|
+
const storedSchemaSignature = this.getStoredSchemaSignature(storeData);
|
|
6971
|
+
const schemaChanged = !!storedSchemaSignature && storedSchemaSignature !== schemaSignature;
|
|
6972
|
+
const ensureTable$ = schemaChanged
|
|
6973
|
+
? this.dbManagerService.clearTable(tableName).pipe(switchMap(() => this.dbManagerService.createDatabaseTable(tableDef)))
|
|
6974
|
+
: this.dbManagerService.createDatabaseTable(tableDef);
|
|
6975
|
+
return ensureTable$.pipe(switchMap((created) => {
|
|
6976
|
+
if (!created) {
|
|
6977
|
+
console.warn('[DB STORAGE] Table create/open not ready, skipping DB write for this payload:', { table: tableName });
|
|
6978
|
+
return of(data);
|
|
6979
|
+
}
|
|
6980
|
+
return this.dbManagerService.createTableRecords(tableName, dbData).pipe(tap(() => this.saveRequestCacheMetadata(tableName, 'GET', requestSignature, schemaSignature, options)));
|
|
6981
|
+
}));
|
|
6982
|
+
}), catchError((error) => {
|
|
6983
|
+
console.error('[DB STORAGE] Failed to ensure table and write records:', { table: tableName, schema, error });
|
|
6984
|
+
return of(data);
|
|
6985
|
+
}));
|
|
6507
6986
|
}
|
|
6508
6987
|
return of(data);
|
|
6509
6988
|
}));
|
|
6510
6989
|
};
|
|
6990
|
+
console.log('[DB STORAGE] Checking database storage:', {
|
|
6991
|
+
hasDatabase: this.hasDatabase,
|
|
6992
|
+
table: this.databaseOptions?.table,
|
|
6993
|
+
databaseOptions: this.databaseOptions
|
|
6994
|
+
});
|
|
6511
6995
|
if (this.hasDatabase && this.databaseOptions?.table) {
|
|
6512
6996
|
return this.dbManagerService.databaseExists().pipe(switchMap((dbExists) => {
|
|
6513
6997
|
if (!dbExists) {
|
|
6514
6998
|
const initObs = this.initDBStorageAsync();
|
|
6515
|
-
return initObs.pipe(switchMap(() => fetchFromAPI()))
|
|
6999
|
+
return initObs.pipe(switchMap(() => fetchFromAPI()), catchError((error) => {
|
|
7000
|
+
console.warn('[DB STORAGE] initDBStorageAsync failed when DB did not exist, continuing with API:', error);
|
|
7001
|
+
return fetchFromAPI();
|
|
7002
|
+
}));
|
|
6516
7003
|
}
|
|
6517
7004
|
return this.dbManagerService.hasDatabaseTable(this.databaseOptions.table).pipe(switchMap((tableExists) => {
|
|
6518
7005
|
if (!tableExists) {
|
|
6519
7006
|
const initObs = this.initDBStorageAsync();
|
|
6520
|
-
return initObs.pipe(switchMap(() => fetchFromAPI()))
|
|
7007
|
+
return initObs.pipe(switchMap(() => fetchFromAPI()), catchError((error) => {
|
|
7008
|
+
console.warn('[DB STORAGE] initDBStorageAsync failed when table missing, continuing with API:', error);
|
|
7009
|
+
return fetchFromAPI();
|
|
7010
|
+
}));
|
|
6521
7011
|
}
|
|
6522
7012
|
return this.localStorageManagerService.store$(this.databaseOptions.table).pipe(take(1), switchMap((storeData) => {
|
|
7013
|
+
const forceRefresh = !!options?.forceRefresh;
|
|
7014
|
+
const storedSchemaSignature = this.getStoredSchemaSignature(storeData);
|
|
6523
7015
|
const expires = storeData?.expires || 0;
|
|
6524
7016
|
const hasExpired = expires > 0 && this.utils.hasExpired(expires);
|
|
7017
|
+
if (forceRefresh) {
|
|
7018
|
+
return fetchFromAPI();
|
|
7019
|
+
}
|
|
6525
7020
|
if (hasExpired) {
|
|
6526
7021
|
return this.dbManagerService.clearTable(this.databaseOptions.table).pipe(switchMap(() => fetchFromAPI()), tap(() => {
|
|
6527
7022
|
this.localStorageManagerService.updateStore({
|
|
@@ -6530,12 +7025,41 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6530
7025
|
});
|
|
6531
7026
|
}));
|
|
6532
7027
|
}
|
|
7028
|
+
const expectedSchema = this.buildSchemaFromAdapter();
|
|
7029
|
+
const expectedSchemaSignature = this.buildSchemaSignature(expectedSchema);
|
|
7030
|
+
if (storedSchemaSignature && storedSchemaSignature !== expectedSchemaSignature) {
|
|
7031
|
+
const tableDef = TableSchemaDef.adapt({ table: this.databaseOptions.table, schema: expectedSchema });
|
|
7032
|
+
return this.dbManagerService.clearTable(this.databaseOptions.table).pipe(switchMap(() => this.dbManagerService.createDatabaseTable(tableDef)), switchMap(() => fetchFromAPI()));
|
|
7033
|
+
}
|
|
7034
|
+
const trackerAllowsRequest = this.queryParamsTrackerService.checkRequestOptions(this.resolvePath(effectiveParams), this.buildQueryTrackerOptions(options));
|
|
7035
|
+
const shouldMakeRequest = trackerAllowsRequest;
|
|
7036
|
+
if (shouldMakeRequest) {
|
|
7037
|
+
return fetchFromAPI();
|
|
7038
|
+
}
|
|
6533
7039
|
return this.dbManagerService.getTableRecords(this.databaseOptions.table).pipe(switchMap((dbData) => {
|
|
6534
7040
|
if (Array.isArray(dbData) && dbData.length > 0) {
|
|
6535
7041
|
this.setData$(dbData);
|
|
6536
7042
|
return of(dbData);
|
|
6537
7043
|
}
|
|
6538
|
-
|
|
7044
|
+
const currentStateData = this.get()?.data;
|
|
7045
|
+
if (Array.isArray(currentStateData) && currentStateData.length > 0) {
|
|
7046
|
+
return of(currentStateData);
|
|
7047
|
+
}
|
|
7048
|
+
if (currentStateData &&
|
|
7049
|
+
!Array.isArray(currentStateData) &&
|
|
7050
|
+
Object.keys(currentStateData).length > 0) {
|
|
7051
|
+
return of(currentStateData);
|
|
7052
|
+
}
|
|
7053
|
+
return of(this.dataType === DataType.ARRAY ? [] : {});
|
|
7054
|
+
}), catchError((error) => {
|
|
7055
|
+
const tableName = this.databaseOptions.table;
|
|
7056
|
+
console.warn('[DB STORAGE] getTableRecords failed, recreating table and falling back to API:', { table: tableName, error });
|
|
7057
|
+
const schema = this.buildSchemaFromAdapter();
|
|
7058
|
+
const tableDef = TableSchemaDef.adapt({ table: tableName, schema });
|
|
7059
|
+
return this.dbManagerService.createDatabaseTable(tableDef).pipe(switchMap(() => fetchFromAPI()), catchError((recreateError) => {
|
|
7060
|
+
console.error('[DB STORAGE] Failed to recreate table after read error, continuing with API only:', recreateError);
|
|
7061
|
+
return fetchFromAPI();
|
|
7062
|
+
}));
|
|
6539
7063
|
}));
|
|
6540
7064
|
}));
|
|
6541
7065
|
}));
|
|
@@ -6659,7 +7183,7 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6659
7183
|
if (res.length > 0)
|
|
6660
7184
|
this.setData$(res);
|
|
6661
7185
|
this.streamedResponse = res;
|
|
6662
|
-
}), scan((acc, res) => {
|
|
7186
|
+
}), concatMap((res) => this.persistStreamDataToDb(res, options)), scan((acc, res) => {
|
|
6663
7187
|
const previous = acc.current;
|
|
6664
7188
|
const current = res;
|
|
6665
7189
|
return { previous, current };
|
|
@@ -6679,8 +7203,70 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6679
7203
|
}), switchMap((options) => {
|
|
6680
7204
|
const requestOptions = this.updateRequestOptions(options?.headers);
|
|
6681
7205
|
requestOptions.stream = true;
|
|
7206
|
+
const effectiveParams = this.getEffectiveParams(options?.path);
|
|
6682
7207
|
console.log('[DEBUG] Making streaming request:', requestOptions);
|
|
6683
|
-
|
|
7208
|
+
if (this.hasDatabase && this.databaseOptions?.table) {
|
|
7209
|
+
const requestSignature = this.buildRequestSignature('STREAM', requestOptions, effectiveParams);
|
|
7210
|
+
return this.localStorageManagerService.store$(this.databaseOptions.table).pipe(take(1), switchMap((storeData) => {
|
|
7211
|
+
const forceRefresh = !!options?.forceRefresh;
|
|
7212
|
+
const expires = storeData?.expires || 0;
|
|
7213
|
+
const hasExpired = expires > 0 && this.utils.hasExpired(expires);
|
|
7214
|
+
if (forceRefresh) {
|
|
7215
|
+
this.setCachedRequestSignature(this.databaseOptions.table, 'STREAM', requestSignature);
|
|
7216
|
+
return this.httpManagerService.getRequest(requestOptions, effectiveParams).pipe(map((apiData) => ({ data: apiData, fromCache: false })));
|
|
7217
|
+
}
|
|
7218
|
+
if (hasExpired) {
|
|
7219
|
+
return this.dbManagerService.clearTable(this.databaseOptions.table).pipe(tap(() => this.setCachedRequestSignature(this.databaseOptions.table, 'STREAM', requestSignature)), switchMap(() => this.httpManagerService.getRequest(requestOptions, effectiveParams)), map((apiData) => ({ data: apiData, fromCache: false })));
|
|
7220
|
+
}
|
|
7221
|
+
const trackerAllowsRequest = this.queryParamsTrackerService.checkRequestOptions(this.resolvePath(effectiveParams), this.buildQueryTrackerOptions(options));
|
|
7222
|
+
const shouldMakeRequest = trackerAllowsRequest;
|
|
7223
|
+
if (!shouldMakeRequest) {
|
|
7224
|
+
return this.dbManagerService.getTableRecords(this.databaseOptions.table).pipe(switchMap((dbData) => {
|
|
7225
|
+
if (Array.isArray(dbData) && dbData.length > 0) {
|
|
7226
|
+
return of({ data: dbData, fromCache: true });
|
|
7227
|
+
}
|
|
7228
|
+
const currentStateData = this.get()?.data;
|
|
7229
|
+
if (Array.isArray(currentStateData) && currentStateData.length > 0) {
|
|
7230
|
+
return of({ data: currentStateData, fromCache: true });
|
|
7231
|
+
}
|
|
7232
|
+
if (currentStateData &&
|
|
7233
|
+
!Array.isArray(currentStateData) &&
|
|
7234
|
+
Object.keys(currentStateData).length > 0) {
|
|
7235
|
+
return of({ data: currentStateData, fromCache: true });
|
|
7236
|
+
}
|
|
7237
|
+
return of({ data: this.dataType === DataType.ARRAY ? [] : {}, fromCache: true });
|
|
7238
|
+
}));
|
|
7239
|
+
}
|
|
7240
|
+
this.setCachedRequestSignature(this.databaseOptions.table, 'STREAM', requestSignature);
|
|
7241
|
+
return this.httpManagerService.getRequest(requestOptions, effectiveParams).pipe(map((apiData) => ({ data: apiData, fromCache: false })));
|
|
7242
|
+
})).pipe(tap((packet) => {
|
|
7243
|
+
const res = packet?.data;
|
|
7244
|
+
console.log('[DEBUG] Streaming response received:', res);
|
|
7245
|
+
if (res && res.length > 0) {
|
|
7246
|
+
console.log('[DEBUG] Updating state with streaming data:', res);
|
|
7247
|
+
this.setData$(res);
|
|
7248
|
+
this.streamedResponse = res;
|
|
7249
|
+
}
|
|
7250
|
+
else {
|
|
7251
|
+
console.log('[DEBUG] No streaming data or empty array:', res);
|
|
7252
|
+
}
|
|
7253
|
+
// Reset pending once we have a response packet (cache or network)
|
|
7254
|
+
this.httpManagerService.isPending.next(false);
|
|
7255
|
+
}), concatMap((packet) => {
|
|
7256
|
+
if (packet?.fromCache) {
|
|
7257
|
+
return of(packet?.data);
|
|
7258
|
+
}
|
|
7259
|
+
return this.persistStreamDataToDb(packet?.data, options);
|
|
7260
|
+
}), map((res) => {
|
|
7261
|
+
console.log('[DEBUG] Returning data to subscribers:', res);
|
|
7262
|
+
return res;
|
|
7263
|
+
}), catchError((error) => {
|
|
7264
|
+
console.error('[DEBUG] Streaming error:', error);
|
|
7265
|
+
this.httpManagerService.isPending.next(false);
|
|
7266
|
+
return of([]);
|
|
7267
|
+
}));
|
|
7268
|
+
}
|
|
7269
|
+
return this.httpManagerService.getRequest(requestOptions, effectiveParams)
|
|
6684
7270
|
.pipe(tap((res) => {
|
|
6685
7271
|
console.log('[DEBUG] Streaming response received:', res);
|
|
6686
7272
|
// Always update state with streaming data
|
|
@@ -6692,7 +7278,9 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6692
7278
|
else {
|
|
6693
7279
|
console.log('[DEBUG] No streaming data or empty array:', res);
|
|
6694
7280
|
}
|
|
6695
|
-
|
|
7281
|
+
// Reset pending once we have a response packet
|
|
7282
|
+
this.httpManagerService.isPending.next(false);
|
|
7283
|
+
}), concatMap((res) => this.persistStreamDataToDb(res, options)), map((res) => {
|
|
6696
7284
|
console.log('[DEBUG] Returning data to subscribers:', res);
|
|
6697
7285
|
return res; // Return the data so subscribers can receive it
|
|
6698
7286
|
}), catchError((error) => {
|
|
@@ -6719,7 +7307,11 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6719
7307
|
encrypted: false,
|
|
6720
7308
|
})
|
|
6721
7309
|
});
|
|
6722
|
-
|
|
7310
|
+
// Use initDBStorageAsync directly - the effect initDBStorage requires subscription
|
|
7311
|
+
this.initDBStorageAsync().subscribe({
|
|
7312
|
+
next: () => console.log('[Constructor] Database storage initialized'),
|
|
7313
|
+
error: (err) => console.error('[Constructor] Database storage initialization failed:', err)
|
|
7314
|
+
});
|
|
6723
7315
|
}
|
|
6724
7316
|
}
|
|
6725
7317
|
catch (error) {
|
|
@@ -6773,8 +7365,24 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6773
7365
|
this.dataType = (dataType) ? dataType : DataType.ARRAY;
|
|
6774
7366
|
// Only update database options if a database parameter is explicitly provided
|
|
6775
7367
|
if (database !== undefined) {
|
|
7368
|
+
console.log('[setApiRequestOptions] Database config:', {
|
|
7369
|
+
database,
|
|
7370
|
+
hasTable: !!database?.table,
|
|
7371
|
+
tableValue: database?.table
|
|
7372
|
+
});
|
|
6776
7373
|
this.hasDatabase = (database?.table) ? true : false;
|
|
6777
|
-
|
|
7374
|
+
const adapted = DatabaseStorage.adapt(database);
|
|
7375
|
+
console.log('[setApiRequestOptions] DatabaseStorage.adapt result:', adapted);
|
|
7376
|
+
this.databaseOptions = (this.hasDatabase) ? adapted : undefined;
|
|
7377
|
+
// Trigger database table creation if table is configured
|
|
7378
|
+
if (this.hasDatabase && this.databaseOptions?.table) {
|
|
7379
|
+
console.log('[setApiRequestOptions] Initializing database storage for table:', this.databaseOptions.table);
|
|
7380
|
+
// Use initDBStorageAsync directly instead of the effect - effects need subscription to run
|
|
7381
|
+
this.initDBStorageAsync().subscribe({
|
|
7382
|
+
next: () => console.log('[setApiRequestOptions] Database storage initialized successfully'),
|
|
7383
|
+
error: (err) => console.error('[setApiRequestOptions] Database storage initialization failed:', err)
|
|
7384
|
+
});
|
|
7385
|
+
}
|
|
6778
7386
|
}
|
|
6779
7387
|
if (this.apiOptions.ws && this.apiOptions.ws.id !== '') {
|
|
6780
7388
|
// Auto-prefix channel ID for private state manager channels
|
|
@@ -6896,11 +7504,20 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6896
7504
|
this.setData$(data);
|
|
6897
7505
|
}
|
|
6898
7506
|
updateArrayState(currentData, newData) {
|
|
7507
|
+
// For non-streaming requests (GET), REPLACE data entirely
|
|
7508
|
+
// For streaming requests, MERGE with existing data incrementally
|
|
7509
|
+
if (this.streamedResponse.length === 0) {
|
|
7510
|
+
// GET request: return new data as-is (replace)
|
|
7511
|
+
return newData;
|
|
7512
|
+
}
|
|
7513
|
+
// Streaming: merge with existing data
|
|
6899
7514
|
const filterCurrentData = () => {
|
|
6900
7515
|
const ids = this.streamedResponse.map((obj) => obj.id);
|
|
6901
7516
|
return currentData.filter(obj => (obj.id) ? ids.includes(obj.id) : obj);
|
|
6902
7517
|
};
|
|
6903
|
-
const filteredCurrentData = (this.httpManagerService.isPending.value)
|
|
7518
|
+
const filteredCurrentData = (this.httpManagerService.isPending.value)
|
|
7519
|
+
? currentData
|
|
7520
|
+
: filterCurrentData();
|
|
6904
7521
|
const updatedData = filteredCurrentData.map(item => {
|
|
6905
7522
|
const newItem = newData.find(newItem => {
|
|
6906
7523
|
const hasId = (newItem?.id && item?.id) ? true : false;
|
|
@@ -6917,32 +7534,92 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6917
7534
|
return [...updatedData, ...addedData];
|
|
6918
7535
|
}
|
|
6919
7536
|
initDBStorageAsync() {
|
|
7537
|
+
console.log('[initDBStorageAsync] Starting initialization:', {
|
|
7538
|
+
dataType: this.dataType,
|
|
7539
|
+
hasAdapter: !!this.apiOptions?.adapter,
|
|
7540
|
+
table: this.databaseOptions?.table,
|
|
7541
|
+
databaseOptions: this.databaseOptions
|
|
7542
|
+
});
|
|
6920
7543
|
if (this.dataType !== DataType.ARRAY) {
|
|
6921
7544
|
console.warn('Database storage requires dataType to be ARRAY');
|
|
6922
7545
|
return of(null);
|
|
6923
7546
|
}
|
|
6924
7547
|
if (!this.apiOptions.adapter) {
|
|
6925
|
-
console.warn('Database storage
|
|
6926
|
-
return of(null);
|
|
7548
|
+
console.warn('Database storage adapter missing, using minimal or inferred schema');
|
|
6927
7549
|
}
|
|
6928
7550
|
if (!this.databaseOptions?.table) {
|
|
6929
7551
|
console.warn('Database storage requires a table name');
|
|
6930
7552
|
return of(null);
|
|
6931
7553
|
}
|
|
7554
|
+
const schema = this.buildSchemaFromAdapter();
|
|
7555
|
+
const tableDef = TableSchemaDef.adapt({
|
|
7556
|
+
table: this.databaseOptions?.table,
|
|
7557
|
+
schema: schema
|
|
7558
|
+
});
|
|
7559
|
+
return this.dbManagerService.createDatabaseTable(tableDef).pipe(tap((created) => {
|
|
7560
|
+
if (created && this.databaseOptions?.table) {
|
|
7561
|
+
this.saveSchemaSignature(this.databaseOptions.table, this.buildSchemaSignature(schema));
|
|
7562
|
+
}
|
|
7563
|
+
}));
|
|
7564
|
+
}
|
|
7565
|
+
buildSchemaFromAdapter() {
|
|
6932
7566
|
const sampleData = this.apiOptions.adapter?.({}) || {};
|
|
6933
|
-
|
|
7567
|
+
return this.buildSchemaFromSample(sampleData);
|
|
7568
|
+
}
|
|
7569
|
+
buildSchemaFromSample(sampleData) {
|
|
7570
|
+
const schemaKeys = Object.keys(sampleData || {}).filter(key => sampleData[key] !== undefined);
|
|
6934
7571
|
let schema = '++id';
|
|
6935
7572
|
if (schemaKeys.length > 0) {
|
|
6936
|
-
const otherKeys = schemaKeys.filter(k => k !== 'id');
|
|
7573
|
+
const otherKeys = schemaKeys.filter((k) => k !== 'id');
|
|
6937
7574
|
if (otherKeys.length > 0) {
|
|
6938
7575
|
schema += ', ' + otherKeys.join(', ');
|
|
6939
7576
|
}
|
|
6940
7577
|
}
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
7578
|
+
return schema;
|
|
7579
|
+
}
|
|
7580
|
+
persistStreamDataToDb(payload, streamOptions) {
|
|
7581
|
+
if (!this.hasDatabase || !this.databaseOptions?.table) {
|
|
7582
|
+
return of(payload);
|
|
7583
|
+
}
|
|
7584
|
+
const dbData = (payload?.results && Array.isArray(payload.results))
|
|
7585
|
+
? payload.results
|
|
7586
|
+
: Array.isArray(payload)
|
|
7587
|
+
? payload
|
|
7588
|
+
: payload
|
|
7589
|
+
? [payload]
|
|
7590
|
+
: [];
|
|
7591
|
+
if (dbData.length === 0) {
|
|
7592
|
+
return of(payload);
|
|
7593
|
+
}
|
|
7594
|
+
const tableName = this.databaseOptions.table;
|
|
7595
|
+
const requestOptions = this.updateRequestOptions(streamOptions?.headers);
|
|
7596
|
+
requestOptions.stream = true;
|
|
7597
|
+
const effectiveParams = this.getEffectiveParams(streamOptions?.path);
|
|
7598
|
+
const requestSignature = this.buildRequestSignature('STREAM', requestOptions, effectiveParams);
|
|
7599
|
+
this.localStorageManagerService.updateStore({
|
|
7600
|
+
name: tableName,
|
|
7601
|
+
data: { ...this.databaseOptions, ...{ expires: this.utils.expires(this.databaseOptions.expiresIn) } }
|
|
7602
|
+
});
|
|
7603
|
+
const schema = this.buildSchemaFromSample(dbData[0]);
|
|
7604
|
+
const schemaSignature = this.buildSchemaSignature(schema);
|
|
7605
|
+
const tableDef = TableSchemaDef.adapt({ table: tableName, schema });
|
|
7606
|
+
return this.localStorageManagerService.store$(tableName).pipe(take(1), switchMap((storeData) => {
|
|
7607
|
+
const storedSchemaSignature = this.getStoredSchemaSignature(storeData);
|
|
7608
|
+
const schemaChanged = !!storedSchemaSignature && storedSchemaSignature !== schemaSignature;
|
|
7609
|
+
const ensureTable$ = schemaChanged
|
|
7610
|
+
? this.dbManagerService.clearTable(tableName).pipe(switchMap(() => this.dbManagerService.createDatabaseTable(tableDef)))
|
|
7611
|
+
: this.dbManagerService.createDatabaseTable(tableDef);
|
|
7612
|
+
return ensureTable$.pipe(switchMap((created) => {
|
|
7613
|
+
if (!created) {
|
|
7614
|
+
console.warn('[DB STORAGE] Stream table create/open not ready, skipping DB write for this chunk:', { table: tableName });
|
|
7615
|
+
return of(payload);
|
|
7616
|
+
}
|
|
7617
|
+
return this.dbManagerService.createTableRecords(tableName, dbData).pipe(tap(() => this.saveRequestCacheMetadata(tableName, 'STREAM', requestSignature, schemaSignature, streamOptions)), map(() => payload));
|
|
7618
|
+
}));
|
|
7619
|
+
}), map(() => payload), catchError((error) => {
|
|
7620
|
+
console.error('[DB STORAGE] Failed to persist streaming payload:', { table: tableName, schema, error });
|
|
7621
|
+
return of(payload);
|
|
7622
|
+
}));
|
|
6946
7623
|
}
|
|
6947
7624
|
// WEBSOCKET COMMUNICATION (STATE MANAGER)
|
|
6948
7625
|
wsCommunication(method, path) {
|
|
@@ -7238,6 +7915,7 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
7238
7915
|
const tableName = this.databaseOptions.table;
|
|
7239
7916
|
this.dbManagerService.clearTable(tableName).subscribe({
|
|
7240
7917
|
next: () => {
|
|
7918
|
+
this.clearRequestCacheMetadata(tableName);
|
|
7241
7919
|
if (this.dataType === DataType.ARRAY) {
|
|
7242
7920
|
this.setData$([]);
|
|
7243
7921
|
}
|
|
@@ -7260,6 +7938,132 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
7260
7938
|
: { ...options.headers };
|
|
7261
7939
|
return options;
|
|
7262
7940
|
}
|
|
7941
|
+
buildQueryTrackerOptions(options) {
|
|
7942
|
+
return {
|
|
7943
|
+
watchParams: Array.isArray(options?.watchParams) ? options.watchParams : [],
|
|
7944
|
+
watchExpiresAt: options?.watchExpiresAt,
|
|
7945
|
+
};
|
|
7946
|
+
}
|
|
7947
|
+
normalizeObject(value) {
|
|
7948
|
+
if (Array.isArray(value)) {
|
|
7949
|
+
return value.map((item) => this.normalizeObject(item));
|
|
7950
|
+
}
|
|
7951
|
+
if (value && typeof value === 'object') {
|
|
7952
|
+
return Object.keys(value)
|
|
7953
|
+
.sort()
|
|
7954
|
+
.reduce((acc, key) => {
|
|
7955
|
+
acc[key] = this.normalizeObject(value[key]);
|
|
7956
|
+
return acc;
|
|
7957
|
+
}, {});
|
|
7958
|
+
}
|
|
7959
|
+
return value;
|
|
7960
|
+
}
|
|
7961
|
+
filterHeaders(headers) {
|
|
7962
|
+
const source = headers || {};
|
|
7963
|
+
return Object.keys(source).reduce((acc, key) => {
|
|
7964
|
+
if (!this.volatileHeaders.has(key.toLowerCase())) {
|
|
7965
|
+
acc[key] = source[key];
|
|
7966
|
+
}
|
|
7967
|
+
return acc;
|
|
7968
|
+
}, {});
|
|
7969
|
+
}
|
|
7970
|
+
resolvePath(params) {
|
|
7971
|
+
const basePath = Array.isArray(this.apiOptions.path) ? this.apiOptions.path : [];
|
|
7972
|
+
const effective = this.getEffectiveParams(params);
|
|
7973
|
+
return effective ? [...basePath, ...effective] : [...basePath];
|
|
7974
|
+
}
|
|
7975
|
+
getEffectiveParams(params) {
|
|
7976
|
+
if (!Array.isArray(params) || params.length === 0) {
|
|
7977
|
+
return undefined;
|
|
7978
|
+
}
|
|
7979
|
+
const basePath = Array.isArray(this.apiOptions.path) ? this.apiOptions.path : [];
|
|
7980
|
+
if (basePath.length !== params.length) {
|
|
7981
|
+
return params;
|
|
7982
|
+
}
|
|
7983
|
+
const normalizePart = (value) => {
|
|
7984
|
+
if (value && typeof value === 'object') {
|
|
7985
|
+
return JSON.stringify(this.normalizeObject(value));
|
|
7986
|
+
}
|
|
7987
|
+
return String(value);
|
|
7988
|
+
};
|
|
7989
|
+
const samePath = params.every((part, index) => normalizePart(part) === normalizePart(basePath[index]));
|
|
7990
|
+
return samePath ? undefined : params;
|
|
7991
|
+
}
|
|
7992
|
+
buildRequestSignature(method, requestOptions, params) {
|
|
7993
|
+
const signaturePayload = {
|
|
7994
|
+
method,
|
|
7995
|
+
server: requestOptions.server,
|
|
7996
|
+
path: this.resolvePath(params),
|
|
7997
|
+
headers: this.filterHeaders(requestOptions.headers),
|
|
7998
|
+
stream: !!requestOptions.stream,
|
|
7999
|
+
streamType: requestOptions.streamType || null
|
|
8000
|
+
};
|
|
8001
|
+
return JSON.stringify(this.normalizeObject(signaturePayload));
|
|
8002
|
+
}
|
|
8003
|
+
buildSchemaSignature(schema) {
|
|
8004
|
+
return JSON.stringify(this.normalizeObject(schema.split(',').map((part) => part.trim()).filter(Boolean)));
|
|
8005
|
+
}
|
|
8006
|
+
getCachedRequestSignature(tableName, type) {
|
|
8007
|
+
return this.requestSignatureCache[tableName]?.[type] || null;
|
|
8008
|
+
}
|
|
8009
|
+
setCachedRequestSignature(tableName, type, signature) {
|
|
8010
|
+
const existing = this.requestSignatureCache[tableName] || {};
|
|
8011
|
+
this.requestSignatureCache[tableName] = {
|
|
8012
|
+
...existing,
|
|
8013
|
+
[type]: signature,
|
|
8014
|
+
};
|
|
8015
|
+
}
|
|
8016
|
+
getRequestCacheMetadata(storeData, type) {
|
|
8017
|
+
return storeData?.requestCache?.[type] || null;
|
|
8018
|
+
}
|
|
8019
|
+
getStoredSchemaSignature(storeData) {
|
|
8020
|
+
return storeData?.schemaSignature || null;
|
|
8021
|
+
}
|
|
8022
|
+
saveSchemaSignature(tableName, schemaSignature) {
|
|
8023
|
+
this.localStorageManagerService.store$(tableName).pipe(take(1), tap((storeData) => {
|
|
8024
|
+
this.localStorageManagerService.updateStore({
|
|
8025
|
+
name: tableName,
|
|
8026
|
+
data: {
|
|
8027
|
+
...(storeData || {}),
|
|
8028
|
+
schemaSignature,
|
|
8029
|
+
}
|
|
8030
|
+
});
|
|
8031
|
+
})).subscribe();
|
|
8032
|
+
}
|
|
8033
|
+
saveRequestCacheMetadata(tableName, type, signature, schemaSignature, options) {
|
|
8034
|
+
this.setCachedRequestSignature(tableName, type, signature);
|
|
8035
|
+
this.localStorageManagerService.store$(tableName).pipe(take(1), tap((storeData) => {
|
|
8036
|
+
const currentCache = storeData?.requestCache || {};
|
|
8037
|
+
this.localStorageManagerService.updateStore({
|
|
8038
|
+
name: tableName,
|
|
8039
|
+
data: {
|
|
8040
|
+
...(storeData || {}),
|
|
8041
|
+
schemaSignature: schemaSignature || storeData?.schemaSignature || null,
|
|
8042
|
+
requestCache: {
|
|
8043
|
+
...currentCache,
|
|
8044
|
+
[type]: {
|
|
8045
|
+
signature,
|
|
8046
|
+
savedAt: Date.now(),
|
|
8047
|
+
path: this.resolvePath(options?.path),
|
|
8048
|
+
headers: this.filterHeaders(options?.headers)
|
|
8049
|
+
}
|
|
8050
|
+
}
|
|
8051
|
+
}
|
|
8052
|
+
});
|
|
8053
|
+
})).subscribe();
|
|
8054
|
+
}
|
|
8055
|
+
clearRequestCacheMetadata(tableName) {
|
|
8056
|
+
this.localStorageManagerService.store$(tableName).pipe(take(1), tap((storeData) => {
|
|
8057
|
+
if (!storeData)
|
|
8058
|
+
return;
|
|
8059
|
+
const updated = { ...(storeData || {}) };
|
|
8060
|
+
delete updated.requestCache;
|
|
8061
|
+
this.localStorageManagerService.updateStore({
|
|
8062
|
+
name: tableName,
|
|
8063
|
+
data: updated
|
|
8064
|
+
});
|
|
8065
|
+
})).subscribe();
|
|
8066
|
+
}
|
|
7263
8067
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: HTTPManagerStateService, deps: [{ token: API_OPTS }, { token: "dataType" }, { token: DatabaseStorage }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
7264
8068
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: HTTPManagerStateService }); }
|
|
7265
8069
|
}
|
|
@@ -8566,12 +9370,12 @@ class StateManagerDemoService extends HTTPManagerStateService {
|
|
|
8566
9370
|
setAPIOptions(apiOptions, dataType, database) {
|
|
8567
9371
|
this.setApiRequestOptions(apiOptions, dataType, database);
|
|
8568
9372
|
}
|
|
8569
|
-
getClients() {
|
|
9373
|
+
getClients(options) {
|
|
8570
9374
|
// const headers = {
|
|
8571
9375
|
// auth: "sample-auth-token"
|
|
8572
9376
|
// }
|
|
8573
9377
|
// const sampleOptions = RequestOptions.adapt({ path: ["id", 12], headers, sample: true })
|
|
8574
|
-
this.fetchRecords();
|
|
9378
|
+
this.fetchRecords(options);
|
|
8575
9379
|
}
|
|
8576
9380
|
createClient(data) {
|
|
8577
9381
|
// const headers = {
|
|
@@ -8596,13 +9400,13 @@ class StateManagerDemoService extends HTTPManagerStateService {
|
|
|
8596
9400
|
const sampleOptions = RequestOptions.adapt({ path: [data.id] });
|
|
8597
9401
|
this.deleteRecord(sampleOptions);
|
|
8598
9402
|
}
|
|
8599
|
-
streamRequest() {
|
|
9403
|
+
streamRequest(options) {
|
|
8600
9404
|
console.log('[DEMO SERVICE] streamRequest called');
|
|
8601
9405
|
const headers = {
|
|
8602
9406
|
auth: "sample-auth-token"
|
|
8603
9407
|
};
|
|
8604
9408
|
console.log('[DEMO SERVICE] Calling fetchStream with headers:', headers);
|
|
8605
|
-
this.fetchStream();
|
|
9409
|
+
this.fetchStream(options);
|
|
8606
9410
|
}
|
|
8607
9411
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: StateManagerDemoService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
8608
9412
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: StateManagerDemoService }); }
|
|
@@ -8750,6 +9554,8 @@ class RequestManagerStateDemoComponent {
|
|
|
8750
9554
|
database: this.fb.group({
|
|
8751
9555
|
table: [''],
|
|
8752
9556
|
expiresIn: ['1m'],
|
|
9557
|
+
watchParams: [''],
|
|
9558
|
+
watchExpiresAt: ['never'],
|
|
8753
9559
|
})
|
|
8754
9560
|
});
|
|
8755
9561
|
this.sampleAdaptors = [
|
|
@@ -8782,26 +9588,26 @@ class RequestManagerStateDemoComponent {
|
|
|
8782
9588
|
});
|
|
8783
9589
|
this.stateManagerDemoService.data$
|
|
8784
9590
|
.pipe(tap$1((data) => {
|
|
8785
|
-
console.log('[COMPONENT] State data received:', data)
|
|
9591
|
+
// console.log('[COMPONENT] State data received:', data)
|
|
8786
9592
|
switch (this.requestType) {
|
|
8787
9593
|
case 'GET':
|
|
8788
|
-
console.log('[COMPONENT] Updating GET$ with data:', data)
|
|
9594
|
+
// console.log('[COMPONENT] Updating GET$ with data:', data)
|
|
8789
9595
|
this.GET$.next(data);
|
|
8790
9596
|
break;
|
|
8791
9597
|
case 'PUT':
|
|
8792
|
-
console.log('[COMPONENT] Updating PUT$ with data:', data)
|
|
9598
|
+
// console.log('[COMPONENT] Updating PUT$ with data:', data)
|
|
8793
9599
|
this.PUT$.next(data);
|
|
8794
9600
|
break;
|
|
8795
9601
|
case 'POST':
|
|
8796
|
-
console.log('[COMPONENT] Updating POST$ with data:', data)
|
|
9602
|
+
// console.log('[COMPONENT] Updating POST$ with data:', data)
|
|
8797
9603
|
this.POST$.next(data);
|
|
8798
9604
|
break;
|
|
8799
9605
|
case 'DELETE':
|
|
8800
|
-
console.log('[COMPONENT] Updating DELETE$ with data:', data)
|
|
9606
|
+
// console.log('[COMPONENT] Updating DELETE$ with data:', data)
|
|
8801
9607
|
this.DELETE$.next(data);
|
|
8802
9608
|
break;
|
|
8803
9609
|
case 'STREAM':
|
|
8804
|
-
console.log('[COMPONENT] Updating STREAM$ with data:', data)
|
|
9610
|
+
// console.log('[COMPONENT] Updating STREAM$ with data:', data)
|
|
8805
9611
|
this.STREAM.next(data);
|
|
8806
9612
|
// Update table columns dynamically based on streaming data
|
|
8807
9613
|
if (data && Array.isArray(data) && data.length > 0) {
|
|
@@ -8809,11 +9615,11 @@ class RequestManagerStateDemoComponent {
|
|
|
8809
9615
|
}
|
|
8810
9616
|
break;
|
|
8811
9617
|
case 'STREAM_AI':
|
|
8812
|
-
console.log('[COMPONENT] Updating STREAM_AI$ with data:', data)
|
|
9618
|
+
// console.log('[COMPONENT] Updating STREAM_AI$ with data:', data)
|
|
8813
9619
|
this.STREAM_AI.next(data);
|
|
8814
9620
|
break;
|
|
8815
9621
|
default:
|
|
8816
|
-
console.log('[COMPONENT] No requestType set, ignoring data')
|
|
9622
|
+
// console.log('[COMPONENT] No requestType set, ignoring data')
|
|
8817
9623
|
break;
|
|
8818
9624
|
}
|
|
8819
9625
|
})).subscribe();
|
|
@@ -8831,10 +9637,59 @@ class RequestManagerStateDemoComponent {
|
|
|
8831
9637
|
removeHeader(index) {
|
|
8832
9638
|
this.headers.removeAt(index);
|
|
8833
9639
|
}
|
|
9640
|
+
parsePathInput(pathInput) {
|
|
9641
|
+
const raw = typeof pathInput === 'string' ? pathInput.trim() : '';
|
|
9642
|
+
if (!raw) {
|
|
9643
|
+
return [];
|
|
9644
|
+
}
|
|
9645
|
+
const [rawPath = '', rawQuery = ''] = raw.split('?', 2);
|
|
9646
|
+
const pathSegments = rawPath
|
|
9647
|
+
.split('/')
|
|
9648
|
+
.map((segment) => segment.trim())
|
|
9649
|
+
.filter((segment) => segment.length > 0)
|
|
9650
|
+
.map((segment) => decodeURIComponent(segment));
|
|
9651
|
+
if (!rawQuery) {
|
|
9652
|
+
return pathSegments;
|
|
9653
|
+
}
|
|
9654
|
+
const searchParams = new URLSearchParams(rawQuery);
|
|
9655
|
+
const queryObject = {};
|
|
9656
|
+
searchParams.forEach((value, key) => {
|
|
9657
|
+
const normalizedKey = key.trim();
|
|
9658
|
+
if (!normalizedKey) {
|
|
9659
|
+
return;
|
|
9660
|
+
}
|
|
9661
|
+
const normalizedValue = this.normalizeQueryValue(value);
|
|
9662
|
+
if (Object.prototype.hasOwnProperty.call(queryObject, normalizedKey)) {
|
|
9663
|
+
const currentValue = queryObject[normalizedKey];
|
|
9664
|
+
queryObject[normalizedKey] = Array.isArray(currentValue)
|
|
9665
|
+
? [...currentValue, normalizedValue]
|
|
9666
|
+
: [currentValue, normalizedValue];
|
|
9667
|
+
return;
|
|
9668
|
+
}
|
|
9669
|
+
queryObject[normalizedKey] = normalizedValue;
|
|
9670
|
+
});
|
|
9671
|
+
return Object.keys(queryObject).length > 0
|
|
9672
|
+
? [...pathSegments, queryObject]
|
|
9673
|
+
: pathSegments;
|
|
9674
|
+
}
|
|
9675
|
+
normalizeQueryValue(value) {
|
|
9676
|
+
const trimmedValue = value.trim();
|
|
9677
|
+
const lowerValue = trimmedValue.toLowerCase();
|
|
9678
|
+
if (lowerValue === 'true') {
|
|
9679
|
+
return true;
|
|
9680
|
+
}
|
|
9681
|
+
if (lowerValue === 'false') {
|
|
9682
|
+
return false;
|
|
9683
|
+
}
|
|
9684
|
+
if (trimmedValue !== '' && !Number.isNaN(Number(trimmedValue))) {
|
|
9685
|
+
return Number(trimmedValue);
|
|
9686
|
+
}
|
|
9687
|
+
return trimmedValue;
|
|
9688
|
+
}
|
|
8834
9689
|
compileRequest() {
|
|
8835
9690
|
const requestParams = this.requestForm.value;
|
|
8836
9691
|
requestParams.headers = this.arrayObjectsToObjects(requestParams.headers || []);
|
|
8837
|
-
const pathReq =
|
|
9692
|
+
const pathReq = this.parsePathInput(requestParams.path || '');
|
|
8838
9693
|
if (!this.pollingState.checked)
|
|
8839
9694
|
requestParams.polling = 0;
|
|
8840
9695
|
if (!this.failedState.checked) {
|
|
@@ -8848,12 +9703,53 @@ class RequestManagerStateDemoComponent {
|
|
|
8848
9703
|
const apiOptions = ApiRequest.adapt({ ...currentOptions, path: pathReq });
|
|
8849
9704
|
return { apiOptions: apiOptions, path: pathReq };
|
|
8850
9705
|
}
|
|
9706
|
+
parseWatchParams(value) {
|
|
9707
|
+
if (!value || typeof value !== 'string') {
|
|
9708
|
+
return [];
|
|
9709
|
+
}
|
|
9710
|
+
return value
|
|
9711
|
+
.split(',')
|
|
9712
|
+
.map((item) => item.trim())
|
|
9713
|
+
.filter((item) => item.length > 0);
|
|
9714
|
+
}
|
|
9715
|
+
normalizeWatchExpiry(value) {
|
|
9716
|
+
if (typeof value === 'undefined' || value === null || value === '') {
|
|
9717
|
+
return undefined;
|
|
9718
|
+
}
|
|
9719
|
+
if (typeof value === 'string' && value.trim().toLowerCase() === 'never') {
|
|
9720
|
+
return undefined;
|
|
9721
|
+
}
|
|
9722
|
+
return value;
|
|
9723
|
+
}
|
|
9724
|
+
buildDemoRequestOptions(path, headers) {
|
|
9725
|
+
const dbValue = this.database || {};
|
|
9726
|
+
const isDbEnabled = !!this.DBState?.checked;
|
|
9727
|
+
return RequestOptions.adapt({
|
|
9728
|
+
path,
|
|
9729
|
+
headers,
|
|
9730
|
+
forceRefresh: false,
|
|
9731
|
+
watchParams: isDbEnabled ? this.parseWatchParams(dbValue?.watchParams) : [],
|
|
9732
|
+
watchExpiresAt: isDbEnabled ? this.normalizeWatchExpiry(dbValue?.watchExpiresAt) : undefined,
|
|
9733
|
+
});
|
|
9734
|
+
}
|
|
8851
9735
|
onSetStateOptions() {
|
|
8852
|
-
|
|
9736
|
+
const dbValue = this.database;
|
|
9737
|
+
console.log('[onSetStateOptions] Called, checking form values:', {
|
|
9738
|
+
isValid: this.isValid,
|
|
9739
|
+
database: dbValue,
|
|
9740
|
+
hasTable: !!dbValue?.table,
|
|
9741
|
+
tableValue: dbValue?.table,
|
|
9742
|
+
dataType: this.dataType
|
|
9743
|
+
});
|
|
9744
|
+
if (!this.isValid) {
|
|
9745
|
+
console.log('[onSetStateOptions] Form invalid, aborting');
|
|
8853
9746
|
return;
|
|
9747
|
+
}
|
|
8854
9748
|
const reqParams = this.compileRequest();
|
|
8855
|
-
const db = DatabaseStorage.adapt(
|
|
9749
|
+
const db = DatabaseStorage.adapt(dbValue);
|
|
9750
|
+
console.log('[onSetStateOptions] DatabaseStorage.adapt result:', db);
|
|
8856
9751
|
const type = this.dataType === "ARRAY" ? DataType.ARRAY : DataType.OBJECT;
|
|
9752
|
+
console.log('[onSetStateOptions] Calling setAPIOptions with:', { db, type, hasTable: !!db?.table });
|
|
8857
9753
|
this.stateManagerDemoService.setAPIOptions(reqParams.apiOptions, type, db);
|
|
8858
9754
|
this.requestForm.markAsPristine();
|
|
8859
9755
|
}
|
|
@@ -8862,7 +9758,9 @@ class RequestManagerStateDemoComponent {
|
|
|
8862
9758
|
}
|
|
8863
9759
|
onGetRequest() {
|
|
8864
9760
|
this.requestType = 'GET';
|
|
8865
|
-
this.
|
|
9761
|
+
const reqParams = this.compileRequest();
|
|
9762
|
+
const requestOptions = this.buildDemoRequestOptions(reqParams.path, reqParams.apiOptions.headers);
|
|
9763
|
+
this.stateManagerDemoService.getClients(requestOptions);
|
|
8866
9764
|
}
|
|
8867
9765
|
onCreateRequest() {
|
|
8868
9766
|
this.requestType = 'POST';
|
|
@@ -8877,19 +9775,22 @@ class RequestManagerStateDemoComponent {
|
|
|
8877
9775
|
this.stateManagerDemoService.deleteClient(this.sampleClientData);
|
|
8878
9776
|
}
|
|
8879
9777
|
onStreamRequest() {
|
|
8880
|
-
console.log('[COMPONENT] onStreamRequest called')
|
|
9778
|
+
// console.log('[COMPONENT] onStreamRequest called')
|
|
8881
9779
|
if (!this.isValid) {
|
|
8882
|
-
console.log('[COMPONENT] Form invalid, aborting')
|
|
9780
|
+
// console.log('[COMPONENT] Form invalid, aborting')
|
|
8883
9781
|
return;
|
|
8884
9782
|
}
|
|
8885
|
-
console.log('[COMPONENT] Compiling request...')
|
|
9783
|
+
// console.log('[COMPONENT] Compiling request...')
|
|
8886
9784
|
const reqParams = this.compileRequest();
|
|
8887
|
-
console.log('[COMPONENT] Setting stream options:', reqParams.apiOptions)
|
|
9785
|
+
// console.log('[COMPONENT] Setting stream options:', reqParams.apiOptions)
|
|
8888
9786
|
reqParams.apiOptions.stream = true;
|
|
8889
9787
|
reqParams.apiOptions.streamType = this.streamType;
|
|
8890
9788
|
this.requestType = 'STREAM';
|
|
8891
|
-
|
|
8892
|
-
|
|
9789
|
+
const streamOptions = RequestOptions.adapt({
|
|
9790
|
+
...this.buildDemoRequestOptions(reqParams.path, reqParams.apiOptions.headers),
|
|
9791
|
+
});
|
|
9792
|
+
// console.log('[COMPONENT] Calling streamRequest...')
|
|
9793
|
+
this.stateManagerDemoService.streamRequest(streamOptions);
|
|
8893
9794
|
}
|
|
8894
9795
|
errorHandling(err, type) {
|
|
8895
9796
|
console.log(err, type);
|
|
@@ -8913,11 +9814,11 @@ class RequestManagerStateDemoComponent {
|
|
|
8913
9814
|
this.prompts = [];
|
|
8914
9815
|
}
|
|
8915
9816
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RequestManagerStateDemoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
8916
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: RequestManagerStateDemoComponent, selector: "app-request-manager-state-demo", inputs: { server: "server", adapter: "adapter", mapper: "mapper" }, providers: [StateManagerDemoService, HTTPManagerService], 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 @for (adapter of sampleAdaptors; track adapter) {\n <mat-option [value]=\"adapter.value\">\n {{adapter.label}}\n </mat-option>\n }\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 @for (mapper of sampleMappers; track mapper) {\n <mat-option [value]=\"mapper.value\">\n {{mapper.label}}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n @if (adapterSelect.value || mapperSelect.value) {\n <div style=\"display: flex; margin-bottom: 2rem;\">\n <div style=\"flex:1\" class=\"box\">\n <h3>Adapter (Incoming)</h3>\n @if (adapterSelect.value) {\n <div>\n {{ props(adapterSelect.value) | json }}\n </div>\n } @else {\n No Transformation\n }\n </div>\n <div style=\"flex:1\" class=\"box\">\n <h3>Mapper (Outgoing)</h3>\n @if (mapperSelect.value) {\n <div>\n {{ props(mapperSelect.value) | json }}\n </div>\n } @else {\n No Transformation\n }\n </div>\n </div>\n }\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 @for (task of headers.controls; track task; let i = $index) {\n <div [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 }\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 @if (failedState.checked) {\n <div 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 }\n </div>\n <div>\n <mat-slide-toggle #pollingState>Polling</mat-slide-toggle>\n @if (pollingState.checked) {\n <div>\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 }\n </div>\n <div>\n <mat-slide-toggle #DBState>Database Storage</mat-slide-toggle>\n @if (DBState.checked) {\n <div 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 }\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 @if ((error$ | async); as error) {\n <mat-error>\n {{ error }}\n </mat-error>\n }\n </div>\n </div>\n </div>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n @if ((isPending$ | async)) {\n <mat-progress-bar mode=\"indeterminate\"\n ></mat-progress-bar>\n }\n @if (pollingState.checked) {\n <div>\n @if (!(isPending$ | async) && (this.countdown$ | async) || -1 > 0) {\n <mat-progress-bar mode=\"determinate\"\n [value]=\"(this.countdown$ | async)\"\n ></mat-progress-bar>\n }\n </div>\n }\n\n </div>\n\n @if ((GET$ | async); as data) {\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1;\">CURRENT STATE ({{ dataType }})</h2>\n </div>\n @if (data.length > 1) {\n <div>\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 @for (item of data; track item) {\n <mat-option [value]=\"item\">\n {{item.name || (item.first_name) | titlecase}}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n }\n @if ((dataObservable$ | async); as dataRecord) {\n <div>\n @if ((selectedRecord$ | async); as record) {\n <div>\n {{ record | json }}\n </div>\n } @else {\n No Record Selected from State\n }\n </div>\n }\n <div style=\"margin-top: 1rem;\">\n @if (data !== null && data?.length > 0) {\n <span>\n State contains a Total of {{ data.length }} Records\n </span>\n } @else {\n No Records\n }\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 </div>\n </div>\n </div>\n }\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 @if ((GET_error$ | async); as get_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ get_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(GET$ | async) | jsonv\"></div> -->\n @if ((GET$ | async); as getData) {\n <div>{{ getData | json }}</div>\n }\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 @if ((POST_error$ | async); as post_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ post_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(POST$ | async) | jsonv\"></div> -->\n @if ((POST$ | async); as postData) {\n <div>{{ postData | json }}</div>\n }\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 @if ((PUT_error$ | async); as put_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ put_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(PUT$ | async) | jsonv\"></div> -->\n @if ((PUT$ | async); as putData) {\n <div>{{ putData | json }}</div>\n }\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 @if ((DELETE_error$ | async); as delete_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ delete_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(DELETE$ | async) | jsonv\"></div> -->\n @if ((DELETE$ | async); as deleteData) {\n <div>{{ deleteData | json }}</div>\n }\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 style=\"display: flex; gap: 1rem; align-items: center;\">\n <div style=\"display: flex; gap: 1rem; align-items: center;\">\n {{ streamType }}\n <button mat-icon-button [matMenuTriggerFor]=\"menu\">\n <mat-icon>data_usage</mat-icon>\n </button>\n </div>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item *ngFor=\"let item of streamTypes\" (click)=\"onStreamType(item.id)\">{{ item.value }}</button>\n </mat-menu>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\" [disabled]=\"hasChanged\">Request</button>\n </div>\n </div>\n\n @if ((STREAM_error$ | async); as stream_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n @if ((STREAM$ | async); as data) {\n <div class=\"container\">\n <table mat-table [dataSource]=\"data\" class=\"mat-elevation-z8\">\n\n <!-- Dynamic columns -->\n <ng-container *ngFor=\"let column of displayedColumns\" [matColumnDef]=\"column\">\n <th mat-header-cell *matHeaderCellDef> {{ column | titlecase }} </th>\n <td mat-cell *matCellDef=\"let element\">\n @if (isObject(element[column]); as objValue) {\n <pre style=\"margin: 0; font-size: 0.8em; white-space: pre-wrap;\">{{ objValue | json }}</pre>\n } @else {\n {{ element[column] }}\n }\n </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\n <!-- Debug info -->\n <div style=\"margin-top: 1rem; font-size: 0.8em; color: #666;\">\n Columns: {{ displayedColumns.join(', ') }} | Data received\n </div>\n </div>\n }\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: i2$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i2$1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i2$1.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3.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: i8$1.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" }] }); }
|
|
9817
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: RequestManagerStateDemoComponent, selector: "app-request-manager-state-demo", inputs: { server: "server", adapter: "adapter", mapper: "mapper" }, providers: [StateManagerDemoService, HTTPManagerService], viewQueries: [{ propertyName: "failedState", first: true, predicate: ["failedState"], descendants: true, static: true }, { propertyName: "pollingState", first: true, predicate: ["pollingState"], descendants: true, static: true }, { propertyName: "DBState", first: true, predicate: ["DBState"], 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 @for (adapter of sampleAdaptors; track adapter) {\n <mat-option [value]=\"adapter.value\">\n {{adapter.label}}\n </mat-option>\n }\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 @for (mapper of sampleMappers; track mapper) {\n <mat-option [value]=\"mapper.value\">\n {{mapper.label}}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n @if (adapterSelect.value || mapperSelect.value) {\n <div style=\"display: flex; margin-bottom: 2rem;\">\n <div style=\"flex:1\" class=\"box\">\n <h3>Adapter (Incoming)</h3>\n @if (adapterSelect.value) {\n <div>\n {{ props(adapterSelect.value) | json }}\n </div>\n } @else {\n No Transformation\n }\n </div>\n <div style=\"flex:1\" class=\"box\">\n <h3>Mapper (Outgoing)</h3>\n @if (mapperSelect.value) {\n <div>\n {{ props(mapperSelect.value) | json }}\n </div>\n } @else {\n No Transformation\n }\n </div>\n </div>\n }\n\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\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 @for (task of headers.controls; track task; let i = $index) {\n <div [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 }\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 @if (failedState.checked) {\n <div 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 }\n </div>\n <div>\n <mat-slide-toggle #pollingState>Polling</mat-slide-toggle>\n @if (pollingState.checked) {\n <div>\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 }\n </div>\n <div>\n <mat-slide-toggle #DBState>Database Storage</mat-slide-toggle>\n @if (DBState.checked) {\n <div style=\"margin-top: 1rem\" formGroupName=\"database\">\n <div style=\"display: flex; gap: .5rem;\">\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 style=\"display: flex; gap: .5rem;\">\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Watch Params (comma delimited)</mat-label>\n <input matInput placeholder=\"sort,active\" formControlName=\"watchParams\" value=\"\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Watch Param Expiry</mat-label>\n <mat-select formControlName=\"watchExpiresAt\">\n <mat-option value=\"never\">Never</mat-option>\n <mat-option value=\"10s\">10 Seconds</mat-option>\n <mat-option value=\"30s\">30 Seconds</mat-option>\n <mat-option value=\"1m\">1 Minute</mat-option>\n <mat-option value=\"5m\">5 Minutes</mat-option>\n <mat-option value=\"10m\">10 Minutes</mat-option>\n <mat-option value=\"1h\">1 Hour</mat-option>\n <mat-option value=\"1d\">1 Day</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n }\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 @if ((error$ | async); as error) {\n <mat-error>\n {{ error }}\n </mat-error>\n }\n </div>\n </div>\n </div>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n @if ((isPending$ | async)) {\n <mat-progress-bar mode=\"indeterminate\"\n ></mat-progress-bar>\n }\n @if (pollingState.checked) {\n <div>\n @if (!(isPending$ | async) && (this.countdown$ | async) || -1 > 0) {\n <mat-progress-bar mode=\"determinate\"\n [value]=\"(this.countdown$ | async)\"\n ></mat-progress-bar>\n }\n </div>\n }\n\n </div>\n\n @if ((GET$ | async); as data) {\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1;\">CURRENT STATE ({{ dataType }})</h2>\n </div>\n @if (data.length > 1) {\n <div>\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 @for (item of data; track item) {\n <mat-option [value]=\"item\">\n {{item.name || (item.first_name) | titlecase}}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n }\n @if ((dataObservable$ | async); as dataRecord) {\n <div>\n @if ((selectedRecord$ | async); as record) {\n <div>\n {{ record | json }}\n </div>\n } @else {\n No Record Selected from State\n }\n </div>\n }\n <div style=\"margin-top: 1rem;\">\n @if (data !== null && data?.length > 0) {\n <span>\n State contains a Total of {{ data.length }} Records\n </span>\n } @else {\n No Records\n }\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 </div>\n </div>\n </div>\n }\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 @if ((GET_error$ | async); as get_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ get_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(GET$ | async) | jsonv\"></div> -->\n @if ((GET$ | async); as getData) {\n <div>{{ getData | json }}</div>\n }\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 @if ((POST_error$ | async); as post_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ post_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(POST$ | async) | jsonv\"></div> -->\n @if ((POST$ | async); as postData) {\n <div>{{ postData | json }}</div>\n }\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 @if ((PUT_error$ | async); as put_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ put_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(PUT$ | async) | jsonv\"></div> -->\n @if ((PUT$ | async); as putData) {\n <div>{{ putData | json }}</div>\n }\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 @if ((DELETE_error$ | async); as delete_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ delete_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(DELETE$ | async) | jsonv\"></div> -->\n @if ((DELETE$ | async); as deleteData) {\n <div>{{ deleteData | json }}</div>\n }\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 style=\"display: flex; gap: 1rem; align-items: center;\">\n <div style=\"display: flex; gap: 1rem; align-items: center;\">\n {{ streamType }}\n <button mat-icon-button [matMenuTriggerFor]=\"menu\">\n <mat-icon>data_usage</mat-icon>\n </button>\n </div>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item *ngFor=\"let item of streamTypes\" (click)=\"onStreamType(item.id)\">{{ item.value }}</button>\n </mat-menu>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\" [disabled]=\"hasChanged\">Request</button>\n </div>\n </div>\n\n @if ((STREAM_error$ | async); as stream_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n @if ((STREAM$ | async); as data) {\n <div class=\"container\">\n <table mat-table [dataSource]=\"data\" class=\"mat-elevation-z8\">\n\n <!-- Dynamic columns -->\n <ng-container *ngFor=\"let column of displayedColumns\" [matColumnDef]=\"column\">\n <th mat-header-cell *matHeaderCellDef> {{ column | titlecase }} </th>\n <td mat-cell *matCellDef=\"let element\">\n @if (isObject(element[column]); as objValue) {\n <pre style=\"margin: 0; font-size: 0.8em; white-space: pre-wrap;\">{{ objValue | json }}</pre>\n } @else {\n {{ element[column] }}\n }\n </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\n <!-- Debug info -->\n <div style=\"margin-top: 1rem; font-size: 0.8em; color: #666;\">\n Columns: {{ displayedColumns.join(', ') }} | Data received\n </div>\n </div>\n }\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: i2$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i2$1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i2$1.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3.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: i8$1.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" }] }); }
|
|
8917
9818
|
}
|
|
8918
9819
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RequestManagerStateDemoComponent, decorators: [{
|
|
8919
9820
|
type: Component,
|
|
8920
|
-
args: [{ selector: 'app-request-manager-state-demo', providers: [StateManagerDemoService, HTTPManagerService], standalone: false, 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 @for (adapter of sampleAdaptors; track adapter) {\n <mat-option [value]=\"adapter.value\">\n {{adapter.label}}\n </mat-option>\n }\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 @for (mapper of sampleMappers; track mapper) {\n <mat-option [value]=\"mapper.value\">\n {{mapper.label}}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n @if (adapterSelect.value || mapperSelect.value) {\n <div style=\"display: flex; margin-bottom: 2rem;\">\n <div style=\"flex:1\" class=\"box\">\n <h3>Adapter (Incoming)</h3>\n @if (adapterSelect.value) {\n <div>\n {{ props(adapterSelect.value) | json }}\n </div>\n } @else {\n No Transformation\n }\n </div>\n <div style=\"flex:1\" class=\"box\">\n <h3>Mapper (Outgoing)</h3>\n @if (mapperSelect.value) {\n <div>\n {{ props(mapperSelect.value) | json }}\n </div>\n } @else {\n No Transformation\n }\n </div>\n </div>\n }\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 @for (task of headers.controls; track task; let i = $index) {\n <div [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 }\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 @if (failedState.checked) {\n <div 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 }\n </div>\n <div>\n <mat-slide-toggle #pollingState>Polling</mat-slide-toggle>\n @if (pollingState.checked) {\n <div>\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 }\n </div>\n <div>\n <mat-slide-toggle #DBState>Database Storage</mat-slide-toggle>\n @if (DBState.checked) {\n <div 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 }\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 @if ((error$ | async); as error) {\n <mat-error>\n {{ error }}\n </mat-error>\n }\n </div>\n </div>\n </div>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n @if ((isPending$ | async)) {\n <mat-progress-bar mode=\"indeterminate\"\n ></mat-progress-bar>\n }\n @if (pollingState.checked) {\n <div>\n @if (!(isPending$ | async) && (this.countdown$ | async) || -1 > 0) {\n <mat-progress-bar mode=\"determinate\"\n [value]=\"(this.countdown$ | async)\"\n ></mat-progress-bar>\n }\n </div>\n }\n\n </div>\n\n @if ((GET$ | async); as data) {\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1;\">CURRENT STATE ({{ dataType }})</h2>\n </div>\n @if (data.length > 1) {\n <div>\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 @for (item of data; track item) {\n <mat-option [value]=\"item\">\n {{item.name || (item.first_name) | titlecase}}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n }\n @if ((dataObservable$ | async); as dataRecord) {\n <div>\n @if ((selectedRecord$ | async); as record) {\n <div>\n {{ record | json }}\n </div>\n } @else {\n No Record Selected from State\n }\n </div>\n }\n <div style=\"margin-top: 1rem;\">\n @if (data !== null && data?.length > 0) {\n <span>\n State contains a Total of {{ data.length }} Records\n </span>\n } @else {\n No Records\n }\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 </div>\n </div>\n </div>\n }\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 @if ((GET_error$ | async); as get_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ get_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(GET$ | async) | jsonv\"></div> -->\n @if ((GET$ | async); as getData) {\n <div>{{ getData | json }}</div>\n }\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 @if ((POST_error$ | async); as post_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ post_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(POST$ | async) | jsonv\"></div> -->\n @if ((POST$ | async); as postData) {\n <div>{{ postData | json }}</div>\n }\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 @if ((PUT_error$ | async); as put_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ put_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(PUT$ | async) | jsonv\"></div> -->\n @if ((PUT$ | async); as putData) {\n <div>{{ putData | json }}</div>\n }\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 @if ((DELETE_error$ | async); as delete_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ delete_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(DELETE$ | async) | jsonv\"></div> -->\n @if ((DELETE$ | async); as deleteData) {\n <div>{{ deleteData | json }}</div>\n }\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 style=\"display: flex; gap: 1rem; align-items: center;\">\n <div style=\"display: flex; gap: 1rem; align-items: center;\">\n {{ streamType }}\n <button mat-icon-button [matMenuTriggerFor]=\"menu\">\n <mat-icon>data_usage</mat-icon>\n </button>\n </div>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item *ngFor=\"let item of streamTypes\" (click)=\"onStreamType(item.id)\">{{ item.value }}</button>\n </mat-menu>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\" [disabled]=\"hasChanged\">Request</button>\n </div>\n </div>\n\n @if ((STREAM_error$ | async); as stream_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n @if ((STREAM$ | async); as data) {\n <div class=\"container\">\n <table mat-table [dataSource]=\"data\" class=\"mat-elevation-z8\">\n\n <!-- Dynamic columns -->\n <ng-container *ngFor=\"let column of displayedColumns\" [matColumnDef]=\"column\">\n <th mat-header-cell *matHeaderCellDef> {{ column | titlecase }} </th>\n <td mat-cell *matCellDef=\"let element\">\n @if (isObject(element[column]); as objValue) {\n <pre style=\"margin: 0; font-size: 0.8em; white-space: pre-wrap;\">{{ objValue | json }}</pre>\n } @else {\n {{ element[column] }}\n }\n </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\n <!-- Debug info -->\n <div style=\"margin-top: 1rem; font-size: 0.8em; color: #666;\">\n Columns: {{ displayedColumns.join(', ') }} | Data received\n </div>\n </div>\n }\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"] }]
|
|
9821
|
+
args: [{ selector: 'app-request-manager-state-demo', providers: [StateManagerDemoService, HTTPManagerService], standalone: false, 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 @for (adapter of sampleAdaptors; track adapter) {\n <mat-option [value]=\"adapter.value\">\n {{adapter.label}}\n </mat-option>\n }\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 @for (mapper of sampleMappers; track mapper) {\n <mat-option [value]=\"mapper.value\">\n {{mapper.label}}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n @if (adapterSelect.value || mapperSelect.value) {\n <div style=\"display: flex; margin-bottom: 2rem;\">\n <div style=\"flex:1\" class=\"box\">\n <h3>Adapter (Incoming)</h3>\n @if (adapterSelect.value) {\n <div>\n {{ props(adapterSelect.value) | json }}\n </div>\n } @else {\n No Transformation\n }\n </div>\n <div style=\"flex:1\" class=\"box\">\n <h3>Mapper (Outgoing)</h3>\n @if (mapperSelect.value) {\n <div>\n {{ props(mapperSelect.value) | json }}\n </div>\n } @else {\n No Transformation\n }\n </div>\n </div>\n }\n\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\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 @for (task of headers.controls; track task; let i = $index) {\n <div [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 }\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 @if (failedState.checked) {\n <div 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 }\n </div>\n <div>\n <mat-slide-toggle #pollingState>Polling</mat-slide-toggle>\n @if (pollingState.checked) {\n <div>\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 }\n </div>\n <div>\n <mat-slide-toggle #DBState>Database Storage</mat-slide-toggle>\n @if (DBState.checked) {\n <div style=\"margin-top: 1rem\" formGroupName=\"database\">\n <div style=\"display: flex; gap: .5rem;\">\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 style=\"display: flex; gap: .5rem;\">\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Watch Params (comma delimited)</mat-label>\n <input matInput placeholder=\"sort,active\" formControlName=\"watchParams\" value=\"\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Watch Param Expiry</mat-label>\n <mat-select formControlName=\"watchExpiresAt\">\n <mat-option value=\"never\">Never</mat-option>\n <mat-option value=\"10s\">10 Seconds</mat-option>\n <mat-option value=\"30s\">30 Seconds</mat-option>\n <mat-option value=\"1m\">1 Minute</mat-option>\n <mat-option value=\"5m\">5 Minutes</mat-option>\n <mat-option value=\"10m\">10 Minutes</mat-option>\n <mat-option value=\"1h\">1 Hour</mat-option>\n <mat-option value=\"1d\">1 Day</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n }\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 @if ((error$ | async); as error) {\n <mat-error>\n {{ error }}\n </mat-error>\n }\n </div>\n </div>\n </div>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n @if ((isPending$ | async)) {\n <mat-progress-bar mode=\"indeterminate\"\n ></mat-progress-bar>\n }\n @if (pollingState.checked) {\n <div>\n @if (!(isPending$ | async) && (this.countdown$ | async) || -1 > 0) {\n <mat-progress-bar mode=\"determinate\"\n [value]=\"(this.countdown$ | async)\"\n ></mat-progress-bar>\n }\n </div>\n }\n\n </div>\n\n @if ((GET$ | async); as data) {\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1;\">CURRENT STATE ({{ dataType }})</h2>\n </div>\n @if (data.length > 1) {\n <div>\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 @for (item of data; track item) {\n <mat-option [value]=\"item\">\n {{item.name || (item.first_name) | titlecase}}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n }\n @if ((dataObservable$ | async); as dataRecord) {\n <div>\n @if ((selectedRecord$ | async); as record) {\n <div>\n {{ record | json }}\n </div>\n } @else {\n No Record Selected from State\n }\n </div>\n }\n <div style=\"margin-top: 1rem;\">\n @if (data !== null && data?.length > 0) {\n <span>\n State contains a Total of {{ data.length }} Records\n </span>\n } @else {\n No Records\n }\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 </div>\n </div>\n </div>\n }\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 @if ((GET_error$ | async); as get_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ get_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(GET$ | async) | jsonv\"></div> -->\n @if ((GET$ | async); as getData) {\n <div>{{ getData | json }}</div>\n }\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 @if ((POST_error$ | async); as post_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ post_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(POST$ | async) | jsonv\"></div> -->\n @if ((POST$ | async); as postData) {\n <div>{{ postData | json }}</div>\n }\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 @if ((PUT_error$ | async); as put_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ put_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(PUT$ | async) | jsonv\"></div> -->\n @if ((PUT$ | async); as putData) {\n <div>{{ putData | json }}</div>\n }\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 @if ((DELETE_error$ | async); as delete_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ delete_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(DELETE$ | async) | jsonv\"></div> -->\n @if ((DELETE$ | async); as deleteData) {\n <div>{{ deleteData | json }}</div>\n }\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 style=\"display: flex; gap: 1rem; align-items: center;\">\n <div style=\"display: flex; gap: 1rem; align-items: center;\">\n {{ streamType }}\n <button mat-icon-button [matMenuTriggerFor]=\"menu\">\n <mat-icon>data_usage</mat-icon>\n </button>\n </div>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item *ngFor=\"let item of streamTypes\" (click)=\"onStreamType(item.id)\">{{ item.value }}</button>\n </mat-menu>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\" [disabled]=\"hasChanged\">Request</button>\n </div>\n </div>\n\n @if ((STREAM_error$ | async); as stream_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n @if ((STREAM$ | async); as data) {\n <div class=\"container\">\n <table mat-table [dataSource]=\"data\" class=\"mat-elevation-z8\">\n\n <!-- Dynamic columns -->\n <ng-container *ngFor=\"let column of displayedColumns\" [matColumnDef]=\"column\">\n <th mat-header-cell *matHeaderCellDef> {{ column | titlecase }} </th>\n <td mat-cell *matCellDef=\"let element\">\n @if (isObject(element[column]); as objValue) {\n <pre style=\"margin: 0; font-size: 0.8em; white-space: pre-wrap;\">{{ objValue | json }}</pre>\n } @else {\n {{ element[column] }}\n }\n </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\n <!-- Debug info -->\n <div style=\"margin-top: 1rem; font-size: 0.8em; color: #666;\">\n Columns: {{ displayedColumns.join(', ') }} | Data received\n </div>\n </div>\n }\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"] }]
|
|
8921
9822
|
}], ctorParameters: () => [], propDecorators: { server: [{
|
|
8922
9823
|
type: Input
|
|
8923
9824
|
}], adapter: [{
|
|
@@ -8930,6 +9831,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
8930
9831
|
}], pollingState: [{
|
|
8931
9832
|
type: ViewChild,
|
|
8932
9833
|
args: ["pollingState", { static: true }]
|
|
9834
|
+
}], DBState: [{
|
|
9835
|
+
type: ViewChild,
|
|
9836
|
+
args: ["DBState", { static: true }]
|
|
8933
9837
|
}] } });
|
|
8934
9838
|
|
|
8935
9839
|
class RequestManagerDemoComponent {
|
|
@@ -9934,6 +10838,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
9934
10838
|
}]
|
|
9935
10839
|
}] });
|
|
9936
10840
|
|
|
10841
|
+
class UserData {
|
|
10842
|
+
constructor(ldap = '', name = '', email = '', color = RandomPaletteColor()) {
|
|
10843
|
+
this.ldap = ldap;
|
|
10844
|
+
this.name = name;
|
|
10845
|
+
this.email = email;
|
|
10846
|
+
this.color = color;
|
|
10847
|
+
}
|
|
10848
|
+
static adapt(item) {
|
|
10849
|
+
const userName = `${item?.first_name} ${item?.last_name}`;
|
|
10850
|
+
return new UserData(item?.ldap, userName, item?.email, item?.color);
|
|
10851
|
+
}
|
|
10852
|
+
}
|
|
10853
|
+
|
|
9937
10854
|
class StateDataRequestService extends HTTPManagerStateService {
|
|
9938
10855
|
constructor() {
|
|
9939
10856
|
super(ApiRequest.adapt({
|
|
@@ -11763,5 +12680,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
11763
12680
|
* Generated bundle index. Do not edit.
|
|
11764
12681
|
*/
|
|
11765
12682
|
|
|
11766
|
-
export { ApiRequest, AppService, AsymmetricalEncryptionService, BatchOptions, BatchResult, CONFIG_SETTINGS_TOKEN, ChannelType, ConfigHTTPOptions, ConfigOptions, DataType, DatabaseDataDemoComponent, DatabaseManagerService, DatabaseStorage, DbService, ErrorDisplaySettings, GlobalStoreOptions, HTTPManagerService, HTTPManagerSignalsService, HTTPManagerStateService, HeadersService, HttpRequestManagerModule, HttpRequestServicesDemoComponent, InvalidFileInfoModel, LocalStorageDemoComponent, LocalStorageManagerService, LocalStorageOptions, LocalStorageSignalsDemoComponent, LocalStorageSignalsManagerService, LoggerService, NotificationMessage, OperationResultModel, PathQueryService, PublicMessage, Random, RandomHSLColor, RandomHexColor, RandomNumber, RandomNumbers, RandomNumbersUnique, RandomPaletteColor, RandomSignature, RandomStr, RandomVisibleColor, RequestErrorInterceptor, RequestHeadersInterceptor, RequestManagerDemoComponent, RequestManagerStateDemoComponent, RequestOptions, RequestService, RequestSignalsService, RetryOptions, SettingOptions, StateMessage, StateOperationResult, StateStorageOptions, StorageData, StorageOption, StorageType, StoreStateManagerService, StoreStateManagerSignalsService, StoreStateSignalsDemoComponent, StreamType, SymmetricalEncryptionService, TableSchemaDef, UUID, UUID_STR, UploadDemoComponent, UploadValidationErrorModel, UserData, UtilsService, WSOptions, WebSocketMessageService, WithCredentialsInterceptor, calculateBatchProgress, countdown, createChannelName, delayedRetry, isErrorState, isPendingState, isSuccessState, requestPolling, requestStreaming, streamAI, streamAuto, streamEvents, streamJSON, streamNDJSON };
|
|
12683
|
+
export { ApiRequest, AppService, AsymmetricalEncryptionService, BatchOptions, BatchResult, CONFIG_SETTINGS_TOKEN, ChannelType, ConfigHTTPOptions, ConfigOptions, DataType, DatabaseDataDemoComponent, DatabaseManagerService, DatabaseStorage, DbService, ErrorDisplaySettings, GlobalStoreOptions, HTTPManagerService, HTTPManagerSignalsService, HTTPManagerStateService, HeadersService, HttpRequestManagerModule, HttpRequestServicesDemoComponent, InvalidFileInfoModel, LocalStorageDemoComponent, LocalStorageManagerService, LocalStorageOptions, LocalStorageSignalsDemoComponent, LocalStorageSignalsManagerService, LoggerService, NormalizedRequestOptionsModel, NotificationMessage, OperationResultModel, PathQueryService, PathTrackerStateModel, PublicMessage, QueryParamsTrackerOptionsModel, QueryParamsTrackerService, QueryTrackerStateModel, Random, RandomHSLColor, RandomHexColor, RandomNumber, RandomNumbers, RandomNumbersUnique, RandomPaletteColor, RandomSignature, RandomStr, RandomVisibleColor, RequestErrorInterceptor, RequestHeadersInterceptor, RequestManagerDemoComponent, RequestManagerStateDemoComponent, RequestOptions, RequestService, RequestSignalsService, RetryOptions, SettingOptions, StateMessage, StateOperationResult, StateStorageOptions, StorageData, StorageOption, StorageType, StoreStateManagerService, StoreStateManagerSignalsService, StoreStateSignalsDemoComponent, StreamType, SymmetricalEncryptionService, TableSchemaDef, UUID, UUID_STR, UploadDemoComponent, UploadValidationErrorModel, UserData, UtilsService, WSOptions, WebSocketMessageService, WithCredentialsInterceptor, calculateBatchProgress, countdown, createChannelName, delayedRetry, isErrorState, isPendingState, isSuccessState, requestPolling, requestStreaming, streamAI, streamAuto, streamEvents, streamJSON, streamNDJSON };
|
|
11767
12684
|
//# sourceMappingURL=http-request-manager.mjs.map
|