http-request-manager 18.15.32 → 18.15.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/TEST_COVERAGE_SUMMARY.md +458 -0
- package/ng-package.json +8 -0
- package/package.json +5 -13
- package/src/docs/ADVANCED_WEBSOCKET.md +633 -0
- package/src/docs/ARCHITECTURE.md +633 -0
- package/src/docs/BATCH_REQUEST_README.md +467 -0
- package/src/docs/COMPLETE_API_REFERENCE.md +1037 -0
- package/src/docs/DATABASE_README.md +1195 -0
- package/src/docs/ENCRYPTION_README.md +403 -0
- package/src/docs/HTTP_MANAGER_README.md +628 -0
- package/src/docs/HTTP_SINGNALS_MANAGER_README.md +654 -0
- package/src/docs/HTTP_STATE_MANAGER_README.md +1391 -0
- package/src/docs/INTERCEPTOR_README.md +549 -0
- package/src/docs/LOCAL_STORAGE_README.md +1056 -0
- package/src/docs/LOCAL_STORAGE_SIGNALS_README.md +338 -0
- package/src/docs/LOGGER_README.md +310 -0
- package/src/docs/MESSAGE_TRACKER_README.md +518 -0
- package/src/docs/MESSAGE_TRACKER_SIGNALS_README.md +563 -0
- package/src/docs/MODELS_README.md +1264 -0
- package/src/docs/SIGNAL_SERVICES_README.md +238 -0
- package/src/docs/SQL_DIXIE_README.md +574 -0
- package/src/docs/STORE_STATE_MANAGER_README.md +556 -0
- package/src/docs/STORE_STATE_SIGNALS_README.md +600 -0
- package/src/docs/UPLOAD_REQUEST_README.md +324 -0
- package/src/docs/UTILS_README.md +1604 -0
- package/src/docs/WEBSOCKET_MESSAGE_SERVICE.md +799 -0
- package/src/docs/WEBSOCKET_SIGNALS_README.md +641 -0
- package/src/docs/WEBSOCKET_SINGLETON_REFACTORING.md +201 -0
- package/src/docs/WS_MANAGER_README.md +613 -0
- package/src/lib/http-request-manager.module.ts +147 -0
- package/src/lib/http-request-services-demo/database-data-demo/database-data-demo.component.html +116 -0
- package/src/lib/http-request-services-demo/database-data-demo/database-data-demo.component.scss +0 -0
- package/src/lib/http-request-services-demo/database-data-demo/database-data-demo.component.ts +255 -0
- package/src/lib/http-request-services-demo/http-request-services-demo.component.html +123 -0
- package/src/lib/http-request-services-demo/http-request-services-demo.component.scss +6 -0
- package/src/lib/http-request-services-demo/http-request-services-demo.component.ts +53 -0
- package/src/lib/http-request-services-demo/local-storage-demo/local-storage-demo.component.html +195 -0
- package/src/lib/http-request-services-demo/local-storage-demo/local-storage-demo.component.scss +17 -0
- package/src/lib/http-request-services-demo/local-storage-demo/local-storage-demo.component.ts +208 -0
- package/src/lib/http-request-services-demo/local-storage-signals-demo/local-storage-signals-demo.component.html +200 -0
- package/src/lib/http-request-services-demo/local-storage-signals-demo/local-storage-signals-demo.component.scss +17 -0
- package/src/lib/http-request-services-demo/local-storage-signals-demo/local-storage-signals-demo.component.ts +214 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/file-downloader/download-file/download-file.component.html +53 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/file-downloader/download-file/download-file.component.scss +60 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/file-downloader/download-file/download-file.component.ts +72 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/file-downloader/file-download.module.ts +28 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/file-downloader/file-downloader.component.html +10 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/file-downloader/file-downloader.component.scss +29 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/file-downloader/file-downloader.component.ts +100 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/file-downloader/models/download-labels-model.ts +22 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/file-downloader/spinner/spinner.component.html +8 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/file-downloader/spinner/spinner.component.scss +19 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/file-downloader/spinner/spinner.component.ts +26 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/models/app-session.model.ts +30 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/models/app.model.ts +19 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/models/get-sample.model.ts +25 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/models/sample-ai-prompt.ts +19 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/models/sample-client-details.ts +24 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/models/sample-client-info.ts +30 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/models/sample-client.model.ts +49 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/models/sample-mapper-client-info.ts +33 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/request-manager-basic-demo.component.html +279 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/request-manager-basic-demo.component.scss +24 -0
- package/src/lib/http-request-services-demo/request-manager-basic-demo/request-manager-basic-demo.component.ts +461 -0
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/download-file/download-file.component.html +53 -0
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/download-file/download-file.component.scss +60 -0
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/download-file/download-file.component.ts +72 -0
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-download.module.ts +28 -0
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-downloader.component.html +10 -0
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-downloader.component.scss +29 -0
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-downloader.component.ts +100 -0
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/models/download-labels-model.ts +22 -0
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/spinner/spinner.component.html +8 -0
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/spinner/spinner.component.scss +19 -0
- package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/spinner/spinner.component.ts +26 -0
- package/src/lib/http-request-services-demo/request-manager-demo/models/app-session.model.ts +30 -0
- package/src/lib/http-request-services-demo/request-manager-demo/models/app.model.ts +19 -0
- package/src/lib/http-request-services-demo/request-manager-demo/models/get-sample.model.ts +25 -0
- package/src/lib/http-request-services-demo/request-manager-demo/models/sample-ai-prompt.ts +19 -0
- package/src/lib/http-request-services-demo/request-manager-demo/models/sample-client-details.ts +24 -0
- package/src/lib/http-request-services-demo/request-manager-demo/models/sample-client-info.ts +30 -0
- package/src/lib/http-request-services-demo/request-manager-demo/models/sample-client.model.ts +49 -0
- package/src/lib/http-request-services-demo/request-manager-demo/models/sample-mapper-client-info.ts +33 -0
- package/src/lib/http-request-services-demo/request-manager-demo/request-manager-demo.component.html +622 -0
- package/src/lib/http-request-services-demo/request-manager-demo/request-manager-demo.component.scss +106 -0
- package/src/lib/http-request-services-demo/request-manager-demo/request-manager-demo.component.ts +687 -0
- package/src/lib/http-request-services-demo/request-manager-state-demo/request-manager-state-demo.component.html +418 -0
- package/src/lib/http-request-services-demo/request-manager-state-demo/request-manager-state-demo.component.scss +24 -0
- package/src/lib/http-request-services-demo/request-manager-state-demo/request-manager-state-demo.component.ts +576 -0
- package/src/lib/http-request-services-demo/request-manager-state-demo/services/state-manager-demo.service.ts +89 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/services/state-data-request.service.ts +119 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-ai-messaging/ws-ai-messaging.component.css +0 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-ai-messaging/ws-ai-messaging.component.html +3 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-ai-messaging/ws-ai-messaging.component.ts +16 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-chats/ws-chats.component.css +0 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-chats/ws-chats.component.html +3 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-chats/ws-chats.component.ts +16 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.css +31 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.html +94 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.scss +41 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.spec.ts +203 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.ts +144 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-messaging/ws-messaging.component.css +11 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-messaging/ws-messaging.component.html +102 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-messaging/ws-messaging.component.spec.ts +40 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-messaging/ws-messaging.component.ts +230 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-notifications/ws-notifications.component.css +30 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-notifications/ws-notifications.component.html +172 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-notifications/ws-notifications.component.spec.ts +31 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-notifications/ws-notifications.component.ts +239 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/models/oidc-client.model.ts +31 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/models/user-data.model.ts +32 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/request-manager-ws-demo.component.css +0 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/request-manager-ws-demo.component.html +84 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/request-manager-ws-demo.component.ts +40 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/services/index.ts +3 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/services/jwt-token.service.ts +62 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/services/message-service-demo.service.ts +83 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/services/notification-service-demo.service.ts +147 -0
- package/src/lib/http-request-services-demo/request-manager-ws-demo/services/state-service-demo.service.ts +168 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/download-file/download-file.component.html +53 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/download-file/download-file.component.scss +60 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/download-file/download-file.component.ts +72 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/file-download.module.ts +28 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/file-downloader.component.html +10 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/file-downloader.component.scss +29 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/file-downloader.component.ts +100 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/models/download-labels-model.ts +22 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/spinner/spinner.component.html +8 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/spinner/spinner.component.scss +19 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/spinner/spinner.component.ts +26 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/app-session.model.ts +30 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/app.model.ts +19 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/get-sample.model.ts +25 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-ai-prompt.ts +19 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-client-details.ts +24 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-client-info.ts +30 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-client.model.ts +49 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-mapper-client-info.ts +33 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/request-signals-manager-demo.component.html +380 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/request-signals-manager-demo.component.scss +24 -0
- package/src/lib/http-request-services-demo/request-signals-manager-demo/request-signals-manager-demo.component.ts +410 -0
- package/src/lib/http-request-services-demo/store-state-manager-demo/models/settings.model.ts +28 -0
- package/src/lib/http-request-services-demo/store-state-manager-demo/services/settings-state.service.ts +49 -0
- package/src/lib/http-request-services-demo/store-state-manager-demo/store-state-manager-demo.component.css +0 -0
- package/src/lib/http-request-services-demo/store-state-manager-demo/store-state-manager-demo.component.html +23 -0
- package/src/lib/http-request-services-demo/store-state-manager-demo/store-state-manager-demo.component.ts +36 -0
- package/src/lib/http-request-services-demo/store-state-signals-demo/store-state-signals-demo.component.ts +161 -0
- package/src/lib/http-request-services-demo/upload-demo/models/index.ts +1 -0
- package/src/lib/http-request-services-demo/upload-demo/models/upload-state.model.ts +30 -0
- package/src/lib/http-request-services-demo/upload-demo/upload-demo.component.html +89 -0
- package/src/lib/http-request-services-demo/upload-demo/upload-demo.component.scss +160 -0
- package/src/lib/http-request-services-demo/upload-demo/upload-demo.component.spec.ts +101 -0
- package/src/lib/http-request-services-demo/upload-demo/upload-demo.component.ts +136 -0
- package/src/lib/index.ts +3 -0
- package/src/lib/interceptors/credentials.interceptor.ts +16 -0
- package/src/lib/interceptors/index.ts +6 -0
- package/src/lib/interceptors/models/error-settings.model.ts +22 -0
- package/src/lib/interceptors/models/index.ts +2 -0
- package/src/lib/interceptors/proxy-debugger.interceptor.ts +46 -0
- package/src/lib/interceptors/request-error.interceptor.ts +65 -0
- package/src/lib/interceptors/request-header.interceptor.ts +56 -0
- package/src/lib/message-display/README.md +509 -0
- package/src/lib/message-display/index.ts +4 -0
- package/src/lib/message-display/models/action.model.ts +27 -0
- package/src/lib/message-display/models/communication-message.model.ts +77 -0
- package/src/lib/message-display/models/display-config.model.ts +35 -0
- package/src/lib/message-display/models/display-rule.interface.ts +28 -0
- package/src/lib/message-display/models/display-strategy.interface.ts +8 -0
- package/src/lib/message-display/models/index.ts +6 -0
- package/src/lib/message-display/models/slide.model.ts +24 -0
- package/src/lib/message-display/rules/default-display-rules.ts +35 -0
- package/src/lib/message-display/services/message-display-router.service.ts +63 -0
- package/src/lib/message-display/strategies/snackbar.strategy.ts +46 -0
- package/src/lib/models/batch-options.model.ts +33 -0
- package/src/lib/models/batch-progress.model.ts +19 -0
- package/src/lib/models/batch-request-state.model.ts +40 -0
- package/src/lib/models/batch-result.model.ts +30 -0
- package/src/lib/models/config-http-options.model.ts +45 -0
- package/src/lib/models/config-local-storage-options.model.ts +27 -0
- package/src/lib/models/config-options.model.ts +27 -0
- package/src/lib/models/config-token.model.ts +9 -0
- package/src/lib/models/data-type.enum.ts +5 -0
- package/src/lib/models/database-storage.model.ts +24 -0
- package/src/lib/models/index.ts +16 -0
- package/src/lib/models/retry-options.model.ts +22 -0
- package/src/lib/models/upload-validation-error.model.ts +46 -0
- package/src/lib/services/SQL-DixieJS service/dexie-query-executor.ts +246 -0
- package/src/lib/services/SQL-DixieJS service/dexie-sql.service.ts +31 -0
- package/src/lib/services/SQL-DixieJS service/index.ts +4 -0
- package/src/lib/services/SQL-DixieJS service/models/execution-plan.model.ts +52 -0
- package/src/lib/services/SQL-DixieJS service/models/index.ts +3 -0
- package/src/lib/services/SQL-DixieJS service/models/sql-errors.model.ts +13 -0
- package/src/lib/services/SQL-DixieJS service/models/sql-options.model.ts +3 -0
- package/src/lib/services/SQL-DixieJS service/query-planner.ts +284 -0
- package/src/lib/services/SQL-DixieJS service/schema-validator.ts +217 -0
- package/src/lib/services/SQL-DixieJS service/sql-parser.ts +35 -0
- package/src/lib/services/database-manager-service/database.manager.service.ts +384 -0
- package/src/lib/services/database-manager-service/db.storage.service.ts +240 -0
- package/src/lib/services/database-manager-service/index.ts +4 -0
- package/src/lib/services/database-manager-service/models/index.ts +2 -0
- package/src/lib/services/database-manager-service/models/table-schema.ts +33 -0
- package/src/lib/services/index.ts +20 -0
- package/src/lib/services/local-storage-manager-service/index.ts +4 -0
- package/src/lib/services/local-storage-manager-service/local-storage-manager.service.spec.ts +71 -0
- package/src/lib/services/local-storage-manager-service/local-storage-manager.service.ts +567 -0
- package/src/lib/services/local-storage-manager-service/local-storage-signals-manager.service.spec.ts +67 -0
- package/src/lib/services/local-storage-manager-service/local-storage-signals-manager.service.ts +437 -0
- package/src/lib/services/local-storage-manager-service/models/global-store-options.model.ts +30 -0
- package/src/lib/services/local-storage-manager-service/models/index.ts +6 -0
- package/src/lib/services/local-storage-manager-service/models/setting-options.model.ts +35 -0
- package/src/lib/services/local-storage-manager-service/models/storage-data.model.ts +24 -0
- package/src/lib/services/local-storage-manager-service/models/storage-option.model.ts +32 -0
- package/src/lib/services/local-storage-manager-service/models/storage-type.enum.ts +5 -0
- package/src/lib/services/request-manager-services/README.md +282 -0
- package/src/lib/services/request-manager-services/http-manager-signals.service.ts +674 -0
- package/src/lib/services/request-manager-services/http-manager.service.spec.ts +353 -0
- package/src/lib/services/request-manager-services/http-manager.service.ts +727 -0
- package/src/lib/services/request-manager-services/index.ts +8 -0
- package/src/lib/services/request-manager-services/request-signals.service.ts +372 -0
- package/src/lib/services/request-manager-services/request.service.ts +435 -0
- package/src/lib/services/request-manager-services/rxjs-operators/countdown.ts +17 -0
- package/src/lib/services/request-manager-services/rxjs-operators/delay-retry.ts +16 -0
- package/src/lib/services/request-manager-services/rxjs-operators/index.ts +4 -0
- package/src/lib/services/request-manager-services/rxjs-operators/request-polling.ts +35 -0
- package/src/lib/services/request-manager-services/rxjs-operators/request-streaming.ts +468 -0
- package/src/lib/services/request-manager-state-service/http-manager-state.store.spec.ts +665 -0
- package/src/lib/services/request-manager-state-service/http-manager-state.store.ts +2395 -0
- package/src/lib/services/request-manager-state-service/index.ts +3 -0
- package/src/lib/services/request-manager-state-service/models/api-request.model.ts +86 -0
- package/src/lib/services/request-manager-state-service/models/index.ts +14 -0
- package/src/lib/services/request-manager-state-service/models/operation-result.model.ts +18 -0
- package/src/lib/services/request-manager-state-service/models/parsing-result.model.ts +21 -0
- package/src/lib/services/request-manager-state-service/models/request-options.model.ts +37 -0
- package/src/lib/services/request-manager-state-service/models/stream-config.model.ts +20 -0
- package/src/lib/services/request-manager-state-service/models/stream-event-metadata.model.ts +23 -0
- package/src/lib/services/request-manager-state-service/models/stream-event.model.ts +23 -0
- package/src/lib/services/request-manager-state-service/models/stream-output.model.ts +23 -0
- package/src/lib/services/request-manager-state-service/models/stream-progress.model.ts +24 -0
- package/src/lib/services/request-manager-state-service/models/stream-type.enum.ts +13 -0
- package/src/lib/services/request-manager-state-service/models/ws-options.model.ts +42 -0
- package/src/lib/services/store-state-manager-service/index.ts +4 -0
- package/src/lib/services/store-state-manager-service/models/index.ts +3 -0
- package/src/lib/services/store-state-manager-service/models/state-operation-result.model.ts +30 -0
- package/src/lib/services/store-state-manager-service/models/state-storage-options.model.ts +24 -0
- package/src/lib/services/store-state-manager-service/store-state-manager-signals.service.ts +169 -0
- package/src/lib/services/store-state-manager-service/store-state-manager.service.ts +153 -0
- package/src/lib/services/utils/app.service.spec.ts +25 -0
- package/src/lib/services/utils/app.service.ts +21 -0
- package/src/lib/services/utils/encryption/README.md +79 -0
- package/src/lib/services/utils/encryption/asymmetrical-encryption.service.ts +282 -0
- package/src/lib/services/utils/encryption/encryption-test.service.ts +39 -0
- package/src/lib/services/utils/encryption/index.ts +5 -0
- package/src/lib/services/utils/encryption/random.ts +81 -0
- package/src/lib/services/utils/encryption/symmetrical-encryption.service.ts +106 -0
- package/src/lib/services/utils/headers.service.spec.ts +80 -0
- package/src/lib/services/utils/headers.service.ts +18 -0
- package/src/lib/services/utils/index.ts +9 -0
- package/src/lib/services/utils/logger.service.ts +90 -0
- package/src/lib/services/utils/models/index.ts +4 -0
- package/src/lib/services/utils/models/normalized-request-options.model.ts +24 -0
- package/src/lib/services/utils/models/path-tracker-state.model.ts +20 -0
- package/src/lib/services/utils/models/query-params-tracker-options.model.ts +24 -0
- package/src/lib/services/utils/models/query-tracker-state.model.ts +23 -0
- package/src/lib/services/utils/object-merger.service.spec.ts +18 -0
- package/src/lib/services/utils/object-merger.service.ts +78 -0
- package/src/lib/services/utils/path-query.service.spec.ts +117 -0
- package/src/lib/services/utils/path-query.service.ts +69 -0
- package/src/lib/services/utils/query-params-tracker.service.ts +442 -0
- package/src/lib/services/utils/random-color.utils.ts +83 -0
- package/src/lib/services/utils/utils.service.spec.ts +165 -0
- package/src/lib/services/utils/utils.service.ts +192 -0
- package/src/lib/services/ws-manager-service/index.ts +13 -0
- package/src/lib/services/ws-manager-service/message-tracker-signals.service.ts +147 -0
- package/src/lib/services/ws-manager-service/message-tracker.service.ts +477 -0
- package/src/lib/services/ws-manager-service/models/channel-info.model.ts +29 -0
- package/src/lib/services/ws-manager-service/models/channel-message-data.model.ts +24 -0
- package/src/lib/services/ws-manager-service/models/channel-message.model.ts +36 -0
- package/src/lib/services/ws-manager-service/models/channel-type.enum.ts +6 -0
- package/src/lib/services/ws-manager-service/models/communication-type.enum.ts +5 -0
- package/src/lib/services/ws-manager-service/models/index.ts +10 -0
- package/src/lib/services/ws-manager-service/models/notification-message.model.ts +29 -0
- package/src/lib/services/ws-manager-service/models/public-message.model.ts +18 -0
- package/src/lib/services/ws-manager-service/models/state-message.model.ts +18 -0
- package/src/lib/services/ws-manager-service/models/ws-user.model.ts +38 -0
- package/src/lib/services/ws-manager-service/services/index.ts +4 -0
- package/src/lib/services/ws-manager-service/services/websocket-message.service.ts +129 -0
- package/src/lib/services/ws-manager-service/services/websocket.service.ts +434 -0
- package/src/lib/services/ws-manager-service/websocket-service/index.ts +1 -0
- package/src/lib/services/ws-manager-service/websocket-service/websocket-manager.service.ts +716 -0
- package/src/lib/services/ws-manager-service/websocket-services-complete.spec.ts +596 -0
- package/src/lib/services/ws-manager-service/websocket-signals-manager.service.ts +141 -0
- package/src/public-api.ts +19 -0
- package/tsconfig.lib.json +34 -0
- package/tsconfig.lib.prod.json +10 -0
- package/tsconfig.spec.json +14 -0
- package/fesm2022/http-request-manager.mjs +0 -13297
- package/fesm2022/http-request-manager.mjs.map +0 -1
- package/http-request-manager-18.15.32.tgz +0 -0
- package/types/http-request-manager.d.ts +0 -3875
|
@@ -0,0 +1,1604 @@
|
|
|
1
|
+
# Utils Service
|
|
2
|
+
|
|
3
|
+
Comprehensive utility services for common tasks including encryption, headers, path/query building, object merging, and general utilities.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
1. [UtilsService - General Utilities](#utilsservice---general-utilities)
|
|
8
|
+
2. [Encryption Services](#encryption-services)
|
|
9
|
+
3. [HeadersService](#headersservice)
|
|
10
|
+
4. [PathQueryService](#pathqueryservice)
|
|
11
|
+
5. [ObjectMergerService](#objectmergerservice)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## UtilsService - General Utilities
|
|
16
|
+
|
|
17
|
+
The `UtilsService` provides a collection of utility functions for common tasks.
|
|
18
|
+
|
|
19
|
+
### Overview
|
|
20
|
+
|
|
21
|
+
**Features:**
|
|
22
|
+
- ✅ JSON Handling - Safe JSON parsing and stringification
|
|
23
|
+
- ✅ Type Checking - Runtime type validation
|
|
24
|
+
- ✅ Expiration Management - Duration strings to epoch timestamps
|
|
25
|
+
- ✅ Conversions - Base32, binary, and data format conversions
|
|
26
|
+
- ✅ Object Operations - Deep comparison and property access
|
|
27
|
+
- ✅ Time Utilities - Date/time calculations
|
|
28
|
+
|
|
29
|
+
### JSON Operations
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { UtilsService } from 'http-request-manager';
|
|
33
|
+
|
|
34
|
+
const utils = inject(UtilsService);
|
|
35
|
+
|
|
36
|
+
// Safe JSON parsing
|
|
37
|
+
const parsed = utils.stringToJSON('{"name": "John", "age": 30}');
|
|
38
|
+
// Returns: { name: 'John', age: 30 }
|
|
39
|
+
|
|
40
|
+
// Safe JSON stringification
|
|
41
|
+
const stringified = utils.JSONToString({ name: 'John', age: 30 });
|
|
42
|
+
// Returns: '{"name":"John","age":30}'
|
|
43
|
+
|
|
44
|
+
// Handle invalid JSON gracefully
|
|
45
|
+
const invalid = utils.stringToJSON('invalid json');
|
|
46
|
+
// Returns: null (no exception thrown)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Type Checking
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// Check if value is string
|
|
53
|
+
utils.isString('hello'); // true
|
|
54
|
+
utils.isString(123); // false
|
|
55
|
+
|
|
56
|
+
// Check if value is object
|
|
57
|
+
utils.isObject({}); // true
|
|
58
|
+
utils.isObject([]); // false
|
|
59
|
+
utils.isObject(null); // false
|
|
60
|
+
|
|
61
|
+
// Check if value is JSON
|
|
62
|
+
utils.isJson('{"valid": true}'); // true
|
|
63
|
+
utils.isJson('invalid'); // false
|
|
64
|
+
|
|
65
|
+
// Check if object is empty
|
|
66
|
+
utils.isEmpty({}); // true
|
|
67
|
+
utils.isEmpty({ name: 'John' }); // false
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Expiration Management
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// Parse duration strings to milliseconds
|
|
74
|
+
utils.parseExpiration('1h'); // 3600000
|
|
75
|
+
utils.parseExpiration('7d'); // 604800000
|
|
76
|
+
utils.parseExpiration('30m'); // 1800000
|
|
77
|
+
utils.parseExpiration('1y'); // 31536000000
|
|
78
|
+
|
|
79
|
+
// Convert to epoch timestamp
|
|
80
|
+
const expires = utils.calculateExpiresIn('7d');
|
|
81
|
+
// Returns: epoch timestamp 7 days from now
|
|
82
|
+
|
|
83
|
+
// Check if expired
|
|
84
|
+
const isExpired = utils.isExpired(expires);
|
|
85
|
+
// Returns: true/false
|
|
86
|
+
|
|
87
|
+
// Get time remaining
|
|
88
|
+
const remaining = utils.getTimeRemaining(expires);
|
|
89
|
+
// Returns: milliseconds until expiration
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Duration Formats:**
|
|
93
|
+
- `'30s'` - 30 seconds
|
|
94
|
+
- `'5m'` - 5 minutes
|
|
95
|
+
- `'1h'` - 1 hour
|
|
96
|
+
- `'1d'` - 1 day
|
|
97
|
+
- `'7d'` - 7 days
|
|
98
|
+
- `'30d'` - 30 days
|
|
99
|
+
- `'1y'` - 1 year
|
|
100
|
+
|
|
101
|
+
### Conversions
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// Base32 to Hex
|
|
105
|
+
const hex = utils.base32ToHex('JBSWY3DPEHPK3PXP');
|
|
106
|
+
|
|
107
|
+
// Binary to Hex
|
|
108
|
+
const hex = utils.binaryToHex('01001000');
|
|
109
|
+
|
|
110
|
+
// String to Base64
|
|
111
|
+
const base64 = utils.stringToBase64('Hello World');
|
|
112
|
+
|
|
113
|
+
// Base64 to String
|
|
114
|
+
const str = utils.base64ToString('SGVsbG8gV29ybGQ=');
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Object Operations
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
// Deep compare objects
|
|
121
|
+
const areEqual = utils.isObjectEqual(obj1, obj2);
|
|
122
|
+
|
|
123
|
+
// Get nested property safely
|
|
124
|
+
const value = utils.getProperty(obj, 'user.address.city');
|
|
125
|
+
// Returns: undefined instead of throwing error
|
|
126
|
+
|
|
127
|
+
// Set nested property
|
|
128
|
+
utils.setProperty(obj, 'user.address.city', 'New York');
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Time Utilities
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
// Get current epoch time
|
|
135
|
+
const now = utils.getCurrentEpoch();
|
|
136
|
+
|
|
137
|
+
// Format date
|
|
138
|
+
const formatted = utils.formatDate(new Date(), 'YYYY-MM-DD');
|
|
139
|
+
|
|
140
|
+
// Calculate date difference
|
|
141
|
+
const diff = utils.getDateDifference(date1, date2, 'days');
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Encryption Services
|
|
147
|
+
|
|
148
|
+
### SymmetricalEncryptionService
|
|
149
|
+
|
|
150
|
+
AES symmetric encryption for fast data encryption.
|
|
151
|
+
|
|
152
|
+
**Features:**
|
|
153
|
+
- 🔐 AES-256 encryption
|
|
154
|
+
- 🔑 Automatic key generation
|
|
155
|
+
- 📦 Ideal for data at rest
|
|
156
|
+
- ⚡ Fast encryption/decryption
|
|
157
|
+
|
|
158
|
+
**Usage:**
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import { SymmetricalEncryptionService } from 'http-request-manager';
|
|
162
|
+
|
|
163
|
+
const encryption = inject(SymmetricalEncryptionService);
|
|
164
|
+
|
|
165
|
+
// Encrypt data
|
|
166
|
+
const encrypted = encryption.encrypt('Sensitive data');
|
|
167
|
+
console.log('Encrypted:', encrypted);
|
|
168
|
+
|
|
169
|
+
// Decrypt data
|
|
170
|
+
const decrypted = encryption.decrypt(encrypted);
|
|
171
|
+
console.log('Decrypted:', decrypted);
|
|
172
|
+
|
|
173
|
+
// Generate encryption key
|
|
174
|
+
const key = encryption.generateKey();
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Methods:**
|
|
178
|
+
```typescript
|
|
179
|
+
encrypt(data: string): string
|
|
180
|
+
decrypt(encryptedData: string): string
|
|
181
|
+
generateKey(): string
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### AsymmetricalEncryptionService
|
|
185
|
+
|
|
186
|
+
RSA asymmetric encryption with public/private key pairs.
|
|
187
|
+
|
|
188
|
+
**Features:**
|
|
189
|
+
- 🔑 Public/private key encryption
|
|
190
|
+
- ✍️ Digital signatures
|
|
191
|
+
- 🔒 Enhanced security
|
|
192
|
+
- 📜 Key pair generation
|
|
193
|
+
|
|
194
|
+
**Usage:**
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
import { AsymmetricalEncryptionService } from 'http-request-manager';
|
|
198
|
+
|
|
199
|
+
const encryption = inject(AsymmetricalEncryptionService);
|
|
200
|
+
|
|
201
|
+
// Generate key pair
|
|
202
|
+
const keyPair = encryption.generateKeyPair();
|
|
203
|
+
console.log('Public Key:', keyPair.publicKey);
|
|
204
|
+
console.log('Private Key:', keyPair.privateKey);
|
|
205
|
+
|
|
206
|
+
// Encrypt with public key
|
|
207
|
+
const encrypted = encryption.encryptWithPublicKey(
|
|
208
|
+
'Secret message',
|
|
209
|
+
keyPair.publicKey
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
// Decrypt with private key
|
|
213
|
+
const decrypted = encryption.decryptWithPrivateKey(
|
|
214
|
+
encrypted,
|
|
215
|
+
keyPair.privateKey
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
// Sign data
|
|
219
|
+
const signature = encryption.sign('Data to sign', keyPair.privateKey);
|
|
220
|
+
|
|
221
|
+
// Verify signature
|
|
222
|
+
const isValid = encryption.verify(
|
|
223
|
+
signature,
|
|
224
|
+
'Data to sign',
|
|
225
|
+
keyPair.publicKey
|
|
226
|
+
);
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**Methods:**
|
|
230
|
+
```typescript
|
|
231
|
+
generateKeyPair(): { publicKey: string, privateKey: string }
|
|
232
|
+
encryptWithPublicKey(data: string, publicKey: string): string
|
|
233
|
+
decryptWithPrivateKey(encryptedData: string, privateKey: string): string
|
|
234
|
+
sign(data: string, privateKey: string): string
|
|
235
|
+
verify(signature: string, data: string, publicKey: string): boolean
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Random Utility
|
|
239
|
+
|
|
240
|
+
Cryptographically secure random value generation.
|
|
241
|
+
|
|
242
|
+
**Usage:**
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
import { Random } from 'http-request-manager';
|
|
246
|
+
|
|
247
|
+
// Generate random string
|
|
248
|
+
const randomString = Random.generate(16);
|
|
249
|
+
// Example: 'a3f8b2c1d4e5f6g7'
|
|
250
|
+
|
|
251
|
+
// Generate random number
|
|
252
|
+
const randomNumber = Random.number(1, 100);
|
|
253
|
+
// Example: 42
|
|
254
|
+
|
|
255
|
+
// Generate random color
|
|
256
|
+
const randomColor = Random.color();
|
|
257
|
+
// Example: '#a3f129'
|
|
258
|
+
|
|
259
|
+
// Generate UUID
|
|
260
|
+
const uuid = Random.uuid();
|
|
261
|
+
// Example: '550e8400-e29b-41d4-a716-446655440000'
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Methods:**
|
|
265
|
+
```typescript
|
|
266
|
+
generate(length: number): string
|
|
267
|
+
number(min: number, max: number): number
|
|
268
|
+
color(): string
|
|
269
|
+
uuid(): string
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## HeadersService
|
|
275
|
+
|
|
276
|
+
Dynamic HTTP header management.
|
|
277
|
+
|
|
278
|
+
**Features:**
|
|
279
|
+
- 📝 Content-Type management
|
|
280
|
+
- 🌐 Accept-Language with locale
|
|
281
|
+
- 📅 Current-Date header
|
|
282
|
+
- 🔄 Dynamic updates
|
|
283
|
+
|
|
284
|
+
**Usage:**
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
import { HeadersService } from 'http-request-manager';
|
|
288
|
+
|
|
289
|
+
const headersService = inject(HeadersService);
|
|
290
|
+
|
|
291
|
+
// Get default headers
|
|
292
|
+
const headers = headersService.getDefaultHeaders();
|
|
293
|
+
// {
|
|
294
|
+
// 'Content-Type': 'application/json',
|
|
295
|
+
// 'Accept-Language': 'en-CA',
|
|
296
|
+
// 'Current-Date': '2026-03-28'
|
|
297
|
+
// }
|
|
298
|
+
|
|
299
|
+
// Get specific header
|
|
300
|
+
const contentType = headersService.getHeader('Content-Type');
|
|
301
|
+
|
|
302
|
+
// Set custom header
|
|
303
|
+
headersService.setHeader('Authorization', 'Bearer token');
|
|
304
|
+
|
|
305
|
+
// Remove header
|
|
306
|
+
headersService.removeHeader('Current-Date');
|
|
307
|
+
|
|
308
|
+
// Get all headers
|
|
309
|
+
const allHeaders = headersService.getAllHeaders();
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Methods:**
|
|
313
|
+
```typescript
|
|
314
|
+
getDefaultHeaders(): { [key: string]: string }
|
|
315
|
+
getHeader(name: string): string | undefined
|
|
316
|
+
setHeader(name: string, value: string): void
|
|
317
|
+
removeHeader(name: string): void
|
|
318
|
+
getAllHeaders(): { [key: string]: string }
|
|
319
|
+
getLanguageHeader(): string
|
|
320
|
+
getDateHeader(): string
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
**Automatic Language Detection:**
|
|
324
|
+
|
|
325
|
+
The service automatically detects the current language from `@ngx-translate/core`:
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
// If current language is 'en'
|
|
329
|
+
headersService.getLanguageHeader();
|
|
330
|
+
// Returns: 'en-CA'
|
|
331
|
+
|
|
332
|
+
// If current language is 'fr'
|
|
333
|
+
headersService.getLanguageHeader();
|
|
334
|
+
// Returns: 'fr-CA'
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## PathQueryService
|
|
340
|
+
|
|
341
|
+
URL path and query parameter building.
|
|
342
|
+
|
|
343
|
+
**Features:**
|
|
344
|
+
- 🔗 Path segment joining
|
|
345
|
+
- ❓ Query parameter building
|
|
346
|
+
- 🔍 Query string parsing
|
|
347
|
+
- 📝 URL normalization
|
|
348
|
+
|
|
349
|
+
**Usage:**
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
import { PathQueryService } from 'http-request-manager';
|
|
353
|
+
|
|
354
|
+
const pathQuery = inject(PathQueryService);
|
|
355
|
+
|
|
356
|
+
// Build path from segments
|
|
357
|
+
const path = pathQuery.buildPath(['api', 'users', 123]);
|
|
358
|
+
// Returns: 'api/users/123'
|
|
359
|
+
|
|
360
|
+
// Build query string
|
|
361
|
+
const queryString = pathQuery.buildQuery({
|
|
362
|
+
page: 1,
|
|
363
|
+
limit: 10,
|
|
364
|
+
filter: 'active'
|
|
365
|
+
});
|
|
366
|
+
// Returns: '?page=1&limit=10&filter=active'
|
|
367
|
+
|
|
368
|
+
// Parse query string
|
|
369
|
+
const params = pathQuery.parseQuery('?page=1&limit=10');
|
|
370
|
+
// Returns: { page: 1, limit: 10 }
|
|
371
|
+
|
|
372
|
+
// Join path with query
|
|
373
|
+
const fullUrl = pathQuery.buildUrl('api/users', { page: 1 });
|
|
374
|
+
// Returns: 'api/users?page=1'
|
|
375
|
+
|
|
376
|
+
// Normalize path
|
|
377
|
+
const normalized = pathQuery.normalizePath('api//users///123');
|
|
378
|
+
// Returns: 'api/users/123'
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
**Methods:**
|
|
382
|
+
```typescript
|
|
383
|
+
buildPath(segments: any[]): string
|
|
384
|
+
buildQuery(params: { [key: string]: any }): string
|
|
385
|
+
parseQuery(queryString: string): { [key: string]: any }
|
|
386
|
+
buildUrl(path: string, params?: { [key: string]: any }): string
|
|
387
|
+
normalizePath(path: string): string
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
## ObjectMergerService
|
|
393
|
+
|
|
394
|
+
Deep object merging with advanced options.
|
|
395
|
+
|
|
396
|
+
**Features:**
|
|
397
|
+
- 🔀 Deep merge objects
|
|
398
|
+
- 📦 Array merging options
|
|
399
|
+
- 🎯 Custom merge strategies
|
|
400
|
+
- 🛡️ Clone protection
|
|
401
|
+
|
|
402
|
+
**Usage:**
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
import { ObjectMergerService } from 'http-request-manager';
|
|
406
|
+
|
|
407
|
+
const merger = inject(ObjectMergerService);
|
|
408
|
+
|
|
409
|
+
// Basic deep merge
|
|
410
|
+
const merged = merger.deepMerge(obj1, obj2);
|
|
411
|
+
|
|
412
|
+
// Merge with options
|
|
413
|
+
const mergedWithOptions = merger.deepMerge(obj1, obj2, {
|
|
414
|
+
clone: true, // Don't mutate original objects
|
|
415
|
+
mergeArrays: false // Replace arrays instead of merging
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// Custom merge strategy
|
|
419
|
+
const customMerged = merger.deepMerge(obj1, obj2, {
|
|
420
|
+
customMerge: (key, targetValue, sourceValue) => {
|
|
421
|
+
if (key === 'price') {
|
|
422
|
+
return Math.max(targetValue, sourceValue);
|
|
423
|
+
}
|
|
424
|
+
return undefined; // Use default merge
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
**MergeOptions:**
|
|
430
|
+
```typescript
|
|
431
|
+
interface MergeOptions {
|
|
432
|
+
clone?: boolean; // Clone source objects
|
|
433
|
+
mergeArrays?: boolean; // Merge arrays vs replace
|
|
434
|
+
customMerge?: (key: string, target: any, source: any) => any;
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
**Example:**
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
const user1 = {
|
|
442
|
+
id: 1,
|
|
443
|
+
name: 'John',
|
|
444
|
+
preferences: {
|
|
445
|
+
theme: 'light',
|
|
446
|
+
notifications: true
|
|
447
|
+
},
|
|
448
|
+
tags: ['admin', 'user']
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
const user2 = {
|
|
452
|
+
name: 'John Doe',
|
|
453
|
+
preferences: {
|
|
454
|
+
language: 'en'
|
|
455
|
+
},
|
|
456
|
+
tags: ['moderator']
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
// Deep merge
|
|
460
|
+
const merged = merger.deepMerge(user1, user2);
|
|
461
|
+
// {
|
|
462
|
+
// id: 1,
|
|
463
|
+
// name: 'John Doe',
|
|
464
|
+
// preferences: {
|
|
465
|
+
// theme: 'light',
|
|
466
|
+
// notifications: true,
|
|
467
|
+
// language: 'en'
|
|
468
|
+
// },
|
|
469
|
+
// tags: ['admin', 'user', 'moderator']
|
|
470
|
+
// }
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
**Methods:**
|
|
474
|
+
```typescript
|
|
475
|
+
deepMerge<T>(target: T, source: Partial<T>, options?: MergeOptions): T
|
|
476
|
+
shallowMerge<T>(target: T, source: Partial<T>): T
|
|
477
|
+
mergeArray<T>(target: T[], source: T[]): T[]
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
## AppService
|
|
483
|
+
|
|
484
|
+
Application-level utilities.
|
|
485
|
+
|
|
486
|
+
**Features:**
|
|
487
|
+
- 📱 App ID management
|
|
488
|
+
- 🔍 Environment detection
|
|
489
|
+
- 📊 Logging utilities
|
|
490
|
+
|
|
491
|
+
**Usage:**
|
|
492
|
+
|
|
493
|
+
```typescript
|
|
494
|
+
import { AppService } from 'http-request-manager';
|
|
495
|
+
|
|
496
|
+
const appService = inject(AppService);
|
|
497
|
+
|
|
498
|
+
// Get app ID
|
|
499
|
+
const appId = appService.getAppId();
|
|
500
|
+
|
|
501
|
+
// Check environment
|
|
502
|
+
const isProduction = appService.isProduction();
|
|
503
|
+
const isDevelopment = appService.isDevelopment();
|
|
504
|
+
|
|
505
|
+
// Log with app context
|
|
506
|
+
appService.log('Info message', { data: 'value' });
|
|
507
|
+
appService.warn('Warning message');
|
|
508
|
+
appService.error('Error message');
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
## Related Documentation
|
|
514
|
+
|
|
515
|
+
- [`Encryption Guide`](ENCRYPTION_README.md) - Detailed encryption documentation
|
|
516
|
+
- [`Models Guide`](MODELS_README.md) - Configuration models
|
|
517
|
+
- [`LocalStorage`](LOCAL_STORAGE_README.md) - Encrypted storage usage
|
|
518
|
+
- [`Interceptors`](INTERCEPTOR_README.md) - Header interceptor details
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
checkTypes() {
|
|
522
|
+
this.typeResult = {
|
|
523
|
+
isString: this.utils.isString(this.testValue),
|
|
524
|
+
isObject: this.utils.isObject(this.testValue),
|
|
525
|
+
isJson: this.utils.isJSON(this.testValue)
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
testExpiration() {
|
|
530
|
+
const oneDayFromNow = this.utils.expires('1d');
|
|
531
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
|
532
|
+
|
|
533
|
+
this.expiryResult = {
|
|
534
|
+
oneDay: oneDayFromNow ? new Date(oneDayFromNow * 1000) : null,
|
|
535
|
+
current: new Date(currentTime * 1000),
|
|
536
|
+
hasExpired: this.utils.hasExpired(oneDayFromNow || 0),
|
|
537
|
+
timeRemaining: this.utils.expiresIn(oneDayFromNow || 0)
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
testConversions() {
|
|
542
|
+
this.conversionResult = {
|
|
543
|
+
base32ToHex: this.utils.base32ToHex('JBSWY3DPEHPK3PXP'),
|
|
544
|
+
binaryToHex: this.utils.binaryToHex('11000011100011110000')
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
## API Reference
|
|
551
|
+
|
|
552
|
+
### JSON Operations
|
|
553
|
+
|
|
554
|
+
#### JSONToString(value: any): string
|
|
555
|
+
|
|
556
|
+
Safely convert value to JSON string:
|
|
557
|
+
|
|
558
|
+
```typescript
|
|
559
|
+
const obj = { name: 'John', age: 30 };
|
|
560
|
+
const jsonString = this.utils.JSONToString(obj);
|
|
561
|
+
// Returns: '{"name":"John","age":30}'
|
|
562
|
+
|
|
563
|
+
const number = 42;
|
|
564
|
+
const stringValue = this.utils.JSONToString(number);
|
|
565
|
+
// Returns: '42'
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
#### stringToJSON(value: string): any
|
|
569
|
+
|
|
570
|
+
Safely parse JSON string:
|
|
571
|
+
|
|
572
|
+
```typescript
|
|
573
|
+
const jsonString = '{"name":"John","age":30}';
|
|
574
|
+
const obj = this.utils.stringToJSON(jsonString);
|
|
575
|
+
// Returns: { name: 'John', age: 30 }
|
|
576
|
+
|
|
577
|
+
const invalidJson = '{invalid json}';
|
|
578
|
+
const result = this.utils.stringToJSON(invalidJson);
|
|
579
|
+
// Returns: '{invalid json}' (unchanged)
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
### Type Checking
|
|
583
|
+
|
|
584
|
+
#### isString(value: any): boolean
|
|
585
|
+
|
|
586
|
+
Check if value is a string:
|
|
587
|
+
|
|
588
|
+
```typescript
|
|
589
|
+
this.utils.isString('hello'); // true
|
|
590
|
+
this.utils.isString(123); // false
|
|
591
|
+
this.utils.isString(null); // false
|
|
592
|
+
this.utils.isString(undefined); // false
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
#### isObject(value: any): boolean
|
|
596
|
+
|
|
597
|
+
Check if value is an object:
|
|
598
|
+
|
|
599
|
+
```typescript
|
|
600
|
+
this.utils.isObject({}); // true
|
|
601
|
+
this.utils.isObject({ name: 'John' }); // true
|
|
602
|
+
this.utils.isObject(null); // false
|
|
603
|
+
this.utils.isObject([]); // true (arrays are objects)
|
|
604
|
+
this.utils.isObject('string'); // false
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
#### isJSON(value: any): boolean
|
|
608
|
+
|
|
609
|
+
Check if value is valid JSON:
|
|
610
|
+
|
|
611
|
+
```typescript
|
|
612
|
+
this.utils.isJSON('{"name":"John"}'); // true
|
|
613
|
+
this.utils.isJSON('not json'); // false
|
|
614
|
+
this.utils.isJSON(123); // false
|
|
615
|
+
this.utils.isJSON(null); // false
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
### Property Access
|
|
619
|
+
|
|
620
|
+
#### getValueByProp(obj: any, prop: string): any
|
|
621
|
+
|
|
622
|
+
Get property value by string path:
|
|
623
|
+
|
|
624
|
+
```typescript
|
|
625
|
+
const obj = { user: { name: 'John', settings: { theme: 'dark' } } };
|
|
626
|
+
|
|
627
|
+
this.utils.getValueByProp(obj, 'user.name'); // undefined (doesn't support nesting)
|
|
628
|
+
this.utils.getValueByProp(obj.user, 'name'); // 'John'
|
|
629
|
+
this.utils.getValueByProp(obj.user.settings, 'theme'); // 'dark'
|
|
630
|
+
this.utils.getValueByProp(obj, 'nonexistent'); // false
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
### Object Comparison
|
|
634
|
+
|
|
635
|
+
#### objectsEqual(x: any, y: any): boolean
|
|
636
|
+
|
|
637
|
+
Deep comparison of two objects:
|
|
638
|
+
|
|
639
|
+
```typescript
|
|
640
|
+
const obj1 = { name: 'John', age: 30, active: true };
|
|
641
|
+
const obj2 = { name: 'John', age: 30, active: true };
|
|
642
|
+
const obj3 = { name: 'John', age: 31, active: true };
|
|
643
|
+
|
|
644
|
+
this.utils.objectsEqual(obj1, obj2); // true
|
|
645
|
+
this.utils.objectsEqual(obj1, obj3); // false
|
|
646
|
+
|
|
647
|
+
// Nested objects
|
|
648
|
+
const nested1 = { user: { name: 'John', settings: { theme: 'dark' } } };
|
|
649
|
+
const nested2 = { user: { name: 'John', settings: { theme: 'dark' } } };
|
|
650
|
+
this.utils.objectsEqual(nested1, nested2); // true
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
### HTTP Operations
|
|
654
|
+
|
|
655
|
+
#### getJSON(file: string): Observable<any>
|
|
656
|
+
|
|
657
|
+
Get JSON file from assets:
|
|
658
|
+
|
|
659
|
+
```typescript
|
|
660
|
+
this.utils.getJSON('data/config.json').subscribe(config => {
|
|
661
|
+
console.log('Config loaded:', config);
|
|
662
|
+
});
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
### Time Properties
|
|
666
|
+
|
|
667
|
+
#### today: number
|
|
668
|
+
|
|
669
|
+
Get current time in milliseconds:
|
|
670
|
+
|
|
671
|
+
```typescript
|
|
672
|
+
const currentTime = this.utils.today;
|
|
673
|
+
console.log('Current time (ms):', currentTime);
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
### Expiration Management
|
|
677
|
+
|
|
678
|
+
#### expires(str?: string): number | undefined
|
|
679
|
+
|
|
680
|
+
Convert duration string to epoch timestamp:
|
|
681
|
+
|
|
682
|
+
```typescript
|
|
683
|
+
// Time units: y=years, m=months, w=weeks, d=days, hr=hours, mn=minutes, s=seconds
|
|
684
|
+
|
|
685
|
+
this.utils.expires('1d'); // Current time + 1 day
|
|
686
|
+
this.utils.expires('2h'); // Current time + 2 hours
|
|
687
|
+
this.utils.expires('30m'); // Current time + 30 minutes
|
|
688
|
+
this.utils.expires('1w'); // Current time + 1 week
|
|
689
|
+
this.utils.expires('1y'); // Current time + 1 year
|
|
690
|
+
|
|
691
|
+
this.utils.expires(); // undefined (no duration provided)
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
#### hasExpired(expiryDate: number): boolean
|
|
695
|
+
|
|
696
|
+
Check if timestamp has expired:
|
|
697
|
+
|
|
698
|
+
```typescript
|
|
699
|
+
const futureTime = this.utils.expires('1d');
|
|
700
|
+
const pastTime = Math.floor(Date.now() / 1000) - 3600; // 1 hour ago
|
|
701
|
+
|
|
702
|
+
this.utils.hasExpired(futureTime); // false
|
|
703
|
+
this.utils.hasExpired(pastTime); // true
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
#### hasExpiry(setting: any): boolean
|
|
707
|
+
|
|
708
|
+
Check if setting has expiration:
|
|
709
|
+
|
|
710
|
+
```typescript
|
|
711
|
+
const setting1 = { expires: 1234567890 };
|
|
712
|
+
const setting2 = { encrypted: true };
|
|
713
|
+
|
|
714
|
+
this.utils.hasExpiry(setting1); // true
|
|
715
|
+
this.utils.hasExpiry(setting2); // false
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
#### expiresIn(expiryDate: number): string | undefined
|
|
719
|
+
|
|
720
|
+
Get human-readable time remaining:
|
|
721
|
+
|
|
722
|
+
```typescript
|
|
723
|
+
const oneDayFromNow = this.utils.expires('1d');
|
|
724
|
+
const oneHourFromNow = this.utils.expires('1h');
|
|
725
|
+
|
|
726
|
+
console.log(this.utils.expiresIn(oneDayFromNow)); // "1d"
|
|
727
|
+
console.log(this.utils.expiresIn(oneHourFromNow)); // "1h"
|
|
728
|
+
|
|
729
|
+
// For expired dates
|
|
730
|
+
const pastTime = Math.floor(Date.now() / 1000) - 3600;
|
|
731
|
+
console.log(this.utils.expiresIn(pastTime)); // "Expired"
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
### Conversions
|
|
735
|
+
|
|
736
|
+
#### base32ToHex(base32: string): string
|
|
737
|
+
|
|
738
|
+
Convert Base32 string to hexadecimal:
|
|
739
|
+
|
|
740
|
+
```typescript
|
|
741
|
+
this.utils.base32ToHex('JBSWY3DPEHPK3PXP'); // '48656C6C6F20576F726C64'
|
|
742
|
+
this.utils.base32ToHex('MY'); // '66'
|
|
743
|
+
this.utils.base32ToHex('MZXQ6==='); // '6C'
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
#### binaryToHex(binary: string): string
|
|
747
|
+
|
|
748
|
+
Convert binary string to hexadecimal:
|
|
749
|
+
|
|
750
|
+
```typescript
|
|
751
|
+
this.utils.binaryToHex('11000011100011110000'); // 'C78F0'
|
|
752
|
+
this.utils.binaryToHex('10101010'); // 'AA000000'
|
|
753
|
+
this.utils.binaryToHex('1111'); // 'F000'
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
### String Utilities
|
|
757
|
+
|
|
758
|
+
#### lc(str: string): string
|
|
759
|
+
|
|
760
|
+
Convert string to lowercase with special character replacement:
|
|
761
|
+
|
|
762
|
+
```typescript
|
|
763
|
+
this.utils.lc('Hello World!'); // 'helloworld'
|
|
764
|
+
this.utils.lc('Test-Case_String'); // 'testcasestring'
|
|
765
|
+
this.utils.lc('UPPER CASE'); // 'uppercase'
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
## Advanced Examples
|
|
769
|
+
|
|
770
|
+
### Data Validation Service
|
|
771
|
+
|
|
772
|
+
```typescript
|
|
773
|
+
import { Injectable } from '@angular/core';
|
|
774
|
+
import { UtilsService } from 'http-request-manager';
|
|
775
|
+
|
|
776
|
+
interface ValidationResult {
|
|
777
|
+
isValid: boolean;
|
|
778
|
+
errors: string[];
|
|
779
|
+
sanitizedData?: any;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
@Injectable({
|
|
783
|
+
providedIn: 'root'
|
|
784
|
+
})
|
|
785
|
+
export class DataValidationService {
|
|
786
|
+
constructor(private utils: UtilsService) {}
|
|
787
|
+
|
|
788
|
+
validateAndSanitize<T>(data: any, schema: any): ValidationResult {
|
|
789
|
+
const errors: string[] = [];
|
|
790
|
+
let sanitizedData: any = {};
|
|
791
|
+
|
|
792
|
+
try {
|
|
793
|
+
// Check if data is object
|
|
794
|
+
if (!this.utils.isObject(data)) {
|
|
795
|
+
errors.push('Data must be an object');
|
|
796
|
+
return { isValid: false, errors };
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// Sanitize each field according to schema
|
|
800
|
+
for (const [field, rules] of Object.entries(schema)) {
|
|
801
|
+
const value = data[field];
|
|
802
|
+
const sanitizedValue = this.sanitizeField(field, value, rules as any);
|
|
803
|
+
|
|
804
|
+
if (sanitizedValue !== undefined) {
|
|
805
|
+
sanitizedData[field] = sanitizedValue;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
return {
|
|
810
|
+
isValid: errors.length === 0,
|
|
811
|
+
errors,
|
|
812
|
+
sanitizedData
|
|
813
|
+
};
|
|
814
|
+
} catch (error) {
|
|
815
|
+
errors.push(`Validation error: ${error}`);
|
|
816
|
+
return { isValid: false, errors };
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
private sanitizeField(field: string, value: any, rules: any): any {
|
|
821
|
+
// Handle required fields
|
|
822
|
+
if (rules.required && (value === undefined || value === null)) {
|
|
823
|
+
throw new Error(`Field '${field}' is required`);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
if (value === undefined || value === null) {
|
|
827
|
+
return rules.default;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// Type validation
|
|
831
|
+
switch (rules.type) {
|
|
832
|
+
case 'string':
|
|
833
|
+
if (typeof value !== 'string') {
|
|
834
|
+
throw new Error(`Field '${field}' must be a string`);
|
|
835
|
+
}
|
|
836
|
+
break;
|
|
837
|
+
|
|
838
|
+
case 'number':
|
|
839
|
+
if (typeof value !== 'number' && isNaN(Number(value))) {
|
|
840
|
+
throw new Error(`Field '${field}' must be a number`);
|
|
841
|
+
}
|
|
842
|
+
value = Number(value);
|
|
843
|
+
break;
|
|
844
|
+
|
|
845
|
+
case 'boolean':
|
|
846
|
+
if (typeof value !== 'boolean') {
|
|
847
|
+
throw new Error(`Field '${field}' must be a boolean`);
|
|
848
|
+
}
|
|
849
|
+
break;
|
|
850
|
+
|
|
851
|
+
case 'object':
|
|
852
|
+
if (!this.utils.isObject(value)) {
|
|
853
|
+
throw new Error(`Field '${field}' must be an object`);
|
|
854
|
+
}
|
|
855
|
+
break;
|
|
856
|
+
|
|
857
|
+
case 'json':
|
|
858
|
+
if (!this.utils.isJSON(value)) {
|
|
859
|
+
throw new Error(`Field '${field}' must be valid JSON`);
|
|
860
|
+
}
|
|
861
|
+
value = this.utils.stringToJSON(value);
|
|
862
|
+
break;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// String-specific rules
|
|
866
|
+
if (rules.type === 'string') {
|
|
867
|
+
// Min/max length
|
|
868
|
+
if (rules.minLength && value.length < rules.minLength) {
|
|
869
|
+
throw new Error(`Field '${field}' must be at least ${rules.minLength} characters`);
|
|
870
|
+
}
|
|
871
|
+
if (rules.maxLength && value.length > rules.maxLength) {
|
|
872
|
+
throw new Error(`Field '${field}' must be no more than ${rules.maxLength} characters`);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// Pattern matching
|
|
876
|
+
if (rules.pattern && !rules.pattern.test(value)) {
|
|
877
|
+
throw new Error(`Field '${field}' does not match required pattern`);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// Sanitization
|
|
881
|
+
if (rules.lowercase) {
|
|
882
|
+
value = value.toLowerCase();
|
|
883
|
+
}
|
|
884
|
+
if (rules.trim) {
|
|
885
|
+
value = value.trim();
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// Number-specific rules
|
|
890
|
+
if (rules.type === 'number') {
|
|
891
|
+
if (rules.min !== undefined && value < rules.min) {
|
|
892
|
+
throw new Error(`Field '${field}' must be at least ${rules.min}`);
|
|
893
|
+
}
|
|
894
|
+
if (rules.max !== undefined && value > rules.max) {
|
|
895
|
+
throw new Error(`Field '${field}' must be no more than ${rules.max}`);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// Array-specific rules
|
|
900
|
+
if (rules.type === 'array') {
|
|
901
|
+
if (!Array.isArray(value)) {
|
|
902
|
+
throw new Error(`Field '${field}' must be an array`);
|
|
903
|
+
}
|
|
904
|
+
if (rules.minItems && value.length < rules.minItems) {
|
|
905
|
+
throw new Error(`Field '${field}' must have at least ${rules.minItems} items`);
|
|
906
|
+
}
|
|
907
|
+
if (rules.maxItems && value.length > rules.maxItems) {
|
|
908
|
+
throw new Error(`Field '${field}' must have no more than ${rules.maxItems} items`);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
return value;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
// Predefined schemas
|
|
916
|
+
getUserSchema() {
|
|
917
|
+
return {
|
|
918
|
+
name: {
|
|
919
|
+
type: 'string',
|
|
920
|
+
required: true,
|
|
921
|
+
minLength: 2,
|
|
922
|
+
maxLength: 50,
|
|
923
|
+
pattern: /^[a-zA-Z\s]+$/,
|
|
924
|
+
trim: true
|
|
925
|
+
},
|
|
926
|
+
email: {
|
|
927
|
+
type: 'string',
|
|
928
|
+
required: true,
|
|
929
|
+
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
|
930
|
+
lowercase: true,
|
|
931
|
+
trim: true
|
|
932
|
+
},
|
|
933
|
+
age: {
|
|
934
|
+
type: 'number',
|
|
935
|
+
min: 0,
|
|
936
|
+
max: 150
|
|
937
|
+
},
|
|
938
|
+
preferences: {
|
|
939
|
+
type: 'object',
|
|
940
|
+
default: {}
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
getProductSchema() {
|
|
946
|
+
return {
|
|
947
|
+
name: {
|
|
948
|
+
type: 'string',
|
|
949
|
+
required: true,
|
|
950
|
+
minLength: 1,
|
|
951
|
+
maxLength: 100
|
|
952
|
+
},
|
|
953
|
+
price: {
|
|
954
|
+
type: 'number',
|
|
955
|
+
required: true,
|
|
956
|
+
min: 0
|
|
957
|
+
},
|
|
958
|
+
description: {
|
|
959
|
+
type: 'string',
|
|
960
|
+
maxLength: 500,
|
|
961
|
+
trim: true
|
|
962
|
+
},
|
|
963
|
+
tags: {
|
|
964
|
+
type: 'array',
|
|
965
|
+
maxItems: 10,
|
|
966
|
+
default: []
|
|
967
|
+
}
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
```
|
|
972
|
+
|
|
973
|
+
### Cache Management Service
|
|
974
|
+
|
|
975
|
+
```typescript
|
|
976
|
+
@Injectable()
|
|
977
|
+
export class CacheManagerService {
|
|
978
|
+
constructor(private utils: UtilsService) {}
|
|
979
|
+
|
|
980
|
+
createCache<T>(key: string, ttl: string, maxSize?: number) {
|
|
981
|
+
return {
|
|
982
|
+
key,
|
|
983
|
+
ttl: this.utils.expires(ttl),
|
|
984
|
+
maxSize,
|
|
985
|
+
data: new Map<string, { value: T; timestamp: number }>(),
|
|
986
|
+
|
|
987
|
+
set(itemKey: string, value: T): void {
|
|
988
|
+
// Clean up expired items
|
|
989
|
+
this.cleanup();
|
|
990
|
+
|
|
991
|
+
// Check size limit
|
|
992
|
+
if (this.maxSize && this.data.size >= this.maxSize) {
|
|
993
|
+
// Remove oldest item
|
|
994
|
+
const oldestKey = this.data.keys().next().value;
|
|
995
|
+
this.data.delete(oldestKey);
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
this.data.set(itemKey, {
|
|
999
|
+
value,
|
|
1000
|
+
timestamp: Date.now()
|
|
1001
|
+
});
|
|
1002
|
+
},
|
|
1003
|
+
|
|
1004
|
+
get(itemKey: string): T | null {
|
|
1005
|
+
this.cleanup();
|
|
1006
|
+
|
|
1007
|
+
const item = this.data.get(itemKey);
|
|
1008
|
+
if (item) {
|
|
1009
|
+
return item.value;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
return null;
|
|
1013
|
+
},
|
|
1014
|
+
|
|
1015
|
+
has(itemKey: string): boolean {
|
|
1016
|
+
this.cleanup();
|
|
1017
|
+
return this.data.has(itemKey);
|
|
1018
|
+
},
|
|
1019
|
+
|
|
1020
|
+
delete(itemKey: string): boolean {
|
|
1021
|
+
return this.data.delete(itemKey);
|
|
1022
|
+
},
|
|
1023
|
+
|
|
1024
|
+
clear(): void {
|
|
1025
|
+
this.data.clear();
|
|
1026
|
+
},
|
|
1027
|
+
|
|
1028
|
+
size(): number {
|
|
1029
|
+
this.cleanup();
|
|
1030
|
+
return this.data.size;
|
|
1031
|
+
},
|
|
1032
|
+
|
|
1033
|
+
cleanup(): void {
|
|
1034
|
+
const now = Date.now();
|
|
1035
|
+
const expiredKeys: string[] = [];
|
|
1036
|
+
|
|
1037
|
+
this.data.forEach((item, key) => {
|
|
1038
|
+
if (item.timestamp < this.ttl * 1000) {
|
|
1039
|
+
expiredKeys.push(key);
|
|
1040
|
+
}
|
|
1041
|
+
});
|
|
1042
|
+
|
|
1043
|
+
expiredKeys.forEach(key => this.data.delete(key));
|
|
1044
|
+
},
|
|
1045
|
+
|
|
1046
|
+
export(): Array<{ key: string; value: T; timestamp: number }> {
|
|
1047
|
+
this.cleanup();
|
|
1048
|
+
return Array.from(this.data.entries()).map(([key, item]) => ({
|
|
1049
|
+
key,
|
|
1050
|
+
value: item.value,
|
|
1051
|
+
timestamp: item.timestamp
|
|
1052
|
+
}));
|
|
1053
|
+
},
|
|
1054
|
+
|
|
1055
|
+
import(items: Array<{ key: string; value: T; timestamp: number }>): void {
|
|
1056
|
+
items.forEach(item => {
|
|
1057
|
+
if (item.timestamp > this.ttl * 1000) {
|
|
1058
|
+
this.data.set(item.key, {
|
|
1059
|
+
value: item.value,
|
|
1060
|
+
timestamp: item.timestamp
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// Global cache instance
|
|
1069
|
+
private globalCache = this.createCache('global-cache', '1h', 100);
|
|
1070
|
+
|
|
1071
|
+
cacheResult<T>(key: string, fn: () => T, ttl: string = '1h'): T {
|
|
1072
|
+
const cached = this.globalCache.get(key);
|
|
1073
|
+
if (cached !== null) {
|
|
1074
|
+
return cached;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
const result = fn();
|
|
1078
|
+
this.globalCache.set(key, result);
|
|
1079
|
+
return result;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
invalidateCache(key: string): void {
|
|
1083
|
+
this.globalCache.delete(key);
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
clearCache(): void {
|
|
1087
|
+
this.globalCache.clear();
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
```
|
|
1091
|
+
|
|
1092
|
+
### Configuration Manager
|
|
1093
|
+
|
|
1094
|
+
```typescript
|
|
1095
|
+
@Injectable()
|
|
1096
|
+
export class ConfigurationManagerService {
|
|
1097
|
+
private configs = new Map<string, any>();
|
|
1098
|
+
|
|
1099
|
+
constructor(private utils: UtilsService) {}
|
|
1100
|
+
|
|
1101
|
+
loadConfig<T>(configKey: string, defaultValue: T, expiration?: string): T {
|
|
1102
|
+
const cached = this.configs.get(configKey);
|
|
1103
|
+
|
|
1104
|
+
if (cached && !this.isExpired(cached.timestamp, expiration)) {
|
|
1105
|
+
return cached.value;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
// Load from storage or API
|
|
1109
|
+
const value = this.loadConfigValue<T>(configKey, defaultValue);
|
|
1110
|
+
|
|
1111
|
+
this.configs.set(configKey, {
|
|
1112
|
+
value,
|
|
1113
|
+
timestamp: Date.now(),
|
|
1114
|
+
expiresAt: expiration ? this.utils.expires(expiration) : undefined
|
|
1115
|
+
});
|
|
1116
|
+
|
|
1117
|
+
return value;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
updateConfig<T>(configKey: string, value: T, expiration?: string): void {
|
|
1121
|
+
this.configs.set(configKey, {
|
|
1122
|
+
value,
|
|
1123
|
+
timestamp: Date.now(),
|
|
1124
|
+
expiresAt: expiration ? this.utils.expires(expiration) : undefined
|
|
1125
|
+
});
|
|
1126
|
+
|
|
1127
|
+
this.saveConfigValue(configKey, value);
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
getConfig<T>(configKey: string): T | null {
|
|
1131
|
+
const cached = this.configs.get(configKey);
|
|
1132
|
+
if (cached && !this.isExpired(cached.timestamp, undefined)) {
|
|
1133
|
+
return cached.value;
|
|
1134
|
+
}
|
|
1135
|
+
return null;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
invalidateConfig(configKey: string): void {
|
|
1139
|
+
this.configs.delete(configKey);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
clearConfigs(): void {
|
|
1143
|
+
this.configs.clear();
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
private isExpired(timestamp: number, expiration?: string): boolean {
|
|
1147
|
+
if (!expiration) return false;
|
|
1148
|
+
|
|
1149
|
+
const expiryTime = this.utils.expires(expiration);
|
|
1150
|
+
return this.utils.hasExpired(expiryTime || 0);
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
private loadConfigValue<T>(configKey: string, defaultValue: T): T {
|
|
1154
|
+
try {
|
|
1155
|
+
const stored = localStorage.getItem(`config:${configKey}`);
|
|
1156
|
+
if (stored) {
|
|
1157
|
+
const parsed = JSON.parse(stored);
|
|
1158
|
+
return parsed.value || defaultValue;
|
|
1159
|
+
}
|
|
1160
|
+
} catch (error) {
|
|
1161
|
+
console.warn(`Failed to load config ${configKey}:`, error);
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
return defaultValue;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
private saveConfigValue(configKey: string, value: any): void {
|
|
1168
|
+
try {
|
|
1169
|
+
localStorage.setItem(`config:${configKey}`, JSON.stringify({
|
|
1170
|
+
value,
|
|
1171
|
+
savedAt: new Date().toISOString()
|
|
1172
|
+
}));
|
|
1173
|
+
} catch (error) {
|
|
1174
|
+
console.warn(`Failed to save config ${configKey}:`, error);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
// Predefined configurations
|
|
1179
|
+
getAppConfig() {
|
|
1180
|
+
return this.loadConfig('app-config', {
|
|
1181
|
+
version: '1.0.0',
|
|
1182
|
+
apiUrl: 'http://localhost:3000',
|
|
1183
|
+
features: {
|
|
1184
|
+
enableAnalytics: true,
|
|
1185
|
+
enableNotifications: true,
|
|
1186
|
+
enableOffline: false
|
|
1187
|
+
},
|
|
1188
|
+
limits: {
|
|
1189
|
+
maxFileSize: 10485760, // 10MB
|
|
1190
|
+
maxRequestsPerMinute: 100
|
|
1191
|
+
}
|
|
1192
|
+
}, '1h');
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
getUserPreferences() {
|
|
1196
|
+
return this.loadConfig('user-preferences', {
|
|
1197
|
+
theme: 'light',
|
|
1198
|
+
language: 'en',
|
|
1199
|
+
notifications: {
|
|
1200
|
+
email: true,
|
|
1201
|
+
push: true,
|
|
1202
|
+
sms: false
|
|
1203
|
+
},
|
|
1204
|
+
privacy: {
|
|
1205
|
+
analytics: true,
|
|
1206
|
+
marketing: false
|
|
1207
|
+
}
|
|
1208
|
+
}, '30d');
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
getFeatureFlags() {
|
|
1212
|
+
return this.loadConfig('feature-flags', {
|
|
1213
|
+
newDashboard: false,
|
|
1214
|
+
darkMode: true,
|
|
1215
|
+
betaFeatures: false,
|
|
1216
|
+
experimentalUI: false
|
|
1217
|
+
}, '1d');
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
```
|
|
1221
|
+
|
|
1222
|
+
### URL Parameter Parser
|
|
1223
|
+
|
|
1224
|
+
```typescript
|
|
1225
|
+
@Injectable()
|
|
1226
|
+
export class URLParserService {
|
|
1227
|
+
constructor(private utils: UtilsService) {}
|
|
1228
|
+
|
|
1229
|
+
parseQueryParams(url: string): Record<string, string> {
|
|
1230
|
+
const params: Record<string, string> = {};
|
|
1231
|
+
const queryString = url.split('?')[1];
|
|
1232
|
+
|
|
1233
|
+
if (!queryString) return params;
|
|
1234
|
+
|
|
1235
|
+
queryString.split('&').forEach(pair => {
|
|
1236
|
+
const [key, value] = pair.split('=');
|
|
1237
|
+
if (key && value) {
|
|
1238
|
+
params[decodeURIComponent(key)] = decodeURIComponent(value);
|
|
1239
|
+
}
|
|
1240
|
+
});
|
|
1241
|
+
|
|
1242
|
+
return params;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
buildQueryString(params: Record<string, any>): string {
|
|
1246
|
+
const searchParams = new URLSearchParams();
|
|
1247
|
+
|
|
1248
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
1249
|
+
if (value !== null && value !== undefined) {
|
|
1250
|
+
if (Array.isArray(value)) {
|
|
1251
|
+
value.forEach(item => searchParams.append(key, String(item)));
|
|
1252
|
+
} else {
|
|
1253
|
+
searchParams.set(key, String(value));
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
});
|
|
1257
|
+
|
|
1258
|
+
return searchParams.toString();
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
extractUrlParams(template: string, actualUrl: string): Record<string, string> {
|
|
1262
|
+
const templateParts = template.split('/');
|
|
1263
|
+
const actualParts = actualUrl.split('/');
|
|
1264
|
+
const params: Record<string, string> = {};
|
|
1265
|
+
|
|
1266
|
+
if (templateParts.length !== actualParts.length) {
|
|
1267
|
+
return params;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
templateParts.forEach((part, index) => {
|
|
1271
|
+
if (part.startsWith(':')) {
|
|
1272
|
+
const paramName = part.substring(1);
|
|
1273
|
+
params[paramName] = actualParts[index];
|
|
1274
|
+
}
|
|
1275
|
+
});
|
|
1276
|
+
|
|
1277
|
+
return params;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
buildUrl(template: string, params: Record<string, string>): string {
|
|
1281
|
+
let url = template;
|
|
1282
|
+
|
|
1283
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
1284
|
+
url = url.replace(`:${key}`, encodeURIComponent(value));
|
|
1285
|
+
});
|
|
1286
|
+
|
|
1287
|
+
return url;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
// URL validation
|
|
1291
|
+
isValidUrl(url: string): boolean {
|
|
1292
|
+
try {
|
|
1293
|
+
new URL(url);
|
|
1294
|
+
return true;
|
|
1295
|
+
} catch {
|
|
1296
|
+
return false;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
isValidEmail(email: string): boolean {
|
|
1301
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1302
|
+
return emailRegex.test(email);
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
sanitizeUrl(url: string): string {
|
|
1306
|
+
// Remove potentially dangerous protocols
|
|
1307
|
+
const dangerousProtocols = ['javascript:', 'data:', 'vbscript:'];
|
|
1308
|
+
|
|
1309
|
+
dangerousProtocols.forEach(protocol => {
|
|
1310
|
+
if (url.toLowerCase().startsWith(protocol)) {
|
|
1311
|
+
throw new Error(`Dangerous protocol detected: ${protocol}`);
|
|
1312
|
+
}
|
|
1313
|
+
});
|
|
1314
|
+
|
|
1315
|
+
return url;
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
```
|
|
1319
|
+
|
|
1320
|
+
## Integration with Other Services
|
|
1321
|
+
|
|
1322
|
+
### With HTTP State Service
|
|
1323
|
+
|
|
1324
|
+
```typescript
|
|
1325
|
+
@Injectable()
|
|
1326
|
+
export class SmartCacheService extends HTTPManagerStateService<any> {
|
|
1327
|
+
private utils = inject(UtilsService);
|
|
1328
|
+
private cache = new Map<string, { data: any; expiresAt: number }>();
|
|
1329
|
+
|
|
1330
|
+
constructor() {
|
|
1331
|
+
super(
|
|
1332
|
+
ApiRequest.adapt({ path: ['data'] }),
|
|
1333
|
+
DataType.OBJECT
|
|
1334
|
+
);
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
fetchWithCache(key: string, fetchFn: () => any, ttl: string = '1h') {
|
|
1338
|
+
// Check cache first
|
|
1339
|
+
const cached = this.cache.get(key);
|
|
1340
|
+
if (cached && !this.utils.hasExpired(cached.expiresAt)) {
|
|
1341
|
+
return of(cached.data);
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
// Fetch and cache
|
|
1345
|
+
return fetchFn().pipe(
|
|
1346
|
+
tap(data => {
|
|
1347
|
+
const expiresAt = this.utils.expires(ttl);
|
|
1348
|
+
this.cache.set(key, {
|
|
1349
|
+
data,
|
|
1350
|
+
expiresAt: expiresAt || 0
|
|
1351
|
+
});
|
|
1352
|
+
})
|
|
1353
|
+
);
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
invalidateCache(key: string): void {
|
|
1357
|
+
this.cache.delete(key);
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
clearExpiredCache(): void {
|
|
1361
|
+
const now = Date.now() / 1000;
|
|
1362
|
+
const expiredKeys: string[] = [];
|
|
1363
|
+
|
|
1364
|
+
this.cache.forEach((item, key) => {
|
|
1365
|
+
if (item.expiresAt < now) {
|
|
1366
|
+
expiredKeys.push(key);
|
|
1367
|
+
}
|
|
1368
|
+
});
|
|
1369
|
+
|
|
1370
|
+
expiredKeys.forEach(key => this.cache.delete(key));
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
```
|
|
1374
|
+
|
|
1375
|
+
### With Local Storage
|
|
1376
|
+
|
|
1377
|
+
```typescript
|
|
1378
|
+
@Injectable()
|
|
1379
|
+
export class SmartStorageService {
|
|
1380
|
+
private utils = inject(UtilsService);
|
|
1381
|
+
private storage = inject(LocalStorageManagerService);
|
|
1382
|
+
|
|
1383
|
+
setWithExpiration(key: string, value: any, expiration: string): void {
|
|
1384
|
+
const expiresAt = this.utils.expires(expiration);
|
|
1385
|
+
|
|
1386
|
+
this.storage.setItem(key, {
|
|
1387
|
+
value,
|
|
1388
|
+
expiresAt,
|
|
1389
|
+
createdAt: Date.now()
|
|
1390
|
+
});
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
getWithExpiration<T>(key: string): T | null {
|
|
1394
|
+
const stored = this.storage.getItem(key);
|
|
1395
|
+
|
|
1396
|
+
if (!stored) return null;
|
|
1397
|
+
|
|
1398
|
+
if (this.utils.hasExpired(stored.expiresAt)) {
|
|
1399
|
+
this.storage.removeItem(key);
|
|
1400
|
+
return null;
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
return stored.value;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
setIfNewer(key: string, value: any, timestamp: number): boolean {
|
|
1407
|
+
const stored = this.storage.getItem(key);
|
|
1408
|
+
|
|
1409
|
+
if (stored && stored.timestamp > timestamp) {
|
|
1410
|
+
return false; // Stored version is newer
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
this.storage.setItem(key, {
|
|
1414
|
+
value,
|
|
1415
|
+
timestamp,
|
|
1416
|
+
updatedAt: Date.now()
|
|
1417
|
+
});
|
|
1418
|
+
|
|
1419
|
+
return true;
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
```
|
|
1423
|
+
|
|
1424
|
+
## Best Practices
|
|
1425
|
+
|
|
1426
|
+
### 1. Type Safety
|
|
1427
|
+
|
|
1428
|
+
```typescript
|
|
1429
|
+
// ✅ Good - Validate types before operations
|
|
1430
|
+
if (this.utils.isString(value)) {
|
|
1431
|
+
const result = this.utils.lc(value);
|
|
1432
|
+
// Safe to use result as string
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
// ❌ Avoid - No type checking
|
|
1436
|
+
const result = this.utils.lc(value); // Could fail if value is not string
|
|
1437
|
+
```
|
|
1438
|
+
|
|
1439
|
+
### 2. Error Handling
|
|
1440
|
+
|
|
1441
|
+
```typescript
|
|
1442
|
+
// ✅ Good - Handle potential errors
|
|
1443
|
+
try {
|
|
1444
|
+
const parsed = this.utils.stringToJSON(jsonString);
|
|
1445
|
+
// Process parsed data
|
|
1446
|
+
} catch (error) {
|
|
1447
|
+
console.error('Failed to parse JSON:', error);
|
|
1448
|
+
return defaultValue;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
// ❌ Avoid - No error handling
|
|
1452
|
+
const parsed = this.utils.stringToJSON(jsonString); // Could throw
|
|
1453
|
+
```
|
|
1454
|
+
|
|
1455
|
+
### 3. Performance Considerations
|
|
1456
|
+
|
|
1457
|
+
```typescript
|
|
1458
|
+
// ✅ Good - Cache expensive operations
|
|
1459
|
+
private cachedBase32Conversion = new Map<string, string>();
|
|
1460
|
+
|
|
1461
|
+
convertBase32(value: string): string {
|
|
1462
|
+
if (!this.cachedBase32Conversion.has(value)) {
|
|
1463
|
+
this.cachedBase32Conversion.set(value, this.utils.base32ToHex(value));
|
|
1464
|
+
}
|
|
1465
|
+
return this.cachedBase32Conversion.get(value)!;
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
// ❌ Avoid - Repeated expensive operations
|
|
1469
|
+
// Converting the same value multiple times without caching
|
|
1470
|
+
```
|
|
1471
|
+
|
|
1472
|
+
### 4. Validation Before Usage
|
|
1473
|
+
|
|
1474
|
+
```typescript
|
|
1475
|
+
// ✅ Good - Validate input before processing
|
|
1476
|
+
validateAndProcess(data: any): any {
|
|
1477
|
+
if (!this.utils.isObject(data)) {
|
|
1478
|
+
throw new Error('Data must be an object');
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
if (!this.utils.isJSON(JSON.stringify(data))) {
|
|
1482
|
+
throw new Error('Data contains invalid JSON');
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
return this.processData(data);
|
|
1486
|
+
}
|
|
1487
|
+
```
|
|
1488
|
+
|
|
1489
|
+
## Performance Optimization
|
|
1490
|
+
|
|
1491
|
+
### Batch Operations
|
|
1492
|
+
|
|
1493
|
+
```typescript
|
|
1494
|
+
@Injectable()
|
|
1495
|
+
export class BatchUtilsService {
|
|
1496
|
+
private utils = inject(UtilsService);
|
|
1497
|
+
|
|
1498
|
+
batchConvertExpirations(durations: string[]): number[] {
|
|
1499
|
+
return durations.map(duration => this.utils.expires(duration) || 0);
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
batchValidateTypes(values: any[]): Array<{value: any; isString: boolean; isObject: boolean}> {
|
|
1503
|
+
return values.map(value => ({
|
|
1504
|
+
value,
|
|
1505
|
+
isString: this.utils.isString(value),
|
|
1506
|
+
isObject: this.utils.isObject(value)
|
|
1507
|
+
}));
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
batchCompareObjects(pairs: Array<{x: any; y: any}>): boolean[] {
|
|
1511
|
+
return pairs.map(pair => this.utils.objectsEqual(pair.x, pair.y));
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
```
|
|
1515
|
+
|
|
1516
|
+
### Memoization
|
|
1517
|
+
|
|
1518
|
+
```typescript
|
|
1519
|
+
@Injectable()
|
|
1520
|
+
export class MemoizedUtilsService {
|
|
1521
|
+
private utils = inject(UtilsService);
|
|
1522
|
+
|
|
1523
|
+
// Memoization cache
|
|
1524
|
+
private stringToJsonCache = new Map<string, any>();
|
|
1525
|
+
private objectComparisonCache = new Map<string, boolean>();
|
|
1526
|
+
|
|
1527
|
+
memoizedStringToJSON(value: string): any {
|
|
1528
|
+
if (this.stringToJsonCache.has(value)) {
|
|
1529
|
+
return this.stringToJsonCache.get(value);
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
const result = this.utils.stringToJSON(value);
|
|
1533
|
+
this.stringToJsonCache.set(value, result);
|
|
1534
|
+
return result;
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
memoizedObjectsEqual(x: any, y: any): boolean {
|
|
1538
|
+
const key = JSON.stringify([x, y]);
|
|
1539
|
+
|
|
1540
|
+
if (this.objectComparisonCache.has(key)) {
|
|
1541
|
+
return this.objectComparisonCache.get(key);
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
const result = this.utils.objectsEqual(x, y);
|
|
1545
|
+
this.objectComparisonCache.set(key, result);
|
|
1546
|
+
return result;
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
clearCache(): void {
|
|
1550
|
+
this.stringToJsonCache.clear();
|
|
1551
|
+
this.objectComparisonCache.clear();
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
```
|
|
1555
|
+
|
|
1556
|
+
## Troubleshooting
|
|
1557
|
+
|
|
1558
|
+
### Common Issues
|
|
1559
|
+
|
|
1560
|
+
#### 1. JSON Parsing Errors
|
|
1561
|
+
```typescript
|
|
1562
|
+
// ✅ Good - Safe JSON parsing
|
|
1563
|
+
safeJsonParse(jsonString: string, fallback: any = null): any {
|
|
1564
|
+
try {
|
|
1565
|
+
return this.utils.stringToJSON(jsonString);
|
|
1566
|
+
} catch (error) {
|
|
1567
|
+
console.warn('Invalid JSON:', jsonString, error);
|
|
1568
|
+
return fallback;
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
// ❌ Avoid - Unsafe parsing
|
|
1573
|
+
const data = JSON.parse(jsonString); // Could throw
|
|
1574
|
+
```
|
|
1575
|
+
|
|
1576
|
+
#### 2. Type Checking Edge Cases
|
|
1577
|
+
```typescript
|
|
1578
|
+
// ✅ Good - Handle edge cases
|
|
1579
|
+
isValidString(value: any): boolean {
|
|
1580
|
+
return this.utils.isString(value) && value.trim().length > 0;
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
// ❌ Avoid - Incomplete validation
|
|
1584
|
+
const isString = this.utils.isString(value); // Could be empty string
|
|
1585
|
+
```
|
|
1586
|
+
|
|
1587
|
+
#### 3. Expiration Calculation
|
|
1588
|
+
```typescript
|
|
1589
|
+
// ✅ Good - Handle invalid durations
|
|
1590
|
+
safeExpires(duration: string, fallback: number): number {
|
|
1591
|
+
const expires = this.utils.expires(duration);
|
|
1592
|
+
return expires || fallback;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
// ❌ Avoid - No fallback
|
|
1596
|
+
const expires = this.utils.expires(duration); // Could be undefined
|
|
1597
|
+
```
|
|
1598
|
+
|
|
1599
|
+
## Related Documentation
|
|
1600
|
+
|
|
1601
|
+
- [Local Storage Service](local-storage/README.md)
|
|
1602
|
+
- [HTTP State Service](http-state/README.md)
|
|
1603
|
+
- [Database Manager Service](database/README.md)
|
|
1604
|
+
- [Architecture Overview](../architecture/README.md)
|