http-request-manager 18.16.1 → 18.16.3

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.
@@ -190,19 +190,32 @@ class UtilsService {
190
190
  switch (type) {
191
191
  case 'y':
192
192
  const years = parseInt(str.slice(0, -1));
193
- value = now + (years * 31556926);
193
+ const dateY = new Date();
194
+ dateY.setFullYear(dateY.getFullYear() + years, 0, 1);
195
+ dateY.setHours(0, 0, 0, 0);
196
+ value = Math.floor(dateY.getTime() / 1000);
194
197
  break;
195
198
  case 'm':
196
199
  const months = parseInt(str.slice(0, -1));
197
- value = now + (months * 2629743);
200
+ const dateM = new Date();
201
+ dateM.setMonth(dateM.getMonth() + months, 1);
202
+ dateM.setHours(0, 0, 0, 0);
203
+ value = Math.floor(dateM.getTime() / 1000);
198
204
  break;
199
205
  case 'w':
200
206
  const weeks = parseInt(str.slice(0, -1));
201
- value = now + (weeks * 604800);
207
+ const dateW = new Date();
208
+ const daysUntilSunday = (7 - dateW.getDay()) % 7;
209
+ dateW.setDate(dateW.getDate() + daysUntilSunday + 1 + (weeks - 1) * 7);
210
+ dateW.setHours(0, 0, 0, 0);
211
+ value = Math.floor(dateW.getTime() / 1000);
202
212
  break;
203
213
  case 'd':
204
214
  const days = parseInt(str.slice(0, -1));
205
- value = now + (days * 86400);
215
+ const dateD = new Date();
216
+ dateD.setDate(dateD.getDate() + days);
217
+ dateD.setHours(0, 0, 0, 0);
218
+ value = Math.floor(dateD.getTime() / 1000);
206
219
  break;
207
220
  case 'hr':
208
221
  const hrs = parseInt(str.slice(0, -2));
@@ -1859,6 +1872,7 @@ class WebSocketManagerService {
1859
1872
  this.maxRetries$ = WebSocketManagerService.maxRetriesSubject.asObservable();
1860
1873
  this.messages$ = WebSocketManagerService.messages.asObservable();
1861
1874
  this.connectionStatus$ = WebSocketManagerService.connectionStatus.asObservable();
1875
+ this.connectionError$ = WebSocketManagerService.connectionError.asObservable();
1862
1876
  this.subscribedChannels$ = WebSocketManagerService.subscribedChannels.asObservable();
1863
1877
  this.onReconnect$ = WebSocketManagerService.onReconnect.asObservable();
1864
1878
  }
@@ -1884,6 +1898,7 @@ class WebSocketManagerService {
1884
1898
  // ═══════════════════════════════════════════════════════════════════════════
1885
1899
  static { this.messages = new BehaviorSubject(null); }
1886
1900
  static { this.connectionStatus = new BehaviorSubject(false); }
1901
+ static { this.connectionError = new BehaviorSubject(null); }
1887
1902
  static { this.isSubscribed = false; }
1888
1903
  // Track currently subscribed channels
1889
1904
  static { this.subscribedChannels = new BehaviorSubject(new Set()); }
@@ -1926,6 +1941,7 @@ class WebSocketManagerService {
1926
1941
  // Cancel any pending retry timer before attempting a new connection
1927
1942
  WebSocketManagerService.retrySubscription?.unsubscribe();
1928
1943
  WebSocketManagerService.retrySubscription = null;
1944
+ WebSocketManagerService.connectionError.next(null);
1929
1945
  // Check if connection already exists and is open
1930
1946
  if (WebSocketManagerService.socket) {
1931
1947
  if (WebSocketManagerService.socket.readyState === WebSocket.OPEN) {
@@ -1994,9 +2010,11 @@ class WebSocketManagerService {
1994
2010
  const data = JSON.parse(event.data);
1995
2011
  if (data.error) {
1996
2012
  WebSocketManagerService.messages.next(data);
1997
- // Log the server error and close — onclose will drive the retry logic
1998
2013
  if (data.error === 'JWT_INVALID' || data.error === 'AUTH_BLOCKED') {
1999
- console.error(`🚫 ${data.error}: ${data.message || 'Authentication error'} — will retry`);
2014
+ console.error(`🚫 ${data.error}: ${data.message || 'Authentication error'} — stopping, no retry`);
2015
+ WebSocketManagerService.lastOptions = null;
2016
+ WebSocketManagerService.connectionError.next('Invalid or Expired JWT');
2017
+ WebSocketManagerService.connectionStatus.next(false);
2000
2018
  }
2001
2019
  else {
2002
2020
  console.error(`❌ Server error: ${data.error} — will retry`);
@@ -2029,6 +2047,11 @@ class WebSocketManagerService {
2029
2047
  WebSocketManagerService.connectionReadyNotified = false;
2030
2048
  WebSocketManagerService.socket = null;
2031
2049
  WebSocketManagerService.subscribedChannels.next(new Set());
2050
+ if (event.code === 4000 || event.code === 4001 || event.code === 4003) {
2051
+ WebSocketManagerService.connectionError.next('Invalid or Expired JWT');
2052
+ WebSocketManagerService.connectionStatus.next(false);
2053
+ return;
2054
+ }
2032
2055
  const maxRetries = WebSocketManagerService.maxRetries;
2033
2056
  const retryDelay = WebSocketManagerService.retryDelay;
2034
2057
  if (WebSocketManagerService.lastOptions && WebSocketManagerService.retryCount < maxRetries) {
@@ -3773,6 +3796,7 @@ class HTTPManagerService extends RequestService {
3773
3796
  this.subscribedChannels$ = this.wsManager.subscribedChannels$;
3774
3797
  this.retryCount$ = this.wsManager.retryCount$;
3775
3798
  this.maxRetries$ = this.wsManager.maxRetries$;
3799
+ this.connectionError$ = this.wsManager.connectionError$;
3776
3800
  this.countdown = new BehaviorSubject(0);
3777
3801
  this.countdown$ = this.countdown.asObservable();
3778
3802
  this.error = new BehaviorSubject(false);
@@ -3930,13 +3954,15 @@ class HTTPManagerService extends RequestService {
3930
3954
  return this.createObservable(updatedOptions, requests, func.name)
3931
3955
  .pipe(tap(data => {
3932
3956
  this.data.next(data);
3933
- if (updatedOptions.displaySuccess)
3957
+ if (updatedOptions.displaySuccess && !updatedOptions.stream)
3934
3958
  this.handleSuccessWithSnackBar(updatedOptions.successMessage);
3935
3959
  }), finalize(() => {
3936
3960
  this.streamProgress.next({
3937
3961
  ...this.streamProgress.value,
3938
3962
  stage: 'complete'
3939
3963
  });
3964
+ if (updatedOptions.displaySuccess && updatedOptions.stream)
3965
+ this.handleSuccessWithSnackBar(updatedOptions.successMessage);
3940
3966
  this.isPending.next(false);
3941
3967
  }), catchError((err) => {
3942
3968
  if (updatedOptions.displayError)
@@ -4366,7 +4392,9 @@ class RequestSignalsService extends WebsocketService {
4366
4392
  }
4367
4393
  requestStreamingOperator(options) {
4368
4394
  return (source$) => {
4369
- return source$.pipe(map(output => {
4395
+ return source$.pipe(tap(output => {
4396
+ this.progress.set(output.progress.received);
4397
+ }), map(output => {
4370
4398
  const data = output.data;
4371
4399
  if (!data || (Array.isArray(data) && data.length === 0)) {
4372
4400
  return data;
@@ -4726,9 +4754,13 @@ class HTTPManagerSignalsService extends RequestSignalsService {
4726
4754
  const requests = this.createRequest(func, updatedOptions);
4727
4755
  return this.createObservable(updatedOptions, requests, func.name).pipe(tap(data => {
4728
4756
  this.data.set(data);
4729
- if (updatedOptions.displaySuccess)
4757
+ if (updatedOptions.displaySuccess && !updatedOptions.stream)
4730
4758
  this.handleSuccessWithSnackBar(updatedOptions.successMessage);
4731
- }), finalize(() => this.isPending.set(false)), catchError((err) => {
4759
+ }), finalize(() => {
4760
+ if (updatedOptions.displaySuccess && updatedOptions.stream)
4761
+ this.handleSuccessWithSnackBar(updatedOptions.successMessage);
4762
+ this.isPending.set(false);
4763
+ }), catchError((err) => {
4732
4764
  if (updatedOptions.displayError)
4733
4765
  this.handleErrorWithSnackBar(err, updatedOptions.errorMessage || err?.message);
4734
4766
  this.isPending.set(false);
@@ -6471,19 +6503,16 @@ class DatabaseManagerService extends DbService {
6471
6503
  }
6472
6504
  clearTable(table) {
6473
6505
  const tableName = this.cleanTableName(table);
6506
+ this.localStorageManager.deleteStore({ name: tableName });
6474
6507
  try {
6475
6508
  const tableInstance = this.table(tableName);
6476
6509
  if (!tableInstance) {
6477
6510
  console.warn(`clearTable: Table '${tableName}' not found`);
6478
6511
  return of([]);
6479
6512
  }
6480
- // console.log(`clearTable: Clearing table '${tableName}'...`);
6481
- // Use table.clear() directly and wrap in Observable
6482
6513
  return from(Promise.resolve().then(() => {
6483
6514
  return tableInstance.clear();
6484
- })).pipe(
6485
- // tap(() => console.log(`clearTable: ✅ Table '${tableName}' cleared successfully`)),
6486
- map(() => []));
6515
+ })).pipe(map(() => []));
6487
6516
  }
6488
6517
  catch (error) {
6489
6518
  console.error(`clearTable: ❌ Error:`, error);
@@ -6631,6 +6660,7 @@ class HTTPManagerStateService extends ComponentStore {
6631
6660
  this.connectionStatus$ = this.httpManagerService.connectionStatus$;
6632
6661
  this.wsRetryCount$ = this.httpManagerService.retryCount$;
6633
6662
  this.wsMaxRetries$ = this.httpManagerService.maxRetries$;
6663
+ this.connectionError$ = this.httpManagerService.connectionError$;
6634
6664
  // WebSocket
6635
6665
  this.initWS = this.effect((wsOptions$) => wsOptions$.pipe(switchMap((wsOptions) => merge(this.httpManagerService.connectionStatus$.pipe(tap((isConnected) => {
6636
6666
  if (isConnected) {
@@ -8110,7 +8140,6 @@ class HTTPManagerStateService extends ComponentStore {
8110
8140
  this._requestCachePaths.set(tableName, this.resolvePath(options?.path).filter(p => typeof p === 'string' || typeof p === 'number').map(String));
8111
8141
  this.localStorageManagerService.store$(tableName).pipe(take(1), tap((storeData) => {
8112
8142
  const currentCache = storeData?.requestCache || {};
8113
- const currentEntry = currentCache[type] || {};
8114
8143
  this.localStorageManagerService.updateStore({
8115
8144
  name: tableName,
8116
8145
  data: {
@@ -8118,13 +8147,11 @@ class HTTPManagerStateService extends ComponentStore {
8118
8147
  requestCache: {
8119
8148
  ...currentCache,
8120
8149
  [type]: {
8121
- ...currentEntry,
8150
+ ...(currentCache[type] || {}),
8122
8151
  signature,
8123
8152
  savedAt: Date.now(),
8124
8153
  path: this.resolvePath(options?.path),
8125
- headers: this.filterHeaders(options?.headers),
8126
- queryParams: currentEntry.queryParams,
8127
- queryParamsExpires: currentEntry.queryParamsExpires,
8154
+ headers: this.filterHeaders(options?.headers)
8128
8155
  }
8129
8156
  }
8130
8157
  }
@@ -8266,53 +8293,12 @@ class HTTPManagerStateService extends ComponentStore {
8266
8293
  const ignoreQueryParams = Array.isArray(options?.ignoreQueryParams) ? options.ignoreQueryParams : [];
8267
8294
  if (!normalized.hasQuery) {
8268
8295
  const meta = this.getRequestCacheMetadata(storeData, type);
8269
- if (!meta) {
8270
- // No prior cache entry — record that we're tracking this request
8271
- this.setCachedRequestSignature(tableName, type, '');
8272
- this.localStorageManagerService.store$(tableName).pipe(take(1), tap((s) => {
8273
- const currentCache = s?.requestCache || {};
8274
- this.localStorageManagerService.updateStore({
8275
- name: tableName,
8276
- data: {
8277
- ...(s || {}),
8278
- requestCache: {
8279
- ...currentCache,
8280
- [type]: {
8281
- ...(currentCache[type] || {}),
8282
- active: true,
8283
- }
8284
- }
8285
- }
8286
- });
8287
- })).subscribe();
8288
- }
8289
8296
  return of(!meta);
8290
8297
  }
8291
8298
  const filtered = this.trackerFilterQuery(normalized.query, ignoreQueryParams);
8292
8299
  const keys = Object.keys(filtered);
8293
8300
  if (keys.length === 0) {
8294
- // All query params were filtered out — check if we have any prior entry
8295
- const meta = this.getRequestCacheMetadata(storeData, type);
8296
- if (!meta) {
8297
- this.setCachedRequestSignature(tableName, type, '');
8298
- this.localStorageManagerService.store$(tableName).pipe(take(1), tap((s) => {
8299
- const currentCache = s?.requestCache || {};
8300
- this.localStorageManagerService.updateStore({
8301
- name: tableName,
8302
- data: {
8303
- ...(s || {}),
8304
- requestCache: {
8305
- ...currentCache,
8306
- [type]: {
8307
- ...(currentCache[type] || {}),
8308
- active: true,
8309
- }
8310
- }
8311
- }
8312
- });
8313
- })).subscribe();
8314
- }
8315
- return of(!meta);
8301
+ return of(!this.getRequestCacheMetadata(storeData, type));
8316
8302
  }
8317
8303
  const meta = this.getRequestCacheMetadata(storeData, type) || {};
8318
8304
  const now = Math.floor(Date.now() / 1000);
@@ -9027,7 +9013,7 @@ class QueryPlanner {
9027
9013
  }
9028
9014
  else {
9029
9015
  const projected = columns
9030
- .filter((c) => c.expr?.type === 'column_ref')
9016
+ .filter((c) => c.expr?.type === 'column_ref' && c.expr?.column !== '*')
9031
9017
  .map((c) => c.expr.column);
9032
9018
  plan.projection = projected.length > 0 ? projected : null;
9033
9019
  }
@@ -12433,6 +12419,7 @@ class RequestManagerWsDemoComponent {
12433
12419
  this.retryCount$ = this.stateService.wsRetryCount$;
12434
12420
  this.maxRetries$ = this.stateService.wsMaxRetries$;
12435
12421
  this.connectionStatus$ = this.stateService.connectionStatus$;
12422
+ this.connectionError$ = this.stateService.connectionError$;
12436
12423
  this.data$ = this.stateService.data$;
12437
12424
  this.isPending$ = this.stateService.isPending$;
12438
12425
  }
@@ -12440,11 +12427,11 @@ class RequestManagerWsDemoComponent {
12440
12427
  this.stateService.updateConnection(this.server, this.wsServer, this.jwtToken, this.user, this.path);
12441
12428
  }
12442
12429
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RequestManagerWsDemoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
12443
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: RequestManagerWsDemoComponent, selector: "app-request-manager-ws-demo", inputs: { server: "server", wsServer: "wsServer", jwtToken: "jwtToken", user: "user", path: "path" }, ngImport: i0, template: "<div style=\"margin: 2rem;\">\n\n <h2 style=\"display: flex;\">\n <span style=\"flex:1\">HTTP Request State Manager - Websockets</span>\n @if ((connectionStatus$ | async); as connected) {\n <span>\n WS -\n <span style=\"color: green;\">Connected</span>\n </span>\n } @else {\n <span style=\"color: red;\">Disconnected {{ retryCount$ | async }} / {{ maxRetries$ | async }}</span>\n }\n </h2>\n\n <div>\n\n @if ((user$ | async); as userInfo) {\n <div>\n <mat-toolbar>\n <div style=\"display: flex; flex:1\">\n <div style=\"flex:1\">{{ userInfo.name }}</div>\n <div>({{ userInfo.ldap }})</div>\n </div>\n </mat-toolbar>\n </div>\n }\n\n @if (!(connectionStatus$ | async) && ((retryCount$ | async) ?? 0) < ((maxRetries$ | async) ?? 0)) {\n <div>\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n </div>\n }\n\n <mat-tab-group animationDuration=\"0ms\" [selectedIndex]=\"1\">\n\n <mat-tab label=\"WS - Data Control\" [disabled]=\"!(connectionStatus$ | async)\">\n <!-- DATA CONTROL -->\n <app-ws-data-control\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n ></app-ws-data-control>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Messaging\" [disabled]=\"!(connectionStatus$ | async)\">\n <!-- MESSAGING -->\n <app-ws-messaging\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n ></app-ws-messaging>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Notifications\" [disabled]=\"!(connectionStatus$ | async)\">\n <!-- WS - Notifications -->\n <app-ws-notifications\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n ></app-ws-notifications>\n </mat-tab>\n\n <mat-tab label=\"WS - Chats\" [disabled]=\"true\">\n <!-- WS - Chats -->\n <app-ws-chats></app-ws-chats>\n </mat-tab>\n\n <mat-tab label=\"WS - AI Messaging\" [disabled]=\"true\">\n <!-- WS - AI Messaging -->\n <app-ws-ai-messaging></app-ws-ai-messaging>\n </mat-tab>\n\n </mat-tab-group>\n</div>\n\n</div>\n\n", styles: [""], dependencies: [{ kind: "component", type: i1$2.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass"], exportAs: ["matTab"] }, { kind: "component", type: i1$2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "component", type: i8$1.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "component", type: i3$2.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "component", type: WsDataControlComponent, selector: "app-ws-data-control", inputs: ["server", "wsServer", "jwtToken", "user", "path"] }, { kind: "component", type: WsMessagingComponent, selector: "app-ws-messaging", inputs: ["server", "wsServer", "jwtToken", "user", "path"] }, { kind: "component", type: WsNotificationsComponent, selector: "app-ws-notifications", inputs: ["server", "wsServer", "jwtToken", "user"] }, { kind: "component", type: WsAiMessagingComponent, selector: "app-ws-ai-messaging" }, { kind: "component", type: WsChatsComponent, selector: "app-ws-chats" }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }] }); }
12430
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: RequestManagerWsDemoComponent, selector: "app-request-manager-ws-demo", inputs: { server: "server", wsServer: "wsServer", jwtToken: "jwtToken", user: "user", path: "path" }, ngImport: i0, template: "<div style=\"margin: 2rem;\">\n\n <h2 style=\"display: flex;\">\n <span style=\"flex:1\">HTTP Request State Manager - Websockets</span>\n @if ((connectionStatus$ | async); as connected) {\n <span>\n WS -\n <span style=\"color: green;\">Connected</span>\n </span>\n } @else {\n <span style=\"color: red;\">Disconnected {{ retryCount$ | async }} / {{ maxRetries$ | async }}</span>\n }\n </h2>\n\n <div>\n\n @if ((user$ | async); as userInfo) {\n <div>\n <mat-toolbar>\n <div style=\"display: flex; flex:1\">\n <div style=\"flex:1\">{{ userInfo.name }}</div>\n <div>({{ userInfo.ldap }})</div>\n </div>\n </mat-toolbar>\n </div>\n }\n\n @if (!(connectionStatus$ | async) && !(connectionError$ | async) && ((retryCount$ | async) ?? 0) < ((maxRetries$ | async) ?? 0)) {\n <div>\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n </div>\n }\n\n @if (connectionError$ | async; as errorMsg) {\n <div style=\"padding: 0.75rem 1rem; background: #fdecea; color: #b00020; border-left: 4px solid #b00020; border-radius: 2px; margin-bottom: 0.5rem;\">\n {{ errorMsg }}\n </div>\n }\n\n <mat-tab-group animationDuration=\"0ms\" [selectedIndex]=\"1\">\n\n <mat-tab label=\"WS - Data Control\" [disabled]=\"!(connectionStatus$ | async)\">\n <!-- DATA CONTROL -->\n <app-ws-data-control\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n ></app-ws-data-control>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Messaging\" [disabled]=\"!(connectionStatus$ | async)\">\n <!-- MESSAGING -->\n <app-ws-messaging\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n ></app-ws-messaging>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Notifications\" [disabled]=\"!(connectionStatus$ | async)\">\n <!-- WS - Notifications -->\n <app-ws-notifications\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n ></app-ws-notifications>\n </mat-tab>\n\n <mat-tab label=\"WS - Chats\" [disabled]=\"true\">\n <!-- WS - Chats -->\n <app-ws-chats></app-ws-chats>\n </mat-tab>\n\n <mat-tab label=\"WS - AI Messaging\" [disabled]=\"true\">\n <!-- WS - AI Messaging -->\n <app-ws-ai-messaging></app-ws-ai-messaging>\n </mat-tab>\n\n </mat-tab-group>\n</div>\n\n</div>\n\n", styles: [""], dependencies: [{ kind: "component", type: i1$2.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass"], exportAs: ["matTab"] }, { kind: "component", type: i1$2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "component", type: i8$1.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "component", type: i3$2.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "component", type: WsDataControlComponent, selector: "app-ws-data-control", inputs: ["server", "wsServer", "jwtToken", "user", "path"] }, { kind: "component", type: WsMessagingComponent, selector: "app-ws-messaging", inputs: ["server", "wsServer", "jwtToken", "user", "path"] }, { kind: "component", type: WsNotificationsComponent, selector: "app-ws-notifications", inputs: ["server", "wsServer", "jwtToken", "user"] }, { kind: "component", type: WsAiMessagingComponent, selector: "app-ws-ai-messaging" }, { kind: "component", type: WsChatsComponent, selector: "app-ws-chats" }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }] }); }
12444
12431
  }
12445
12432
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RequestManagerWsDemoComponent, decorators: [{
12446
12433
  type: Component,
12447
- args: [{ selector: 'app-request-manager-ws-demo', standalone: false, template: "<div style=\"margin: 2rem;\">\n\n <h2 style=\"display: flex;\">\n <span style=\"flex:1\">HTTP Request State Manager - Websockets</span>\n @if ((connectionStatus$ | async); as connected) {\n <span>\n WS -\n <span style=\"color: green;\">Connected</span>\n </span>\n } @else {\n <span style=\"color: red;\">Disconnected {{ retryCount$ | async }} / {{ maxRetries$ | async }}</span>\n }\n </h2>\n\n <div>\n\n @if ((user$ | async); as userInfo) {\n <div>\n <mat-toolbar>\n <div style=\"display: flex; flex:1\">\n <div style=\"flex:1\">{{ userInfo.name }}</div>\n <div>({{ userInfo.ldap }})</div>\n </div>\n </mat-toolbar>\n </div>\n }\n\n @if (!(connectionStatus$ | async) && ((retryCount$ | async) ?? 0) < ((maxRetries$ | async) ?? 0)) {\n <div>\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n </div>\n }\n\n <mat-tab-group animationDuration=\"0ms\" [selectedIndex]=\"1\">\n\n <mat-tab label=\"WS - Data Control\" [disabled]=\"!(connectionStatus$ | async)\">\n <!-- DATA CONTROL -->\n <app-ws-data-control\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n ></app-ws-data-control>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Messaging\" [disabled]=\"!(connectionStatus$ | async)\">\n <!-- MESSAGING -->\n <app-ws-messaging\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n ></app-ws-messaging>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Notifications\" [disabled]=\"!(connectionStatus$ | async)\">\n <!-- WS - Notifications -->\n <app-ws-notifications\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n ></app-ws-notifications>\n </mat-tab>\n\n <mat-tab label=\"WS - Chats\" [disabled]=\"true\">\n <!-- WS - Chats -->\n <app-ws-chats></app-ws-chats>\n </mat-tab>\n\n <mat-tab label=\"WS - AI Messaging\" [disabled]=\"true\">\n <!-- WS - AI Messaging -->\n <app-ws-ai-messaging></app-ws-ai-messaging>\n </mat-tab>\n\n </mat-tab-group>\n</div>\n\n</div>\n\n" }]
12434
+ args: [{ selector: 'app-request-manager-ws-demo', standalone: false, template: "<div style=\"margin: 2rem;\">\n\n <h2 style=\"display: flex;\">\n <span style=\"flex:1\">HTTP Request State Manager - Websockets</span>\n @if ((connectionStatus$ | async); as connected) {\n <span>\n WS -\n <span style=\"color: green;\">Connected</span>\n </span>\n } @else {\n <span style=\"color: red;\">Disconnected {{ retryCount$ | async }} / {{ maxRetries$ | async }}</span>\n }\n </h2>\n\n <div>\n\n @if ((user$ | async); as userInfo) {\n <div>\n <mat-toolbar>\n <div style=\"display: flex; flex:1\">\n <div style=\"flex:1\">{{ userInfo.name }}</div>\n <div>({{ userInfo.ldap }})</div>\n </div>\n </mat-toolbar>\n </div>\n }\n\n @if (!(connectionStatus$ | async) && !(connectionError$ | async) && ((retryCount$ | async) ?? 0) < ((maxRetries$ | async) ?? 0)) {\n <div>\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n </div>\n }\n\n @if (connectionError$ | async; as errorMsg) {\n <div style=\"padding: 0.75rem 1rem; background: #fdecea; color: #b00020; border-left: 4px solid #b00020; border-radius: 2px; margin-bottom: 0.5rem;\">\n {{ errorMsg }}\n </div>\n }\n\n <mat-tab-group animationDuration=\"0ms\" [selectedIndex]=\"1\">\n\n <mat-tab label=\"WS - Data Control\" [disabled]=\"!(connectionStatus$ | async)\">\n <!-- DATA CONTROL -->\n <app-ws-data-control\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n ></app-ws-data-control>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Messaging\" [disabled]=\"!(connectionStatus$ | async)\">\n <!-- MESSAGING -->\n <app-ws-messaging\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n ></app-ws-messaging>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Notifications\" [disabled]=\"!(connectionStatus$ | async)\">\n <!-- WS - Notifications -->\n <app-ws-notifications\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n ></app-ws-notifications>\n </mat-tab>\n\n <mat-tab label=\"WS - Chats\" [disabled]=\"true\">\n <!-- WS - Chats -->\n <app-ws-chats></app-ws-chats>\n </mat-tab>\n\n <mat-tab label=\"WS - AI Messaging\" [disabled]=\"true\">\n <!-- WS - AI Messaging -->\n <app-ws-ai-messaging></app-ws-ai-messaging>\n </mat-tab>\n\n </mat-tab-group>\n</div>\n\n</div>\n\n" }]
12448
12435
  }], propDecorators: { server: [{
12449
12436
  type: Input
12450
12437
  }], wsServer: [{
@@ -12674,11 +12661,11 @@ class DatabaseDataDemoComponent {
12674
12661
  });
12675
12662
  }
12676
12663
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DatabaseDataDemoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
12677
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: DatabaseDataDemoComponent, selector: "app-database-data-demo", ngImport: i0, template: "<div style=\"padding: 2rem;\">\n\n <h2>\n Database Manager Service Demo\n </h2>\n\n <div style=\"margin-bottom: 1rem; display: flex; gap: 1rem;\">\n <button mat-stroked-button\n color=\"primary\"\n (click)=\"clearAllData()\"\n >\n Clear Data\n </button>\n <button mat-stroked-button\n color=\"primary\"\n (click)=\"updateData()\"\n >\n Update Data\n </button>\n <div style=\"flex:1\"></div>\n <button\n mat-stroked-button\n color=\"warn\"\n [disabled]=\"!dataToDisplay.length\"\n (click)=\"removeData()\"\n >\n Remove Data\n </button>\n <button mat-stroked-button\n color=\"primary\"\n (click)=\"addData()\"\n >\n Add Data\n </button>\n </div>\n\n <div class=\"table-container\">\n <table mat-table [dataSource]=\"dataSource\">\n\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef>ID</th>\n <td mat-cell *matCellDef=\"let element\">{{element.id}}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"last_name\">\n <th mat-header-cell *matHeaderCellDef>Last Name</th>\n <td mat-cell *matCellDef=\"let element\">{{element.last_name}}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"age\">\n <th mat-header-cell *matHeaderCellDef>Age</th>\n <td mat-cell *matCellDef=\"let element\">{{element.age}}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"amount\">\n <th mat-header-cell *matHeaderCellDef>Amount</th>\n <td mat-cell *matCellDef=\"let element\">{{element.amount | currency}}</td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n\n <!-- \u2500\u2500 SQL Query Section \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <mat-divider style=\"margin: 2rem 0;\"></mat-divider>\n\n <h3 style=\"margin-bottom: 1rem;\">SQL Query with DexieJS</h3>\n\n <div style=\"display: flex; gap: 1rem; align-items: flex-start;\">\n <mat-form-field style=\"flex: 1;\" appearance=\"outline\">\n <mat-label>SQL Query</mat-label>\n <textarea\n matInput\n [(ngModel)]=\"sqlQuery\"\n rows=\"3\"\n placeholder=\"e.g. SELECT * FROM sample_table WHERE age > 20\"\n (keydown.control.enter)=\"runSqlQuery()\"\n ></textarea>\n <mat-hint>Ctrl+Enter to execute</mat-hint>\n </mat-form-field>\n\n <button\n mat-flat-button\n color=\"primary\"\n style=\"margin-top: 4px;\"\n [disabled]=\"sqlLoading || !sqlQuery.trim()\"\n (click)=\"runSqlQuery()\"\n >\n {{ sqlLoading ? 'Running\u2026' : 'Execute' }}\n </button>\n </div>\n\n <!-- Error -->\n <div *ngIf=\"sqlError\" style=\"color: #d32f2f; margin: 0.5rem 0 1rem; font-size: 0.875rem;\">\n <mat-icon style=\"vertical-align: middle; font-size: 16px; height: 16px; width: 16px;\">error</mat-icon>\n {{ sqlError }}\n </div>\n\n <!-- Results table (dynamic columns) -->\n <div *ngIf=\"!sqlLoading && sqlResults.length > 0\" class=\"table-container\" style=\"margin-top: 1rem;\">\n <div>\n {{ sqlResults | json }}\n </div>\n <p style=\"font-size: 0.8rem; color: #666; margin-top: 0.5rem;\">\n {{ sqlResults.length }} row{{ sqlResults.length === 1 ? '' : 's' }} returned\n </p>\n </div>\n\n <!-- Empty state -->\n <p *ngIf=\"!sqlLoading && !sqlError && sqlResults.length === 0 && sqlQuery.trim()\"\n style=\"color: #888; font-size: 0.875rem; margin-top: 0.5rem;\">\n No results to display. Execute a query above.\n </p>\n\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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: 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.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { 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: 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.JsonPipe, name: "json" }, { kind: "pipe", type: i1$1.CurrencyPipe, name: "currency" }] }); }
12664
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: DatabaseDataDemoComponent, selector: "app-database-data-demo", ngImport: i0, template: "<div style=\"padding: 2rem;\">\n\n <h2>\n Database Manager Service Demo\n </h2>\n\n <div style=\"margin-bottom: 1rem; display: flex; gap: 1rem;\">\n <button mat-stroked-button\n color=\"primary\"\n (click)=\"clearAllData()\"\n >\n Clear Data\n </button>\n <button mat-stroked-button\n color=\"primary\"\n (click)=\"updateData()\"\n >\n Update Data\n </button>\n <div style=\"flex:1\"></div>\n <button\n mat-stroked-button\n color=\"warn\"\n [disabled]=\"!dataToDisplay.length\"\n (click)=\"removeData()\"\n >\n Remove Data\n </button>\n <button mat-stroked-button\n color=\"primary\"\n (click)=\"addData()\"\n >\n Add Data\n </button>\n </div>\n\n <div class=\"table-container\">\n <table mat-table [dataSource]=\"dataSource\">\n\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef>ID</th>\n <td mat-cell *matCellDef=\"let element\">{{element.id}}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"last_name\">\n <th mat-header-cell *matHeaderCellDef>Last Name</th>\n <td mat-cell *matCellDef=\"let element\">{{element.last_name}}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"age\">\n <th mat-header-cell *matHeaderCellDef>Age</th>\n <td mat-cell *matCellDef=\"let element\">{{element.age}}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"amount\">\n <th mat-header-cell *matHeaderCellDef>Amount</th>\n <td mat-cell *matCellDef=\"let element\">{{element.amount | currency}}</td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n\n <!-- \u2500\u2500 SQL Query Section \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <mat-divider style=\"margin: 2rem 0;\"></mat-divider>\n\n <h3 style=\"margin-bottom: 1rem;\">SQL Query with DexieJS</h3>\n\n <div style=\"display: flex; gap: 1rem; align-items: flex-start;\">\n <mat-form-field style=\"flex: 1;\" appearance=\"outline\">\n <mat-label>SQL Query</mat-label>\n <textarea\n matInput\n [(ngModel)]=\"sqlQuery\"\n rows=\"3\"\n placeholder=\"e.g. SELECT * FROM sample_table WHERE age > 20\"\n (keydown.control.enter)=\"runSqlQuery()\"\n ></textarea>\n </mat-form-field>\n </div>\n\n <div style=\"display: flex;\">\n <div style=\"flex:1\"></div>\n <button\n mat-flat-button\n color=\"primary\"\n style=\"margin-top: 4px;\"\n [disabled]=\"sqlLoading || !sqlQuery.trim()\"\n (click)=\"runSqlQuery()\"\n >\n {{ sqlLoading ? 'Running\u2026' : 'Execute' }}\n </button>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <!-- Error -->\n <div *ngIf=\"sqlError\" style=\"color: #d32f2f; margin: 0.5rem 0 1rem; font-size: 0.875rem;\">\n <mat-icon style=\"vertical-align: middle; font-size: 16px; height: 16px; width: 16px;\">error</mat-icon>\n {{ sqlError }}\n </div>\n\n <!-- Results table (dynamic columns) -->\n <div *ngIf=\"!sqlLoading && sqlResults.length > 0\" class=\"table-container\" style=\"margin-top: 1rem;\">\n <div>\n {{ sqlResults | json }}\n </div>\n <p style=\"font-size: 0.8rem; color: #666; margin-top: 0.5rem;\">\n {{ sqlResults.length }} row{{ sqlResults.length === 1 ? '' : 's' }} returned\n </p>\n </div>\n\n <!-- Empty state -->\n <p *ngIf=\"!sqlLoading && !sqlError && sqlResults.length === 0 && sqlQuery.trim()\"\n style=\"color: #888; font-size: 0.875rem; margin-top: 0.5rem;\">\n No results to display. Execute a query above.\n </p>\n\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { 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: 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.JsonPipe, name: "json" }, { kind: "pipe", type: i1$1.CurrencyPipe, name: "currency" }] }); }
12678
12665
  }
12679
12666
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DatabaseDataDemoComponent, decorators: [{
12680
12667
  type: Component,
12681
- args: [{ selector: 'app-database-data-demo', standalone: false, template: "<div style=\"padding: 2rem;\">\n\n <h2>\n Database Manager Service Demo\n </h2>\n\n <div style=\"margin-bottom: 1rem; display: flex; gap: 1rem;\">\n <button mat-stroked-button\n color=\"primary\"\n (click)=\"clearAllData()\"\n >\n Clear Data\n </button>\n <button mat-stroked-button\n color=\"primary\"\n (click)=\"updateData()\"\n >\n Update Data\n </button>\n <div style=\"flex:1\"></div>\n <button\n mat-stroked-button\n color=\"warn\"\n [disabled]=\"!dataToDisplay.length\"\n (click)=\"removeData()\"\n >\n Remove Data\n </button>\n <button mat-stroked-button\n color=\"primary\"\n (click)=\"addData()\"\n >\n Add Data\n </button>\n </div>\n\n <div class=\"table-container\">\n <table mat-table [dataSource]=\"dataSource\">\n\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef>ID</th>\n <td mat-cell *matCellDef=\"let element\">{{element.id}}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"last_name\">\n <th mat-header-cell *matHeaderCellDef>Last Name</th>\n <td mat-cell *matCellDef=\"let element\">{{element.last_name}}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"age\">\n <th mat-header-cell *matHeaderCellDef>Age</th>\n <td mat-cell *matCellDef=\"let element\">{{element.age}}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"amount\">\n <th mat-header-cell *matHeaderCellDef>Amount</th>\n <td mat-cell *matCellDef=\"let element\">{{element.amount | currency}}</td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n\n <!-- \u2500\u2500 SQL Query Section \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <mat-divider style=\"margin: 2rem 0;\"></mat-divider>\n\n <h3 style=\"margin-bottom: 1rem;\">SQL Query with DexieJS</h3>\n\n <div style=\"display: flex; gap: 1rem; align-items: flex-start;\">\n <mat-form-field style=\"flex: 1;\" appearance=\"outline\">\n <mat-label>SQL Query</mat-label>\n <textarea\n matInput\n [(ngModel)]=\"sqlQuery\"\n rows=\"3\"\n placeholder=\"e.g. SELECT * FROM sample_table WHERE age > 20\"\n (keydown.control.enter)=\"runSqlQuery()\"\n ></textarea>\n <mat-hint>Ctrl+Enter to execute</mat-hint>\n </mat-form-field>\n\n <button\n mat-flat-button\n color=\"primary\"\n style=\"margin-top: 4px;\"\n [disabled]=\"sqlLoading || !sqlQuery.trim()\"\n (click)=\"runSqlQuery()\"\n >\n {{ sqlLoading ? 'Running\u2026' : 'Execute' }}\n </button>\n </div>\n\n <!-- Error -->\n <div *ngIf=\"sqlError\" style=\"color: #d32f2f; margin: 0.5rem 0 1rem; font-size: 0.875rem;\">\n <mat-icon style=\"vertical-align: middle; font-size: 16px; height: 16px; width: 16px;\">error</mat-icon>\n {{ sqlError }}\n </div>\n\n <!-- Results table (dynamic columns) -->\n <div *ngIf=\"!sqlLoading && sqlResults.length > 0\" class=\"table-container\" style=\"margin-top: 1rem;\">\n <div>\n {{ sqlResults | json }}\n </div>\n <p style=\"font-size: 0.8rem; color: #666; margin-top: 0.5rem;\">\n {{ sqlResults.length }} row{{ sqlResults.length === 1 ? '' : 's' }} returned\n </p>\n </div>\n\n <!-- Empty state -->\n <p *ngIf=\"!sqlLoading && !sqlError && sqlResults.length === 0 && sqlQuery.trim()\"\n style=\"color: #888; font-size: 0.875rem; margin-top: 0.5rem;\">\n No results to display. Execute a query above.\n </p>\n\n</div>\n" }]
12668
+ args: [{ selector: 'app-database-data-demo', standalone: false, template: "<div style=\"padding: 2rem;\">\n\n <h2>\n Database Manager Service Demo\n </h2>\n\n <div style=\"margin-bottom: 1rem; display: flex; gap: 1rem;\">\n <button mat-stroked-button\n color=\"primary\"\n (click)=\"clearAllData()\"\n >\n Clear Data\n </button>\n <button mat-stroked-button\n color=\"primary\"\n (click)=\"updateData()\"\n >\n Update Data\n </button>\n <div style=\"flex:1\"></div>\n <button\n mat-stroked-button\n color=\"warn\"\n [disabled]=\"!dataToDisplay.length\"\n (click)=\"removeData()\"\n >\n Remove Data\n </button>\n <button mat-stroked-button\n color=\"primary\"\n (click)=\"addData()\"\n >\n Add Data\n </button>\n </div>\n\n <div class=\"table-container\">\n <table mat-table [dataSource]=\"dataSource\">\n\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef>ID</th>\n <td mat-cell *matCellDef=\"let element\">{{element.id}}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"last_name\">\n <th mat-header-cell *matHeaderCellDef>Last Name</th>\n <td mat-cell *matCellDef=\"let element\">{{element.last_name}}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"age\">\n <th mat-header-cell *matHeaderCellDef>Age</th>\n <td mat-cell *matCellDef=\"let element\">{{element.age}}</td>\n </ng-container>\n\n <ng-container matColumnDef=\"amount\">\n <th mat-header-cell *matHeaderCellDef>Amount</th>\n <td mat-cell *matCellDef=\"let element\">{{element.amount | currency}}</td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n\n <!-- \u2500\u2500 SQL Query Section \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <mat-divider style=\"margin: 2rem 0;\"></mat-divider>\n\n <h3 style=\"margin-bottom: 1rem;\">SQL Query with DexieJS</h3>\n\n <div style=\"display: flex; gap: 1rem; align-items: flex-start;\">\n <mat-form-field style=\"flex: 1;\" appearance=\"outline\">\n <mat-label>SQL Query</mat-label>\n <textarea\n matInput\n [(ngModel)]=\"sqlQuery\"\n rows=\"3\"\n placeholder=\"e.g. SELECT * FROM sample_table WHERE age > 20\"\n (keydown.control.enter)=\"runSqlQuery()\"\n ></textarea>\n </mat-form-field>\n </div>\n\n <div style=\"display: flex;\">\n <div style=\"flex:1\"></div>\n <button\n mat-flat-button\n color=\"primary\"\n style=\"margin-top: 4px;\"\n [disabled]=\"sqlLoading || !sqlQuery.trim()\"\n (click)=\"runSqlQuery()\"\n >\n {{ sqlLoading ? 'Running\u2026' : 'Execute' }}\n </button>\n </div>\n\n <div style=\"margin-top: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <!-- Error -->\n <div *ngIf=\"sqlError\" style=\"color: #d32f2f; margin: 0.5rem 0 1rem; font-size: 0.875rem;\">\n <mat-icon style=\"vertical-align: middle; font-size: 16px; height: 16px; width: 16px;\">error</mat-icon>\n {{ sqlError }}\n </div>\n\n <!-- Results table (dynamic columns) -->\n <div *ngIf=\"!sqlLoading && sqlResults.length > 0\" class=\"table-container\" style=\"margin-top: 1rem;\">\n <div>\n {{ sqlResults | json }}\n </div>\n <p style=\"font-size: 0.8rem; color: #666; margin-top: 0.5rem;\">\n {{ sqlResults.length }} row{{ sqlResults.length === 1 ? '' : 's' }} returned\n </p>\n </div>\n\n <!-- Empty state -->\n <p *ngIf=\"!sqlLoading && !sqlError && sqlResults.length === 0 && sqlQuery.trim()\"\n style=\"color: #888; font-size: 0.875rem; margin-top: 0.5rem;\">\n No results to display. Execute a query above.\n </p>\n\n</div>\n" }]
12682
12669
  }], ctorParameters: () => [] });
12683
12670
  class DatabaseDataSource extends DataSource {
12684
12671
  constructor(initialData) {