http-request-manager 18.7.20 → 18.7.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/ARCHITECTURE.md +483 -0
  2. package/DATABASE_README.md +1176 -0
  3. package/HTTP_MANAGER_README.md +579 -0
  4. package/HTTP_SINGNALS_MANAGER_README.md +654 -0
  5. package/HTTP_STATE_MANAGER_README.md +948 -0
  6. package/INTERCEPTOR_README.md +549 -0
  7. package/LOCAL_STORAGE_README.md +1056 -0
  8. package/STORE_STATE_MANAGER_README.md +1322 -0
  9. package/UTILS_README.md +1186 -0
  10. package/WS_MANAGER_README.md +613 -0
  11. package/ng-package.json +8 -0
  12. package/package.json +1 -12
  13. package/src/lib/http-request-manager.module.ts +132 -0
  14. package/src/lib/http-request-services-demo/database-data-demo/database-data-demo.component.html +65 -0
  15. package/src/lib/http-request-services-demo/database-data-demo/database-data-demo.component.scss +0 -0
  16. package/src/lib/http-request-services-demo/database-data-demo/database-data-demo.component.ts +224 -0
  17. package/src/lib/http-request-services-demo/http-request-services-demo.component.html +114 -0
  18. package/src/lib/http-request-services-demo/http-request-services-demo.component.scss +6 -0
  19. package/src/lib/http-request-services-demo/http-request-services-demo.component.ts +52 -0
  20. package/src/lib/http-request-services-demo/local-storage-demo/local-storage-demo.component.html +195 -0
  21. package/src/lib/http-request-services-demo/local-storage-demo/local-storage-demo.component.scss +17 -0
  22. package/src/lib/http-request-services-demo/local-storage-demo/local-storage-demo.component.ts +206 -0
  23. package/src/lib/http-request-services-demo/local-storage-signals-demo/local-storage-signals-demo.component.html +200 -0
  24. package/src/lib/http-request-services-demo/local-storage-signals-demo/local-storage-signals-demo.component.scss +17 -0
  25. package/src/lib/http-request-services-demo/local-storage-signals-demo/local-storage-signals-demo.component.ts +212 -0
  26. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/download-file/download-file.component.html +53 -0
  27. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/download-file/download-file.component.scss +60 -0
  28. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/download-file/download-file.component.ts +72 -0
  29. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-download.module.ts +28 -0
  30. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-downloader.component.html +10 -0
  31. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-downloader.component.scss +29 -0
  32. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-downloader.component.ts +100 -0
  33. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/models/download-labels-model.ts +22 -0
  34. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/spinner/spinner.component.html +8 -0
  35. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/spinner/spinner.component.scss +19 -0
  36. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/spinner/spinner.component.ts +26 -0
  37. package/src/lib/http-request-services-demo/request-manager-demo/models/app-session.model.ts +30 -0
  38. package/src/lib/http-request-services-demo/request-manager-demo/models/app.model.ts +19 -0
  39. package/src/lib/http-request-services-demo/request-manager-demo/models/get-sample.model.ts +25 -0
  40. package/src/lib/http-request-services-demo/request-manager-demo/models/sample-ai-prompt.ts +19 -0
  41. package/src/lib/http-request-services-demo/request-manager-demo/models/sample-client-details.ts +24 -0
  42. package/src/lib/http-request-services-demo/request-manager-demo/models/sample-client-info.ts +30 -0
  43. package/src/lib/http-request-services-demo/request-manager-demo/models/sample-client.model.ts +49 -0
  44. package/src/lib/http-request-services-demo/request-manager-demo/models/sample-mapper-client-info.ts +33 -0
  45. package/src/lib/http-request-services-demo/request-manager-demo/request-manager-demo.component.html +392 -0
  46. package/src/lib/http-request-services-demo/request-manager-demo/request-manager-demo.component.scss +24 -0
  47. package/src/lib/http-request-services-demo/request-manager-demo/request-manager-demo.component.ts +461 -0
  48. package/src/lib/http-request-services-demo/request-manager-state-demo/request-manager-state-demo.component.html +393 -0
  49. package/src/lib/http-request-services-demo/request-manager-state-demo/request-manager-state-demo.component.scss +24 -0
  50. package/src/lib/http-request-services-demo/request-manager-state-demo/request-manager-state-demo.component.ts +421 -0
  51. package/src/lib/http-request-services-demo/request-manager-state-demo/services/state-manager-demo.service.ts +87 -0
  52. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/services/state-data-request.service.ts +120 -0
  53. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-ai-messaging/ws-ai-messaging.component.css +0 -0
  54. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-ai-messaging/ws-ai-messaging.component.html +3 -0
  55. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-ai-messaging/ws-ai-messaging.component.ts +16 -0
  56. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-chats/ws-chats.component.css +0 -0
  57. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-chats/ws-chats.component.html +3 -0
  58. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-chats/ws-chats.component.ts +16 -0
  59. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.css +31 -0
  60. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.html +72 -0
  61. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.scss +41 -0
  62. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.spec.ts +205 -0
  63. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.ts +77 -0
  64. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-messaging/ws-messaging.component.css +11 -0
  65. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-messaging/ws-messaging.component.html +96 -0
  66. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-messaging/ws-messaging.component.spec.ts +31 -0
  67. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-messaging/ws-messaging.component.ts +229 -0
  68. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-notifications/ws-notifications.component.css +30 -0
  69. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-notifications/ws-notifications.component.html +172 -0
  70. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-notifications/ws-notifications.component.spec.ts +31 -0
  71. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-notifications/ws-notifications.component.ts +239 -0
  72. package/src/lib/http-request-services-demo/request-manager-ws-demo/models/oidc-client.model.ts +31 -0
  73. package/src/lib/http-request-services-demo/request-manager-ws-demo/models/user-data.model.ts +32 -0
  74. package/src/lib/http-request-services-demo/request-manager-ws-demo/request-manager-ws-demo.component.css +0 -0
  75. package/src/lib/http-request-services-demo/request-manager-ws-demo/request-manager-ws-demo.component.html +84 -0
  76. package/src/lib/http-request-services-demo/request-manager-ws-demo/request-manager-ws-demo.component.ts +41 -0
  77. package/src/lib/http-request-services-demo/request-manager-ws-demo/services/index.ts +3 -0
  78. package/src/lib/http-request-services-demo/request-manager-ws-demo/services/message-service-demo.service.ts +83 -0
  79. package/src/lib/http-request-services-demo/request-manager-ws-demo/services/notification-service-demo.service.ts +147 -0
  80. package/src/lib/http-request-services-demo/request-manager-ws-demo/services/state-service-demo.service.ts +158 -0
  81. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/download-file/download-file.component.html +53 -0
  82. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/download-file/download-file.component.scss +60 -0
  83. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/download-file/download-file.component.ts +72 -0
  84. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/file-download.module.ts +28 -0
  85. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/file-downloader.component.html +10 -0
  86. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/file-downloader.component.scss +29 -0
  87. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/file-downloader.component.ts +100 -0
  88. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/models/download-labels-model.ts +22 -0
  89. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/spinner/spinner.component.html +8 -0
  90. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/spinner/spinner.component.scss +19 -0
  91. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/spinner/spinner.component.ts +26 -0
  92. package/src/lib/http-request-services-demo/request-signals-manager-demo/models/app-session.model.ts +30 -0
  93. package/src/lib/http-request-services-demo/request-signals-manager-demo/models/app.model.ts +19 -0
  94. package/src/lib/http-request-services-demo/request-signals-manager-demo/models/get-sample.model.ts +25 -0
  95. package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-ai-prompt.ts +19 -0
  96. package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-client-details.ts +24 -0
  97. package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-client-info.ts +30 -0
  98. package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-client.model.ts +49 -0
  99. package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-mapper-client-info.ts +33 -0
  100. package/src/lib/http-request-services-demo/request-signals-manager-demo/request-signals-manager-demo.component.html +380 -0
  101. package/src/lib/http-request-services-demo/request-signals-manager-demo/request-signals-manager-demo.component.scss +24 -0
  102. package/src/lib/http-request-services-demo/request-signals-manager-demo/request-signals-manager-demo.component.ts +410 -0
  103. package/src/lib/http-request-services-demo/store-state-manager-demo/models/settings.model.ts +28 -0
  104. package/src/lib/http-request-services-demo/store-state-manager-demo/services/settings-state.service.ts +48 -0
  105. package/src/lib/http-request-services-demo/store-state-manager-demo/store-state-manager-demo.component.css +0 -0
  106. package/src/lib/http-request-services-demo/store-state-manager-demo/store-state-manager-demo.component.html +23 -0
  107. package/src/lib/http-request-services-demo/store-state-manager-demo/store-state-manager-demo.component.ts +36 -0
  108. package/src/lib/index.ts +3 -0
  109. package/src/lib/interceptors/credentials.interceptor.ts +16 -0
  110. package/src/lib/interceptors/index.ts +6 -0
  111. package/src/lib/interceptors/models/error-settings.model.ts +22 -0
  112. package/src/lib/interceptors/models/index.ts +2 -0
  113. package/src/lib/interceptors/proxy-debugger.interceptor.ts +46 -0
  114. package/src/lib/interceptors/request-error.interceptor.ts +65 -0
  115. package/src/lib/interceptors/request-header.interceptor.ts +53 -0
  116. package/src/lib/models/config-http-options.model.ts +42 -0
  117. package/src/lib/models/config-local-storage-options.model.ts +27 -0
  118. package/src/lib/models/config-options.model.ts +27 -0
  119. package/src/lib/models/config-token.model.ts +9 -0
  120. package/src/lib/models/data-type.enum.ts +5 -0
  121. package/src/lib/models/database-storage.model.ts +24 -0
  122. package/src/lib/models/index.ts +12 -0
  123. package/src/lib/models/retry-options.model.ts +22 -0
  124. package/src/lib/services/database-manager-service/database.manager.service.ts +262 -0
  125. package/src/lib/services/database-manager-service/db.storage.service.ts +207 -0
  126. package/src/lib/services/database-manager-service/index.ts +4 -0
  127. package/src/lib/services/database-manager-service/models/index.ts +2 -0
  128. package/src/lib/services/database-manager-service/models/table-schema.ts +33 -0
  129. package/src/lib/services/index.ts +12 -0
  130. package/src/lib/services/local-storage-manager-service/index.ts +4 -0
  131. package/src/lib/services/local-storage-manager-service/local-storage-manager.service.spec.ts +71 -0
  132. package/src/lib/services/local-storage-manager-service/local-storage-manager.service.ts +426 -0
  133. package/src/lib/services/local-storage-manager-service/local-storage-signals-manager.service.spec.ts +67 -0
  134. package/src/lib/services/local-storage-manager-service/local-storage-signals-manager.service.ts +345 -0
  135. package/src/lib/services/local-storage-manager-service/models/global-store-options.model.ts +30 -0
  136. package/src/lib/services/local-storage-manager-service/models/index.ts +6 -0
  137. package/src/lib/services/local-storage-manager-service/models/setting-options.model.ts +35 -0
  138. package/src/lib/services/local-storage-manager-service/models/storage-data.model.ts +24 -0
  139. package/src/lib/services/local-storage-manager-service/models/storage-option.model.ts +32 -0
  140. package/src/lib/services/local-storage-manager-service/models/storage-type.enum.ts +5 -0
  141. package/src/lib/services/request-manager-services/README.md +268 -0
  142. package/src/lib/services/request-manager-services/http-manager-signals.service.ts +246 -0
  143. package/src/lib/services/request-manager-services/http-manager.service.spec.ts +232 -0
  144. package/src/lib/services/request-manager-services/http-manager.service.ts +274 -0
  145. package/src/lib/services/request-manager-services/index.ts +8 -0
  146. package/src/lib/services/request-manager-services/request-signals.service.ts +214 -0
  147. package/src/lib/services/request-manager-services/request.service.ts +309 -0
  148. package/src/lib/services/request-manager-services/rxjs-operators/countdown.ts +17 -0
  149. package/src/lib/services/request-manager-services/rxjs-operators/delay-retry.ts +16 -0
  150. package/src/lib/services/request-manager-services/rxjs-operators/index.ts +4 -0
  151. package/src/lib/services/request-manager-services/rxjs-operators/request-polling.ts +35 -0
  152. package/src/lib/services/request-manager-services/rxjs-operators/request-streaming.ts +436 -0
  153. package/src/lib/services/request-manager-state-service/http-manager-state.store.ts +1321 -0
  154. package/src/lib/services/request-manager-state-service/index.ts +3 -0
  155. package/src/lib/services/request-manager-state-service/models/api-request.model.ts +61 -0
  156. package/src/lib/services/request-manager-state-service/models/index.ts +6 -0
  157. package/src/lib/services/request-manager-state-service/models/request-options.model.ts +22 -0
  158. package/src/lib/services/request-manager-state-service/models/stream-type.enum.ts +13 -0
  159. package/src/lib/services/request-manager-state-service/models/ws-options.model.ts +39 -0
  160. package/src/lib/services/store-state-manager-service/index.ts +3 -0
  161. package/src/lib/services/store-state-manager-service/models/index.ts +2 -0
  162. package/src/lib/services/store-state-manager-service/models/state-storage-options.model.ts +24 -0
  163. package/src/lib/services/store-state-manager-service/store-state-manager.service.ts +88 -0
  164. package/src/lib/services/utils/app.service.spec.ts +25 -0
  165. package/src/lib/services/utils/app.service.ts +21 -0
  166. package/src/lib/services/utils/encryption/README.md +79 -0
  167. package/src/lib/services/utils/encryption/asymmetrical-encryption.service.ts +282 -0
  168. package/src/lib/services/utils/encryption/encryption-test.service.ts +39 -0
  169. package/src/lib/services/utils/encryption/index.ts +5 -0
  170. package/src/lib/services/utils/encryption/random.ts +81 -0
  171. package/src/lib/services/utils/encryption/symmetrical-encryption.service.ts +93 -0
  172. package/src/lib/services/utils/headers.service.spec.ts +80 -0
  173. package/src/lib/services/utils/headers.service.ts +18 -0
  174. package/src/lib/services/utils/index.ts +7 -0
  175. package/src/lib/services/utils/object-merger.service.spec.ts +18 -0
  176. package/src/lib/services/utils/object-merger.service.ts +78 -0
  177. package/src/lib/services/utils/path-query.service.spec.ts +117 -0
  178. package/src/lib/services/utils/path-query.service.ts +69 -0
  179. package/src/lib/services/utils/random-color.utils.ts +83 -0
  180. package/src/lib/services/utils/utils.service.spec.ts +165 -0
  181. package/src/lib/services/utils/utils.service.ts +192 -0
  182. package/src/lib/services/ws-manager-service/index.ts +4 -0
  183. package/src/lib/services/ws-manager-service/models/channel-info.model.ts +24 -0
  184. package/src/lib/services/ws-manager-service/models/channel-message-data.model.ts +24 -0
  185. package/src/lib/services/ws-manager-service/models/channel-message.model.ts +24 -0
  186. package/src/lib/services/ws-manager-service/models/communication-type.enum.ts +5 -0
  187. package/src/lib/services/ws-manager-service/models/index.ts +5 -0
  188. package/src/lib/services/ws-manager-service/models/ws-user.model.ts +38 -0
  189. package/src/lib/services/ws-manager-service/services/index.ts +3 -0
  190. package/src/lib/services/ws-manager-service/services/websocket.service.ts +392 -0
  191. package/src/public-api.ts +14 -0
  192. package/tsconfig.lib.json +32 -0
  193. package/tsconfig.lib.prod.json +10 -0
  194. package/tsconfig.spec.json +14 -0
  195. package/fesm2022/http-request-manager.mjs +0 -7633
  196. package/fesm2022/http-request-manager.mjs.map +0 -1
  197. package/types/http-request-manager.d.ts +0 -2277
@@ -0,0 +1,1186 @@
1
+ # Utils Service
2
+
3
+ The `UtilsService` provides a collection of utility functions for common tasks including JSON handling, type checking, expiration management, conversions, and validation. These utilities simplify everyday development tasks.
4
+
5
+ ## Overview
6
+
7
+ This service provides:
8
+
9
+ - **JSON Handling** - Safe JSON parsing and stringification
10
+ - **Type Checking** - Runtime type validation and verification
11
+ - **Expiration Management** - Convert duration strings to epoch timestamps and check expiry
12
+ - **Conversions** - Base32, binary, and other data format conversions
13
+ - **Object Operations** - Deep comparison and property access
14
+ - **Time Utilities** - Date/time calculations and formatting
15
+
16
+ ## Installation
17
+
18
+ ```typescript
19
+ import { HttpRequestManagerModule } from 'http-request-manager';
20
+
21
+ @NgModule({
22
+ imports: [HttpRequestManagerModule.forRoot({})]
23
+ })
24
+ export class AppModule { }
25
+ ```
26
+
27
+ ## Basic Usage
28
+
29
+ ### Service Injection
30
+
31
+ ```typescript
32
+ import { Component, inject } from '@angular/core';
33
+ import { UtilsService } from 'http-request-manager';
34
+
35
+ @Component({
36
+ selector: 'app-utils-demo',
37
+ template: `
38
+ <div class="utils-demo">
39
+ <h2>Utils Service Demo</h2>
40
+
41
+ <div class="json-section">
42
+ <h3>JSON Operations</h3>
43
+ <button (click)="testJsonOperations()">Test JSON Operations</button>
44
+ <div *ngIf="jsonResult">
45
+ <p>Original: {{ jsonResult.original }}</p>
46
+ <p>Parsed: {{ jsonResult.parsed | json }}</p>
47
+ <p>Stringified: {{ jsonResult.stringified }}</p>
48
+ </div>
49
+ </div>
50
+
51
+ <div class="type-section">
52
+ <h3>Type Checking</h3>
53
+ <input [(ngModel)]="testValue" placeholder="Enter a value">
54
+ <button (click)="checkTypes()">Check Types</button>
55
+ <div *ngIf="typeResult">
56
+ <p>Is String: {{ typeResult.isString }}</p>
57
+ <p>Is Object: {{ typeResult.isObject }}</p>
58
+ <p>Is JSON: {{ typeResult.isJson }}</p>
59
+ </div>
60
+ </div>
61
+
62
+ <div class="expiry-section">
63
+ <h3>Expiration Management</h3>
64
+ <button (click)="testExpiration()">Test Expiration</button>
65
+ <div *ngIf="expiryResult">
66
+ <p>1 day from now: {{ expiryResult.oneDay | date:'medium' }}</p>
67
+ <p>Current time: {{ expiryResult.current | date:'medium' }}</p>
68
+ <p>Has 1 day expired: {{ expiryResult.hasExpired }}</p>
69
+ <p>Time remaining: {{ expiryResult.timeRemaining }}</p>
70
+ </div>
71
+ </div>
72
+
73
+ <div class="conversion-section">
74
+ <h3>Conversions</h3>
75
+ <button (click)="testConversions()">Test Conversions</button>
76
+ <div *ngIf="conversionResult">
77
+ <p>Base32 to Hex: {{ conversionResult.base32ToHex }}</p>
78
+ <p>Binary to Hex: {{ conversionResult.binaryToHex }}</p>
79
+ </div>
80
+ </div>
81
+ </div>
82
+ `
83
+ })
84
+ export class UtilsDemoComponent {
85
+ private utils = inject(UtilsService);
86
+
87
+ testValue = '';
88
+ jsonResult: any;
89
+ typeResult: any;
90
+ expiryResult: any;
91
+ conversionResult: any;
92
+
93
+ testJsonOperations() {
94
+ const testObj = { name: 'John', age: 30, active: true };
95
+
96
+ this.jsonResult = {
97
+ original: testObj,
98
+ parsed: this.utils.stringToJSON(JSON.stringify(testObj)),
99
+ stringified: this.utils.JSONToString(testObj)
100
+ };
101
+ }
102
+
103
+ checkTypes() {
104
+ this.typeResult = {
105
+ isString: this.utils.isString(this.testValue),
106
+ isObject: this.utils.isObject(this.testValue),
107
+ isJson: this.utils.isJSON(this.testValue)
108
+ };
109
+ }
110
+
111
+ testExpiration() {
112
+ const oneDayFromNow = this.utils.expires('1d');
113
+ const currentTime = Math.floor(Date.now() / 1000);
114
+
115
+ this.expiryResult = {
116
+ oneDay: oneDayFromNow ? new Date(oneDayFromNow * 1000) : null,
117
+ current: new Date(currentTime * 1000),
118
+ hasExpired: this.utils.hasExpired(oneDayFromNow || 0),
119
+ timeRemaining: this.utils.expiresIn(oneDayFromNow || 0)
120
+ };
121
+ }
122
+
123
+ testConversions() {
124
+ this.conversionResult = {
125
+ base32ToHex: this.utils.base32ToHex('JBSWY3DPEHPK3PXP'),
126
+ binaryToHex: this.utils.binaryToHex('11000011100011110000')
127
+ };
128
+ }
129
+ }
130
+ ```
131
+
132
+ ## API Reference
133
+
134
+ ### JSON Operations
135
+
136
+ #### JSONToString(value: any): string
137
+
138
+ Safely convert value to JSON string:
139
+
140
+ ```typescript
141
+ const obj = { name: 'John', age: 30 };
142
+ const jsonString = this.utils.JSONToString(obj);
143
+ // Returns: '{"name":"John","age":30}'
144
+
145
+ const number = 42;
146
+ const stringValue = this.utils.JSONToString(number);
147
+ // Returns: '42'
148
+ ```
149
+
150
+ #### stringToJSON(value: string): any
151
+
152
+ Safely parse JSON string:
153
+
154
+ ```typescript
155
+ const jsonString = '{"name":"John","age":30}';
156
+ const obj = this.utils.stringToJSON(jsonString);
157
+ // Returns: { name: 'John', age: 30 }
158
+
159
+ const invalidJson = '{invalid json}';
160
+ const result = this.utils.stringToJSON(invalidJson);
161
+ // Returns: '{invalid json}' (unchanged)
162
+ ```
163
+
164
+ ### Type Checking
165
+
166
+ #### isString(value: any): boolean
167
+
168
+ Check if value is a string:
169
+
170
+ ```typescript
171
+ this.utils.isString('hello'); // true
172
+ this.utils.isString(123); // false
173
+ this.utils.isString(null); // false
174
+ this.utils.isString(undefined); // false
175
+ ```
176
+
177
+ #### isObject(value: any): boolean
178
+
179
+ Check if value is an object:
180
+
181
+ ```typescript
182
+ this.utils.isObject({}); // true
183
+ this.utils.isObject({ name: 'John' }); // true
184
+ this.utils.isObject(null); // false
185
+ this.utils.isObject([]); // true (arrays are objects)
186
+ this.utils.isObject('string'); // false
187
+ ```
188
+
189
+ #### isJSON(value: any): boolean
190
+
191
+ Check if value is valid JSON:
192
+
193
+ ```typescript
194
+ this.utils.isJSON('{"name":"John"}'); // true
195
+ this.utils.isJSON('not json'); // false
196
+ this.utils.isJSON(123); // false
197
+ this.utils.isJSON(null); // false
198
+ ```
199
+
200
+ ### Property Access
201
+
202
+ #### getValueByProp(obj: any, prop: string): any
203
+
204
+ Get property value by string path:
205
+
206
+ ```typescript
207
+ const obj = { user: { name: 'John', settings: { theme: 'dark' } } };
208
+
209
+ this.utils.getValueByProp(obj, 'user.name'); // undefined (doesn't support nesting)
210
+ this.utils.getValueByProp(obj.user, 'name'); // 'John'
211
+ this.utils.getValueByProp(obj.user.settings, 'theme'); // 'dark'
212
+ this.utils.getValueByProp(obj, 'nonexistent'); // false
213
+ ```
214
+
215
+ ### Object Comparison
216
+
217
+ #### objectsEqual(x: any, y: any): boolean
218
+
219
+ Deep comparison of two objects:
220
+
221
+ ```typescript
222
+ const obj1 = { name: 'John', age: 30, active: true };
223
+ const obj2 = { name: 'John', age: 30, active: true };
224
+ const obj3 = { name: 'John', age: 31, active: true };
225
+
226
+ this.utils.objectsEqual(obj1, obj2); // true
227
+ this.utils.objectsEqual(obj1, obj3); // false
228
+
229
+ // Nested objects
230
+ const nested1 = { user: { name: 'John', settings: { theme: 'dark' } } };
231
+ const nested2 = { user: { name: 'John', settings: { theme: 'dark' } } };
232
+ this.utils.objectsEqual(nested1, nested2); // true
233
+ ```
234
+
235
+ ### HTTP Operations
236
+
237
+ #### getJSON(file: string): Observable<any>
238
+
239
+ Get JSON file from assets:
240
+
241
+ ```typescript
242
+ this.utils.getJSON('data/config.json').subscribe(config => {
243
+ console.log('Config loaded:', config);
244
+ });
245
+ ```
246
+
247
+ ### Time Properties
248
+
249
+ #### today: number
250
+
251
+ Get current time in milliseconds:
252
+
253
+ ```typescript
254
+ const currentTime = this.utils.today;
255
+ console.log('Current time (ms):', currentTime);
256
+ ```
257
+
258
+ ### Expiration Management
259
+
260
+ #### expires(str?: string): number | undefined
261
+
262
+ Convert duration string to epoch timestamp:
263
+
264
+ ```typescript
265
+ // Time units: y=years, m=months, w=weeks, d=days, hr=hours, mn=minutes, s=seconds
266
+
267
+ this.utils.expires('1d'); // Current time + 1 day
268
+ this.utils.expires('2h'); // Current time + 2 hours
269
+ this.utils.expires('30m'); // Current time + 30 minutes
270
+ this.utils.expires('1w'); // Current time + 1 week
271
+ this.utils.expires('1y'); // Current time + 1 year
272
+
273
+ this.utils.expires(); // undefined (no duration provided)
274
+ ```
275
+
276
+ #### hasExpired(expiryDate: number): boolean
277
+
278
+ Check if timestamp has expired:
279
+
280
+ ```typescript
281
+ const futureTime = this.utils.expires('1d');
282
+ const pastTime = Math.floor(Date.now() / 1000) - 3600; // 1 hour ago
283
+
284
+ this.utils.hasExpired(futureTime); // false
285
+ this.utils.hasExpired(pastTime); // true
286
+ ```
287
+
288
+ #### hasExpiry(setting: any): boolean
289
+
290
+ Check if setting has expiration:
291
+
292
+ ```typescript
293
+ const setting1 = { expires: 1234567890 };
294
+ const setting2 = { encrypted: true };
295
+
296
+ this.utils.hasExpiry(setting1); // true
297
+ this.utils.hasExpiry(setting2); // false
298
+ ```
299
+
300
+ #### expiresIn(expiryDate: number): string | undefined
301
+
302
+ Get human-readable time remaining:
303
+
304
+ ```typescript
305
+ const oneDayFromNow = this.utils.expires('1d');
306
+ const oneHourFromNow = this.utils.expires('1h');
307
+
308
+ console.log(this.utils.expiresIn(oneDayFromNow)); // "1d"
309
+ console.log(this.utils.expiresIn(oneHourFromNow)); // "1h"
310
+
311
+ // For expired dates
312
+ const pastTime = Math.floor(Date.now() / 1000) - 3600;
313
+ console.log(this.utils.expiresIn(pastTime)); // "Expired"
314
+ ```
315
+
316
+ ### Conversions
317
+
318
+ #### base32ToHex(base32: string): string
319
+
320
+ Convert Base32 string to hexadecimal:
321
+
322
+ ```typescript
323
+ this.utils.base32ToHex('JBSWY3DPEHPK3PXP'); // '48656C6C6F20576F726C64'
324
+ this.utils.base32ToHex('MY'); // '66'
325
+ this.utils.base32ToHex('MZXQ6==='); // '6C'
326
+ ```
327
+
328
+ #### binaryToHex(binary: string): string
329
+
330
+ Convert binary string to hexadecimal:
331
+
332
+ ```typescript
333
+ this.utils.binaryToHex('11000011100011110000'); // 'C78F0'
334
+ this.utils.binaryToHex('10101010'); // 'AA000000'
335
+ this.utils.binaryToHex('1111'); // 'F000'
336
+ ```
337
+
338
+ ### String Utilities
339
+
340
+ #### lc(str: string): string
341
+
342
+ Convert string to lowercase with special character replacement:
343
+
344
+ ```typescript
345
+ this.utils.lc('Hello World!'); // 'helloworld'
346
+ this.utils.lc('Test-Case_String'); // 'testcasestring'
347
+ this.utils.lc('UPPER CASE'); // 'uppercase'
348
+ ```
349
+
350
+ ## Advanced Examples
351
+
352
+ ### Data Validation Service
353
+
354
+ ```typescript
355
+ import { Injectable } from '@angular/core';
356
+ import { UtilsService } from 'http-request-manager';
357
+
358
+ interface ValidationResult {
359
+ isValid: boolean;
360
+ errors: string[];
361
+ sanitizedData?: any;
362
+ }
363
+
364
+ @Injectable({
365
+ providedIn: 'root'
366
+ })
367
+ export class DataValidationService {
368
+ constructor(private utils: UtilsService) {}
369
+
370
+ validateAndSanitize<T>(data: any, schema: any): ValidationResult {
371
+ const errors: string[] = [];
372
+ let sanitizedData: any = {};
373
+
374
+ try {
375
+ // Check if data is object
376
+ if (!this.utils.isObject(data)) {
377
+ errors.push('Data must be an object');
378
+ return { isValid: false, errors };
379
+ }
380
+
381
+ // Sanitize each field according to schema
382
+ for (const [field, rules] of Object.entries(schema)) {
383
+ const value = data[field];
384
+ const sanitizedValue = this.sanitizeField(field, value, rules as any);
385
+
386
+ if (sanitizedValue !== undefined) {
387
+ sanitizedData[field] = sanitizedValue;
388
+ }
389
+ }
390
+
391
+ return {
392
+ isValid: errors.length === 0,
393
+ errors,
394
+ sanitizedData
395
+ };
396
+ } catch (error) {
397
+ errors.push(`Validation error: ${error}`);
398
+ return { isValid: false, errors };
399
+ }
400
+ }
401
+
402
+ private sanitizeField(field: string, value: any, rules: any): any {
403
+ // Handle required fields
404
+ if (rules.required && (value === undefined || value === null)) {
405
+ throw new Error(`Field '${field}' is required`);
406
+ }
407
+
408
+ if (value === undefined || value === null) {
409
+ return rules.default;
410
+ }
411
+
412
+ // Type validation
413
+ switch (rules.type) {
414
+ case 'string':
415
+ if (typeof value !== 'string') {
416
+ throw new Error(`Field '${field}' must be a string`);
417
+ }
418
+ break;
419
+
420
+ case 'number':
421
+ if (typeof value !== 'number' && isNaN(Number(value))) {
422
+ throw new Error(`Field '${field}' must be a number`);
423
+ }
424
+ value = Number(value);
425
+ break;
426
+
427
+ case 'boolean':
428
+ if (typeof value !== 'boolean') {
429
+ throw new Error(`Field '${field}' must be a boolean`);
430
+ }
431
+ break;
432
+
433
+ case 'object':
434
+ if (!this.utils.isObject(value)) {
435
+ throw new Error(`Field '${field}' must be an object`);
436
+ }
437
+ break;
438
+
439
+ case 'json':
440
+ if (!this.utils.isJSON(value)) {
441
+ throw new Error(`Field '${field}' must be valid JSON`);
442
+ }
443
+ value = this.utils.stringToJSON(value);
444
+ break;
445
+ }
446
+
447
+ // String-specific rules
448
+ if (rules.type === 'string') {
449
+ // Min/max length
450
+ if (rules.minLength && value.length < rules.minLength) {
451
+ throw new Error(`Field '${field}' must be at least ${rules.minLength} characters`);
452
+ }
453
+ if (rules.maxLength && value.length > rules.maxLength) {
454
+ throw new Error(`Field '${field}' must be no more than ${rules.maxLength} characters`);
455
+ }
456
+
457
+ // Pattern matching
458
+ if (rules.pattern && !rules.pattern.test(value)) {
459
+ throw new Error(`Field '${field}' does not match required pattern`);
460
+ }
461
+
462
+ // Sanitization
463
+ if (rules.lowercase) {
464
+ value = value.toLowerCase();
465
+ }
466
+ if (rules.trim) {
467
+ value = value.trim();
468
+ }
469
+ }
470
+
471
+ // Number-specific rules
472
+ if (rules.type === 'number') {
473
+ if (rules.min !== undefined && value < rules.min) {
474
+ throw new Error(`Field '${field}' must be at least ${rules.min}`);
475
+ }
476
+ if (rules.max !== undefined && value > rules.max) {
477
+ throw new Error(`Field '${field}' must be no more than ${rules.max}`);
478
+ }
479
+ }
480
+
481
+ // Array-specific rules
482
+ if (rules.type === 'array') {
483
+ if (!Array.isArray(value)) {
484
+ throw new Error(`Field '${field}' must be an array`);
485
+ }
486
+ if (rules.minItems && value.length < rules.minItems) {
487
+ throw new Error(`Field '${field}' must have at least ${rules.minItems} items`);
488
+ }
489
+ if (rules.maxItems && value.length > rules.maxItems) {
490
+ throw new Error(`Field '${field}' must have no more than ${rules.maxItems} items`);
491
+ }
492
+ }
493
+
494
+ return value;
495
+ }
496
+
497
+ // Predefined schemas
498
+ getUserSchema() {
499
+ return {
500
+ name: {
501
+ type: 'string',
502
+ required: true,
503
+ minLength: 2,
504
+ maxLength: 50,
505
+ pattern: /^[a-zA-Z\s]+$/,
506
+ trim: true
507
+ },
508
+ email: {
509
+ type: 'string',
510
+ required: true,
511
+ pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
512
+ lowercase: true,
513
+ trim: true
514
+ },
515
+ age: {
516
+ type: 'number',
517
+ min: 0,
518
+ max: 150
519
+ },
520
+ preferences: {
521
+ type: 'object',
522
+ default: {}
523
+ }
524
+ };
525
+ }
526
+
527
+ getProductSchema() {
528
+ return {
529
+ name: {
530
+ type: 'string',
531
+ required: true,
532
+ minLength: 1,
533
+ maxLength: 100
534
+ },
535
+ price: {
536
+ type: 'number',
537
+ required: true,
538
+ min: 0
539
+ },
540
+ description: {
541
+ type: 'string',
542
+ maxLength: 500,
543
+ trim: true
544
+ },
545
+ tags: {
546
+ type: 'array',
547
+ maxItems: 10,
548
+ default: []
549
+ }
550
+ };
551
+ }
552
+ }
553
+ ```
554
+
555
+ ### Cache Management Service
556
+
557
+ ```typescript
558
+ @Injectable()
559
+ export class CacheManagerService {
560
+ constructor(private utils: UtilsService) {}
561
+
562
+ createCache<T>(key: string, ttl: string, maxSize?: number) {
563
+ return {
564
+ key,
565
+ ttl: this.utils.expires(ttl),
566
+ maxSize,
567
+ data: new Map<string, { value: T; timestamp: number }>(),
568
+
569
+ set(itemKey: string, value: T): void {
570
+ // Clean up expired items
571
+ this.cleanup();
572
+
573
+ // Check size limit
574
+ if (this.maxSize && this.data.size >= this.maxSize) {
575
+ // Remove oldest item
576
+ const oldestKey = this.data.keys().next().value;
577
+ this.data.delete(oldestKey);
578
+ }
579
+
580
+ this.data.set(itemKey, {
581
+ value,
582
+ timestamp: Date.now()
583
+ });
584
+ },
585
+
586
+ get(itemKey: string): T | null {
587
+ this.cleanup();
588
+
589
+ const item = this.data.get(itemKey);
590
+ if (item) {
591
+ return item.value;
592
+ }
593
+
594
+ return null;
595
+ },
596
+
597
+ has(itemKey: string): boolean {
598
+ this.cleanup();
599
+ return this.data.has(itemKey);
600
+ },
601
+
602
+ delete(itemKey: string): boolean {
603
+ return this.data.delete(itemKey);
604
+ },
605
+
606
+ clear(): void {
607
+ this.data.clear();
608
+ },
609
+
610
+ size(): number {
611
+ this.cleanup();
612
+ return this.data.size;
613
+ },
614
+
615
+ cleanup(): void {
616
+ const now = Date.now();
617
+ const expiredKeys: string[] = [];
618
+
619
+ this.data.forEach((item, key) => {
620
+ if (item.timestamp < this.ttl * 1000) {
621
+ expiredKeys.push(key);
622
+ }
623
+ });
624
+
625
+ expiredKeys.forEach(key => this.data.delete(key));
626
+ },
627
+
628
+ export(): Array<{ key: string; value: T; timestamp: number }> {
629
+ this.cleanup();
630
+ return Array.from(this.data.entries()).map(([key, item]) => ({
631
+ key,
632
+ value: item.value,
633
+ timestamp: item.timestamp
634
+ }));
635
+ },
636
+
637
+ import(items: Array<{ key: string; value: T; timestamp: number }>): void {
638
+ items.forEach(item => {
639
+ if (item.timestamp > this.ttl * 1000) {
640
+ this.data.set(item.key, {
641
+ value: item.value,
642
+ timestamp: item.timestamp
643
+ });
644
+ }
645
+ });
646
+ }
647
+ };
648
+ }
649
+
650
+ // Global cache instance
651
+ private globalCache = this.createCache('global-cache', '1h', 100);
652
+
653
+ cacheResult<T>(key: string, fn: () => T, ttl: string = '1h'): T {
654
+ const cached = this.globalCache.get(key);
655
+ if (cached !== null) {
656
+ return cached;
657
+ }
658
+
659
+ const result = fn();
660
+ this.globalCache.set(key, result);
661
+ return result;
662
+ }
663
+
664
+ invalidateCache(key: string): void {
665
+ this.globalCache.delete(key);
666
+ }
667
+
668
+ clearCache(): void {
669
+ this.globalCache.clear();
670
+ }
671
+ }
672
+ ```
673
+
674
+ ### Configuration Manager
675
+
676
+ ```typescript
677
+ @Injectable()
678
+ export class ConfigurationManagerService {
679
+ private configs = new Map<string, any>();
680
+
681
+ constructor(private utils: UtilsService) {}
682
+
683
+ loadConfig<T>(configKey: string, defaultValue: T, expiration?: string): T {
684
+ const cached = this.configs.get(configKey);
685
+
686
+ if (cached && !this.isExpired(cached.timestamp, expiration)) {
687
+ return cached.value;
688
+ }
689
+
690
+ // Load from storage or API
691
+ const value = this.loadConfigValue<T>(configKey, defaultValue);
692
+
693
+ this.configs.set(configKey, {
694
+ value,
695
+ timestamp: Date.now(),
696
+ expiresAt: expiration ? this.utils.expires(expiration) : undefined
697
+ });
698
+
699
+ return value;
700
+ }
701
+
702
+ updateConfig<T>(configKey: string, value: T, expiration?: string): void {
703
+ this.configs.set(configKey, {
704
+ value,
705
+ timestamp: Date.now(),
706
+ expiresAt: expiration ? this.utils.expires(expiration) : undefined
707
+ });
708
+
709
+ this.saveConfigValue(configKey, value);
710
+ }
711
+
712
+ getConfig<T>(configKey: string): T | null {
713
+ const cached = this.configs.get(configKey);
714
+ if (cached && !this.isExpired(cached.timestamp, undefined)) {
715
+ return cached.value;
716
+ }
717
+ return null;
718
+ }
719
+
720
+ invalidateConfig(configKey: string): void {
721
+ this.configs.delete(configKey);
722
+ }
723
+
724
+ clearConfigs(): void {
725
+ this.configs.clear();
726
+ }
727
+
728
+ private isExpired(timestamp: number, expiration?: string): boolean {
729
+ if (!expiration) return false;
730
+
731
+ const expiryTime = this.utils.expires(expiration);
732
+ return this.utils.hasExpired(expiryTime || 0);
733
+ }
734
+
735
+ private loadConfigValue<T>(configKey: string, defaultValue: T): T {
736
+ try {
737
+ const stored = localStorage.getItem(`config:${configKey}`);
738
+ if (stored) {
739
+ const parsed = JSON.parse(stored);
740
+ return parsed.value || defaultValue;
741
+ }
742
+ } catch (error) {
743
+ console.warn(`Failed to load config ${configKey}:`, error);
744
+ }
745
+
746
+ return defaultValue;
747
+ }
748
+
749
+ private saveConfigValue(configKey: string, value: any): void {
750
+ try {
751
+ localStorage.setItem(`config:${configKey}`, JSON.stringify({
752
+ value,
753
+ savedAt: new Date().toISOString()
754
+ }));
755
+ } catch (error) {
756
+ console.warn(`Failed to save config ${configKey}:`, error);
757
+ }
758
+ }
759
+
760
+ // Predefined configurations
761
+ getAppConfig() {
762
+ return this.loadConfig('app-config', {
763
+ version: '1.0.0',
764
+ apiUrl: 'http://localhost:3000',
765
+ features: {
766
+ enableAnalytics: true,
767
+ enableNotifications: true,
768
+ enableOffline: false
769
+ },
770
+ limits: {
771
+ maxFileSize: 10485760, // 10MB
772
+ maxRequestsPerMinute: 100
773
+ }
774
+ }, '1h');
775
+ }
776
+
777
+ getUserPreferences() {
778
+ return this.loadConfig('user-preferences', {
779
+ theme: 'light',
780
+ language: 'en',
781
+ notifications: {
782
+ email: true,
783
+ push: true,
784
+ sms: false
785
+ },
786
+ privacy: {
787
+ analytics: true,
788
+ marketing: false
789
+ }
790
+ }, '30d');
791
+ }
792
+
793
+ getFeatureFlags() {
794
+ return this.loadConfig('feature-flags', {
795
+ newDashboard: false,
796
+ darkMode: true,
797
+ betaFeatures: false,
798
+ experimentalUI: false
799
+ }, '1d');
800
+ }
801
+ }
802
+ ```
803
+
804
+ ### URL Parameter Parser
805
+
806
+ ```typescript
807
+ @Injectable()
808
+ export class URLParserService {
809
+ constructor(private utils: UtilsService) {}
810
+
811
+ parseQueryParams(url: string): Record<string, string> {
812
+ const params: Record<string, string> = {};
813
+ const queryString = url.split('?')[1];
814
+
815
+ if (!queryString) return params;
816
+
817
+ queryString.split('&').forEach(pair => {
818
+ const [key, value] = pair.split('=');
819
+ if (key && value) {
820
+ params[decodeURIComponent(key)] = decodeURIComponent(value);
821
+ }
822
+ });
823
+
824
+ return params;
825
+ }
826
+
827
+ buildQueryString(params: Record<string, any>): string {
828
+ const searchParams = new URLSearchParams();
829
+
830
+ Object.entries(params).forEach(([key, value]) => {
831
+ if (value !== null && value !== undefined) {
832
+ if (Array.isArray(value)) {
833
+ value.forEach(item => searchParams.append(key, String(item)));
834
+ } else {
835
+ searchParams.set(key, String(value));
836
+ }
837
+ }
838
+ });
839
+
840
+ return searchParams.toString();
841
+ }
842
+
843
+ extractUrlParams(template: string, actualUrl: string): Record<string, string> {
844
+ const templateParts = template.split('/');
845
+ const actualParts = actualUrl.split('/');
846
+ const params: Record<string, string> = {};
847
+
848
+ if (templateParts.length !== actualParts.length) {
849
+ return params;
850
+ }
851
+
852
+ templateParts.forEach((part, index) => {
853
+ if (part.startsWith(':')) {
854
+ const paramName = part.substring(1);
855
+ params[paramName] = actualParts[index];
856
+ }
857
+ });
858
+
859
+ return params;
860
+ }
861
+
862
+ buildUrl(template: string, params: Record<string, string>): string {
863
+ let url = template;
864
+
865
+ Object.entries(params).forEach(([key, value]) => {
866
+ url = url.replace(`:${key}`, encodeURIComponent(value));
867
+ });
868
+
869
+ return url;
870
+ }
871
+
872
+ // URL validation
873
+ isValidUrl(url: string): boolean {
874
+ try {
875
+ new URL(url);
876
+ return true;
877
+ } catch {
878
+ return false;
879
+ }
880
+ }
881
+
882
+ isValidEmail(email: string): boolean {
883
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
884
+ return emailRegex.test(email);
885
+ }
886
+
887
+ sanitizeUrl(url: string): string {
888
+ // Remove potentially dangerous protocols
889
+ const dangerousProtocols = ['javascript:', 'data:', 'vbscript:'];
890
+
891
+ dangerousProtocols.forEach(protocol => {
892
+ if (url.toLowerCase().startsWith(protocol)) {
893
+ throw new Error(`Dangerous protocol detected: ${protocol}`);
894
+ }
895
+ });
896
+
897
+ return url;
898
+ }
899
+ }
900
+ ```
901
+
902
+ ## Integration with Other Services
903
+
904
+ ### With HTTP State Service
905
+
906
+ ```typescript
907
+ @Injectable()
908
+ export class SmartCacheService extends HTTPManagerStateService<any> {
909
+ private utils = inject(UtilsService);
910
+ private cache = new Map<string, { data: any; expiresAt: number }>();
911
+
912
+ constructor() {
913
+ super(
914
+ ApiRequest.adapt({ path: ['data'] }),
915
+ DataType.OBJECT
916
+ );
917
+ }
918
+
919
+ fetchWithCache(key: string, fetchFn: () => any, ttl: string = '1h') {
920
+ // Check cache first
921
+ const cached = this.cache.get(key);
922
+ if (cached && !this.utils.hasExpired(cached.expiresAt)) {
923
+ return of(cached.data);
924
+ }
925
+
926
+ // Fetch and cache
927
+ return fetchFn().pipe(
928
+ tap(data => {
929
+ const expiresAt = this.utils.expires(ttl);
930
+ this.cache.set(key, {
931
+ data,
932
+ expiresAt: expiresAt || 0
933
+ });
934
+ })
935
+ );
936
+ }
937
+
938
+ invalidateCache(key: string): void {
939
+ this.cache.delete(key);
940
+ }
941
+
942
+ clearExpiredCache(): void {
943
+ const now = Date.now() / 1000;
944
+ const expiredKeys: string[] = [];
945
+
946
+ this.cache.forEach((item, key) => {
947
+ if (item.expiresAt < now) {
948
+ expiredKeys.push(key);
949
+ }
950
+ });
951
+
952
+ expiredKeys.forEach(key => this.cache.delete(key));
953
+ }
954
+ }
955
+ ```
956
+
957
+ ### With Local Storage
958
+
959
+ ```typescript
960
+ @Injectable()
961
+ export class SmartStorageService {
962
+ private utils = inject(UtilsService);
963
+ private storage = inject(LocalStorageManagerService);
964
+
965
+ setWithExpiration(key: string, value: any, expiration: string): void {
966
+ const expiresAt = this.utils.expires(expiration);
967
+
968
+ this.storage.setItem(key, {
969
+ value,
970
+ expiresAt,
971
+ createdAt: Date.now()
972
+ });
973
+ }
974
+
975
+ getWithExpiration<T>(key: string): T | null {
976
+ const stored = this.storage.getItem(key);
977
+
978
+ if (!stored) return null;
979
+
980
+ if (this.utils.hasExpired(stored.expiresAt)) {
981
+ this.storage.removeItem(key);
982
+ return null;
983
+ }
984
+
985
+ return stored.value;
986
+ }
987
+
988
+ setIfNewer(key: string, value: any, timestamp: number): boolean {
989
+ const stored = this.storage.getItem(key);
990
+
991
+ if (stored && stored.timestamp > timestamp) {
992
+ return false; // Stored version is newer
993
+ }
994
+
995
+ this.storage.setItem(key, {
996
+ value,
997
+ timestamp,
998
+ updatedAt: Date.now()
999
+ });
1000
+
1001
+ return true;
1002
+ }
1003
+ }
1004
+ ```
1005
+
1006
+ ## Best Practices
1007
+
1008
+ ### 1. Type Safety
1009
+
1010
+ ```typescript
1011
+ // ✅ Good - Validate types before operations
1012
+ if (this.utils.isString(value)) {
1013
+ const result = this.utils.lc(value);
1014
+ // Safe to use result as string
1015
+ }
1016
+
1017
+ // ❌ Avoid - No type checking
1018
+ const result = this.utils.lc(value); // Could fail if value is not string
1019
+ ```
1020
+
1021
+ ### 2. Error Handling
1022
+
1023
+ ```typescript
1024
+ // ✅ Good - Handle potential errors
1025
+ try {
1026
+ const parsed = this.utils.stringToJSON(jsonString);
1027
+ // Process parsed data
1028
+ } catch (error) {
1029
+ console.error('Failed to parse JSON:', error);
1030
+ return defaultValue;
1031
+ }
1032
+
1033
+ // ❌ Avoid - No error handling
1034
+ const parsed = this.utils.stringToJSON(jsonString); // Could throw
1035
+ ```
1036
+
1037
+ ### 3. Performance Considerations
1038
+
1039
+ ```typescript
1040
+ // ✅ Good - Cache expensive operations
1041
+ private cachedBase32Conversion = new Map<string, string>();
1042
+
1043
+ convertBase32(value: string): string {
1044
+ if (!this.cachedBase32Conversion.has(value)) {
1045
+ this.cachedBase32Conversion.set(value, this.utils.base32ToHex(value));
1046
+ }
1047
+ return this.cachedBase32Conversion.get(value)!;
1048
+ }
1049
+
1050
+ // ❌ Avoid - Repeated expensive operations
1051
+ // Converting the same value multiple times without caching
1052
+ ```
1053
+
1054
+ ### 4. Validation Before Usage
1055
+
1056
+ ```typescript
1057
+ // ✅ Good - Validate input before processing
1058
+ validateAndProcess(data: any): any {
1059
+ if (!this.utils.isObject(data)) {
1060
+ throw new Error('Data must be an object');
1061
+ }
1062
+
1063
+ if (!this.utils.isJSON(JSON.stringify(data))) {
1064
+ throw new Error('Data contains invalid JSON');
1065
+ }
1066
+
1067
+ return this.processData(data);
1068
+ }
1069
+ ```
1070
+
1071
+ ## Performance Optimization
1072
+
1073
+ ### Batch Operations
1074
+
1075
+ ```typescript
1076
+ @Injectable()
1077
+ export class BatchUtilsService {
1078
+ private utils = inject(UtilsService);
1079
+
1080
+ batchConvertExpirations(durations: string[]): number[] {
1081
+ return durations.map(duration => this.utils.expires(duration) || 0);
1082
+ }
1083
+
1084
+ batchValidateTypes(values: any[]): Array<{value: any; isString: boolean; isObject: boolean}> {
1085
+ return values.map(value => ({
1086
+ value,
1087
+ isString: this.utils.isString(value),
1088
+ isObject: this.utils.isObject(value)
1089
+ }));
1090
+ }
1091
+
1092
+ batchCompareObjects(pairs: Array<{x: any; y: any}>): boolean[] {
1093
+ return pairs.map(pair => this.utils.objectsEqual(pair.x, pair.y));
1094
+ }
1095
+ }
1096
+ ```
1097
+
1098
+ ### Memoization
1099
+
1100
+ ```typescript
1101
+ @Injectable()
1102
+ export class MemoizedUtilsService {
1103
+ private utils = inject(UtilsService);
1104
+
1105
+ // Memoization cache
1106
+ private stringToJsonCache = new Map<string, any>();
1107
+ private objectComparisonCache = new Map<string, boolean>();
1108
+
1109
+ memoizedStringToJSON(value: string): any {
1110
+ if (this.stringToJsonCache.has(value)) {
1111
+ return this.stringToJsonCache.get(value);
1112
+ }
1113
+
1114
+ const result = this.utils.stringToJSON(value);
1115
+ this.stringToJsonCache.set(value, result);
1116
+ return result;
1117
+ }
1118
+
1119
+ memoizedObjectsEqual(x: any, y: any): boolean {
1120
+ const key = JSON.stringify([x, y]);
1121
+
1122
+ if (this.objectComparisonCache.has(key)) {
1123
+ return this.objectComparisonCache.get(key);
1124
+ }
1125
+
1126
+ const result = this.utils.objectsEqual(x, y);
1127
+ this.objectComparisonCache.set(key, result);
1128
+ return result;
1129
+ }
1130
+
1131
+ clearCache(): void {
1132
+ this.stringToJsonCache.clear();
1133
+ this.objectComparisonCache.clear();
1134
+ }
1135
+ }
1136
+ ```
1137
+
1138
+ ## Troubleshooting
1139
+
1140
+ ### Common Issues
1141
+
1142
+ #### 1. JSON Parsing Errors
1143
+ ```typescript
1144
+ // ✅ Good - Safe JSON parsing
1145
+ safeJsonParse(jsonString: string, fallback: any = null): any {
1146
+ try {
1147
+ return this.utils.stringToJSON(jsonString);
1148
+ } catch (error) {
1149
+ console.warn('Invalid JSON:', jsonString, error);
1150
+ return fallback;
1151
+ }
1152
+ }
1153
+
1154
+ // ❌ Avoid - Unsafe parsing
1155
+ const data = JSON.parse(jsonString); // Could throw
1156
+ ```
1157
+
1158
+ #### 2. Type Checking Edge Cases
1159
+ ```typescript
1160
+ // ✅ Good - Handle edge cases
1161
+ isValidString(value: any): boolean {
1162
+ return this.utils.isString(value) && value.trim().length > 0;
1163
+ }
1164
+
1165
+ // ❌ Avoid - Incomplete validation
1166
+ const isString = this.utils.isString(value); // Could be empty string
1167
+ ```
1168
+
1169
+ #### 3. Expiration Calculation
1170
+ ```typescript
1171
+ // ✅ Good - Handle invalid durations
1172
+ safeExpires(duration: string, fallback: number): number {
1173
+ const expires = this.utils.expires(duration);
1174
+ return expires || fallback;
1175
+ }
1176
+
1177
+ // ❌ Avoid - No fallback
1178
+ const expires = this.utils.expires(duration); // Could be undefined
1179
+ ```
1180
+
1181
+ ## Related Documentation
1182
+
1183
+ - [Local Storage Service](local-storage/README.md)
1184
+ - [HTTP State Service](http-state/README.md)
1185
+ - [Database Manager Service](database/README.md)
1186
+ - [Architecture Overview](../architecture/README.md)