http-request-manager 18.15.33 → 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/TEST_COVERAGE_SUMMARY.md +458 -0
- package/ng-package.json +8 -0
- package/package.json +4 -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 -13688
- package/fesm2022/http-request-manager.mjs.map +0 -1
- package/http-request-manager-18.15.33.tgz +0 -0
- package/types/http-request-manager.d.ts +0 -3968
|
@@ -0,0 +1,1391 @@
|
|
|
1
|
+
# HTTP Manager State Service
|
|
2
|
+
|
|
3
|
+
The `HTTPManagerStateService` is a comprehensive state management service that combines HTTP requests, WebSocket real-time updates, and IndexedDB persistence.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **HTTP CRUD Operations**: Fetch, create, update, and delete records
|
|
8
|
+
- **WebSocket Integration**: Real-time synchronization across clients
|
|
9
|
+
- **IndexedDB Persistence**: Cache data locally with expiration
|
|
10
|
+
- **State Management**: Built on NgRx ComponentStore
|
|
11
|
+
- **Automatic Retry**: Configurable retry logic for failed requests
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
### Basic Setup
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { HTTPManagerStateService, ApiRequest, DataType, DatabaseStorage } from 'http-request-manager';
|
|
19
|
+
|
|
20
|
+
@Injectable({
|
|
21
|
+
providedIn: 'root'
|
|
22
|
+
})
|
|
23
|
+
export class MyStateService extends HTTPManagerStateService<MyDataType> {
|
|
24
|
+
|
|
25
|
+
constructor() {
|
|
26
|
+
super(
|
|
27
|
+
ApiRequest.adapt({
|
|
28
|
+
server: 'https://api.example.com',
|
|
29
|
+
path: ['items'],
|
|
30
|
+
adapter: MyDataType.adapt // Model adapter
|
|
31
|
+
}),
|
|
32
|
+
DataType.ARRAY,
|
|
33
|
+
DatabaseStorage.adapt({
|
|
34
|
+
table: 'my_items',
|
|
35
|
+
expiresIn: '1h' // Cache expiration
|
|
36
|
+
})
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Database Operations
|
|
43
|
+
|
|
44
|
+
#### Clear Database Table
|
|
45
|
+
|
|
46
|
+
Clears all records from the configured IndexedDB table and resets in-memory state.
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
// Clear the database (subscription handled internally)
|
|
50
|
+
myStateService.clearDatabase();
|
|
51
|
+
|
|
52
|
+
// This will:
|
|
53
|
+
// 1. Clear all records from the IndexedDB table
|
|
54
|
+
// 2. Clear the in-memory state (set to empty array or object)
|
|
55
|
+
// 3. Log success/error messages to console
|
|
56
|
+
// 4. NOT re-fetch from API - data remains empty until manually refreshed
|
|
57
|
+
// 5. NOT clear localStorage metadata (expiration info is preserved)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Example in Component:**
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
@Component({
|
|
64
|
+
selector: 'app-my-component',
|
|
65
|
+
template: `
|
|
66
|
+
<button (click)="onClearDatabase()">Clear Cache</button>
|
|
67
|
+
`
|
|
68
|
+
})
|
|
69
|
+
export class MyComponent {
|
|
70
|
+
myStateService = inject(MyStateService);
|
|
71
|
+
|
|
72
|
+
onClearDatabase() {
|
|
73
|
+
// Simple call - no subscription needed
|
|
74
|
+
this.myStateService.clearDatabase();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Console Output:**
|
|
80
|
+
```
|
|
81
|
+
🗑️ Clearing database table: my_items
|
|
82
|
+
clearTable: Starting clear for table: my_items
|
|
83
|
+
clearTable: Clearing table 'my_items'...
|
|
84
|
+
clearTable: ✅ Table 'my_items' cleared successfully
|
|
85
|
+
✅ Database table my_items cleared successfully
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### CRUD Operations
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
// Fetch all records
|
|
92
|
+
service.fetchRecords();
|
|
93
|
+
|
|
94
|
+
// Fetch single record
|
|
95
|
+
service.fetchRecord({ path: ['items', 123] }, 'GET');
|
|
96
|
+
|
|
97
|
+
// Create record
|
|
98
|
+
service.createRecord(newData, { path: ['items'] });
|
|
99
|
+
|
|
100
|
+
// Update record
|
|
101
|
+
service.updateRecord(updatedData, { path: ['items', 123] });
|
|
102
|
+
|
|
103
|
+
// Delete record
|
|
104
|
+
service.deleteRecord({ path: ['items', 123] });
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Request Tracking Options (Database Only)
|
|
108
|
+
|
|
109
|
+
The state manager supports query-tracking options on `fetchRecords` and `fetchStream`.
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
service.fetchRecords(RequestOptions.adapt({
|
|
113
|
+
path: ['ai/pagination?page=0&size=25'],
|
|
114
|
+
ignoreQueryParams: ['page', 'size'],
|
|
115
|
+
queryParamsExpiresIn: '10s',
|
|
116
|
+
watchParams: ['category', 'status'],
|
|
117
|
+
watchExpiresAt: 1715904000
|
|
118
|
+
}));
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Options:
|
|
122
|
+
|
|
123
|
+
- `ignoreQueryParams`: Query parameter keys to exclude from request-change tracking.
|
|
124
|
+
- `queryParamsExpiresIn`: Expiry duration for tracked values (e.g., `'10s'`, `'5mn'`, `'1h'`).
|
|
125
|
+
- `watchParams`: Specific query parameter keys to watch for change detection in variation mode. When set, only these params are tracked; others are ignored.
|
|
126
|
+
- `watchExpiresAt`: Epoch timestamp (in seconds) when tracked values expire. Alternative to `queryParamsExpiresIn` for absolute expiry.
|
|
127
|
+
|
|
128
|
+
Scope and behavior:
|
|
129
|
+
|
|
130
|
+
- Database enabled (`DatabaseStorage` configured): tracker is active and can prevent repeated identical API calls until expiry resets tracked values.
|
|
131
|
+
- Database disabled: tracker is not enabled; requests go straight to API.
|
|
132
|
+
- `forceRefresh: true` bypasses tracking and always makes the API request.
|
|
133
|
+
|
|
134
|
+
### WebSocket Integration
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// Automatic real-time sync when WebSocket is configured
|
|
138
|
+
ApiRequest.adapt({
|
|
139
|
+
server: 'https://api.example.com',
|
|
140
|
+
ws: {
|
|
141
|
+
wsServer: 'wss://ws.example.com',
|
|
142
|
+
id: 'my-channel',
|
|
143
|
+
jwtToken: 'your-jwt-token',
|
|
144
|
+
retry: { times: 3, delay: 5 }
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Configuration Options
|
|
150
|
+
|
|
151
|
+
### ApiRequest
|
|
152
|
+
- `server`: Backend API server URL
|
|
153
|
+
- `path`: Array of path segments
|
|
154
|
+
- `adapter`: Function to transform API response to your model
|
|
155
|
+
- `ws`: WebSocket configuration
|
|
156
|
+
- `retry`: Retry options for failed requests
|
|
157
|
+
|
|
158
|
+
### DatabaseStorage
|
|
159
|
+
- `table`: IndexedDB table name
|
|
160
|
+
- `expiresIn`: Cache expiration (e.g., '1h', '30mn', '5mn')
|
|
161
|
+
|
|
162
|
+
### DataType
|
|
163
|
+
- `ARRAY`: For list data (default)
|
|
164
|
+
- `OBJECT`: For single object data
|
|
165
|
+
|
|
166
|
+
## Notes
|
|
167
|
+
|
|
168
|
+
- The `clearDatabase()` method handles subscription internally for simplicity
|
|
169
|
+
- Database operations are asynchronous and logged to console
|
|
170
|
+
- Clearing the database does NOT trigger a refresh - call `fetchRecords()` after clearing if needed
|
|
171
|
+
|
|
172
|
+
The `HTTPManagerStateService` is a comprehensive state management service that combines HTTP requests, WebSocket real-time updates, and IndexedDB persistence.
|
|
173
|
+
|
|
174
|
+
## Features
|
|
175
|
+
|
|
176
|
+
- **HTTP CRUD Operations**: Fetch, create, update, and delete records
|
|
177
|
+
- **WebSocket Integration**: Real-time synchronization across clients
|
|
178
|
+
- **IndexedDB Persistence**: Cache data locally with expiration
|
|
179
|
+
- **State Management**: Built on NgRx ComponentStore
|
|
180
|
+
- **Automatic Retry**: Configurable retry logic for failed requests
|
|
181
|
+
|
|
182
|
+
## Usage
|
|
183
|
+
|
|
184
|
+
### Basic Setup
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
import { HTTPManagerStateService, ApiRequest, DataType, DatabaseStorage } from 'http-request-manager';
|
|
188
|
+
|
|
189
|
+
@Injectable({
|
|
190
|
+
providedIn: 'root'
|
|
191
|
+
})
|
|
192
|
+
export class MyStateService extends HTTPManagerStateService<MyDataType> {
|
|
193
|
+
|
|
194
|
+
constructor() {
|
|
195
|
+
super(
|
|
196
|
+
ApiRequest.adapt({
|
|
197
|
+
server: 'https://api.example.com',
|
|
198
|
+
path: ['items'],
|
|
199
|
+
adapter: MyDataType.adapt // Model adapter
|
|
200
|
+
}),
|
|
201
|
+
DataType.ARRAY,
|
|
202
|
+
DatabaseStorage.adapt({
|
|
203
|
+
table: 'my_items',
|
|
204
|
+
expiresIn: '1h' // Cache expiration
|
|
205
|
+
})
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Database Operations
|
|
212
|
+
|
|
213
|
+
#### Clear Database Table
|
|
214
|
+
|
|
215
|
+
Clears all records from the configured IndexedDB table and resets in-memory state.
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
// Clear the database (subscription handled internally)
|
|
219
|
+
myStateService.clearDatabase();
|
|
220
|
+
|
|
221
|
+
// This will:
|
|
222
|
+
// 1. Clear all records from the IndexedDB table
|
|
223
|
+
// 2. Clear the in-memory state (set to empty array or object)
|
|
224
|
+
// 3. Log success/error messages to console
|
|
225
|
+
// 4. NOT re-fetch from API - data remains empty until manually refreshed
|
|
226
|
+
// 5. NOT clear localStorage metadata (expiration info is preserved)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**Example in Component:**
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
@Component({
|
|
233
|
+
selector: 'app-my-component',
|
|
234
|
+
template: `
|
|
235
|
+
<button (click)="onClearDatabase()">Clear Cache</button>
|
|
236
|
+
`
|
|
237
|
+
})
|
|
238
|
+
export class MyComponent {
|
|
239
|
+
myStateService = inject(MyStateService);
|
|
240
|
+
|
|
241
|
+
onClearDatabase() {
|
|
242
|
+
// Simple call - no subscription needed
|
|
243
|
+
this.myStateService.clearDatabase();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**Console Output:**
|
|
249
|
+
```
|
|
250
|
+
🗑️ Clearing database table: my_items
|
|
251
|
+
clearTable: Starting clear for table: my_items
|
|
252
|
+
clearTable: Clearing table 'my_items'...
|
|
253
|
+
clearTable: ✅ Table 'my_items' cleared successfully
|
|
254
|
+
✅ Database table my_items cleared successfully
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### CRUD Operations
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
// Fetch all records
|
|
261
|
+
service.fetchRecords();
|
|
262
|
+
|
|
263
|
+
// Fetch single record
|
|
264
|
+
service.fetchRecord({ path: ['items', 123] }, 'GET');
|
|
265
|
+
|
|
266
|
+
// Create record
|
|
267
|
+
service.createRecord(newData, { path: ['items'] });
|
|
268
|
+
|
|
269
|
+
// Update record
|
|
270
|
+
service.updateRecord(updatedData, { path: ['items', 123] });
|
|
271
|
+
|
|
272
|
+
// Delete record
|
|
273
|
+
service.deleteRecord({ path: ['items', 123] });
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### WebSocket Integration
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
// Automatic real-time sync when WebSocket is configured
|
|
280
|
+
ApiRequest.adapt({
|
|
281
|
+
server: 'https://api.example.com',
|
|
282
|
+
ws: {
|
|
283
|
+
wsServer: 'wss://ws.example.com',
|
|
284
|
+
id: 'my-channel',
|
|
285
|
+
jwtToken: 'your-jwt-token',
|
|
286
|
+
retry: { times: 3, delay: 5 }
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Configuration Options
|
|
292
|
+
|
|
293
|
+
### ApiRequest
|
|
294
|
+
- `server`: Backend API server URL
|
|
295
|
+
- `path`: Array of path segments
|
|
296
|
+
- `adapter`: Function to transform API response to your model
|
|
297
|
+
- `ws`: WebSocket configuration
|
|
298
|
+
- `retry`: Retry options for failed requests
|
|
299
|
+
|
|
300
|
+
### DatabaseStorage
|
|
301
|
+
- `table`: IndexedDB table name
|
|
302
|
+
- `expiresIn`: Cache expiration (e.g., '1h', '30mn', '5mn')
|
|
303
|
+
|
|
304
|
+
### DataType
|
|
305
|
+
- `ARRAY`: For list data (default)
|
|
306
|
+
- `OBJECT`: For single object data
|
|
307
|
+
|
|
308
|
+
## Notes
|
|
309
|
+
|
|
310
|
+
- The `clearDatabase()` method handles subscription internally for simplicity
|
|
311
|
+
- Database operations are asynchronous and logged to console
|
|
312
|
+
- Clearing the database does NOT trigger a refresh - call `fetchRecords()` after clearing if needed
|
|
313
|
+
|
|
314
|
+
The `HTTPManagerStateService` is a comprehensive state management solution that extends NGRX `ComponentStore` to provide unified HTTP request handling, WebSocket real-time synchronization, and offline-first data persistence.
|
|
315
|
+
|
|
316
|
+
## Overview
|
|
317
|
+
|
|
318
|
+
This service serves as the central hub for managing application state, combining:
|
|
319
|
+
- **HTTP CRUD operations** with automatic local state updates
|
|
320
|
+
- **WebSocket integration** for real-time multi-user synchronization
|
|
321
|
+
- **IndexedDB persistence** for offline-first applications
|
|
322
|
+
- **Pagination and streaming** support out of the box
|
|
323
|
+
- **Message tracking** with guaranteed delivery
|
|
324
|
+
|
|
325
|
+
### Architecture
|
|
326
|
+
|
|
327
|
+
```
|
|
328
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
329
|
+
│ HTTPManagerStateService │
|
|
330
|
+
│ (ComponentStore) │
|
|
331
|
+
├─────────────────────────────────────────────────────────────┤
|
|
332
|
+
│ │
|
|
333
|
+
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|
334
|
+
│ │ HTTP │ │ WebSocket │ │ Database │ │
|
|
335
|
+
│ │ Manager │◀─ Manager │◀─┤ Manager │ │
|
|
336
|
+
│ │ Service │ │ (Singleton) │ │ Service │ │
|
|
337
|
+
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
|
338
|
+
│ │ │ │ │
|
|
339
|
+
│ ▼ ▼ ▼ │
|
|
340
|
+
│ ┌─────────────────────────────────────────────────────┐ │
|
|
341
|
+
│ │ Local State (ComponentStore) │ │
|
|
342
|
+
│ │ - data$ (records) │ │
|
|
343
|
+
│ │ - page$, totalPages$, percentage$ │ │
|
|
344
|
+
│ │ - isPending$, error$ │ │
|
|
345
|
+
│ │ - userList$, channels$, messages$ │ │
|
|
346
|
+
│ └─────────────────────────────────────────────────────┘ │
|
|
347
|
+
└─────────────────────────────────────────────────────────────┘
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### Key Features
|
|
351
|
+
|
|
352
|
+
- **ComponentStore Integration** - Extends NGRX ComponentStore for localized, performant state management
|
|
353
|
+
- **Automatic CRUD Updates** - `createRecord`, `updateRecord`, and `deleteRecord` automatically update local state and sync via WebSocket
|
|
354
|
+
- **WebSocket Real-Time Sync** - Multi-user synchronization with channel-based messaging and user presence tracking
|
|
355
|
+
- **Guaranteed Message Delivery** - MessageTrackerService ensures no messages are missed during disconnections
|
|
356
|
+
- **Database Support** - IndexedDB caching integration for offline-first applications with automatic sync
|
|
357
|
+
- **Pagination State** - Built-in tracking for page, totalPages, and loading percentage
|
|
358
|
+
- **Streaming Support** - Real-time data streaming with AI streaming patterns
|
|
359
|
+
- **Notification System** - Dedicated notification channels with date filtering and latest notification tracking
|
|
360
|
+
- **User Presence** - Track active users per channel and across all channels
|
|
361
|
+
|
|
362
|
+
## Installation
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
import { HttpRequestManagerModule } from 'http-request-manager';
|
|
366
|
+
|
|
367
|
+
@NgModule({
|
|
368
|
+
imports: [HttpRequestManagerModule.forRoot({})]
|
|
369
|
+
})
|
|
370
|
+
export class AppModule { }
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
## Basic Usage
|
|
374
|
+
|
|
375
|
+
### Creating a State Service
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
import { Injectable } from '@angular/core';
|
|
379
|
+
import { HTTPManagerStateService, ApiRequest, DataType } from 'http-request-manager';
|
|
380
|
+
|
|
381
|
+
interface User {
|
|
382
|
+
id: number;
|
|
383
|
+
name: string;
|
|
384
|
+
email: string;
|
|
385
|
+
active: boolean;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
@Injectable({
|
|
389
|
+
providedIn: 'root'
|
|
390
|
+
})
|
|
391
|
+
export class UserStateService extends HTTPManagerStateService<User> {
|
|
392
|
+
|
|
393
|
+
constructor() {
|
|
394
|
+
super(
|
|
395
|
+
ApiRequest.adapt({
|
|
396
|
+
server: 'http://localhost:8080',
|
|
397
|
+
path: ['users'],
|
|
398
|
+
adapter: User.adapt, // Required for Database Storage
|
|
399
|
+
ws: {
|
|
400
|
+
id: 'USERS123', // Base name - automatically becomes SYS-USERS123
|
|
401
|
+
wsServer: 'ws://localhost:8080',
|
|
402
|
+
retry: { times: 3, delay: 5 }
|
|
403
|
+
}
|
|
404
|
+
}),
|
|
405
|
+
DataType.ARRAY,
|
|
406
|
+
DatabaseStorage.adapt({
|
|
407
|
+
table: 'users-cache',
|
|
408
|
+
expiresIn: '1d' // Cache for 1 day
|
|
409
|
+
})
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Public API methods
|
|
414
|
+
loadUsers() {
|
|
415
|
+
this.fetchRecords();
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
addUser(user: Omit<User, 'id'>) {
|
|
419
|
+
this.createRecord(user);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
updateUser(user: User) {
|
|
423
|
+
this.updateRecord(user);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
deleteUser(id: number) {
|
|
427
|
+
this.deleteRecord(id);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
getUser(id: number) {
|
|
431
|
+
return this.selectRecord$(id);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### Using in Components
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
import { Component, inject } from '@angular/core';
|
|
440
|
+
import { UserStateService } from './user-state.service';
|
|
441
|
+
|
|
442
|
+
@Component({
|
|
443
|
+
selector: 'app-users',
|
|
444
|
+
template: `
|
|
445
|
+
<div class="user-management">
|
|
446
|
+
<h2>User Management</h2>
|
|
447
|
+
|
|
448
|
+
<!-- Loading State -->
|
|
449
|
+
<div *ngIf="store.isPending$ | async">
|
|
450
|
+
<div class="loading-spinner">Loading users...</div>
|
|
451
|
+
</div>
|
|
452
|
+
|
|
453
|
+
<!-- Error State -->
|
|
454
|
+
<div *ngIf="store.error$ | async as error" class="error">
|
|
455
|
+
Error: {{ error.message }}
|
|
456
|
+
</div>
|
|
457
|
+
|
|
458
|
+
<!-- User List -->
|
|
459
|
+
<div class="user-grid">
|
|
460
|
+
<div *ngFor="let user of store.data$ | async" class="user-card">
|
|
461
|
+
<div class="user-info">
|
|
462
|
+
<h3>{{ user.name }}</h3>
|
|
463
|
+
<p>{{ user.email }}</p>
|
|
464
|
+
<span class="user-status"
|
|
465
|
+
[class.active]="user.active"
|
|
466
|
+
[class.inactive]="!user.active">
|
|
467
|
+
{{ user.active ? 'Active' : 'Inactive' }}
|
|
468
|
+
</span>
|
|
469
|
+
</div>
|
|
470
|
+
|
|
471
|
+
<div class="user-actions">
|
|
472
|
+
<button (click)="store.updateUser({...user, active: !user.active})">
|
|
473
|
+
{{ user.active ? 'Deactivate' : 'Activate' }}
|
|
474
|
+
</button>
|
|
475
|
+
<button (click)="store.deleteUser(user.id)" class="delete">
|
|
476
|
+
Delete
|
|
477
|
+
</button>
|
|
478
|
+
</div>
|
|
479
|
+
</div>
|
|
480
|
+
</div>
|
|
481
|
+
|
|
482
|
+
<!-- Add User Form -->
|
|
483
|
+
<div class="add-user-form">
|
|
484
|
+
<h3>Add New User</h3>
|
|
485
|
+
<form (ngSubmit)="addUser()">
|
|
486
|
+
<input [(ngModel)]="newUser.name" name="name" placeholder="Name" required>
|
|
487
|
+
<input [(ngModel)]="newUser.email" name="email" placeholder="Email" required>
|
|
488
|
+
<label>
|
|
489
|
+
<input type="checkbox" [(ngModel)]="newUser.active" name="active">
|
|
490
|
+
Active
|
|
491
|
+
</label>
|
|
492
|
+
<button type="submit">Add User</button>
|
|
493
|
+
</form>
|
|
494
|
+
</div>
|
|
495
|
+
</div>
|
|
496
|
+
`
|
|
497
|
+
})
|
|
498
|
+
export class UsersComponent {
|
|
499
|
+
store = inject(UserStateService);
|
|
500
|
+
|
|
501
|
+
newUser = {
|
|
502
|
+
name: '',
|
|
503
|
+
email: '',
|
|
504
|
+
active: true
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
ngOnInit() {
|
|
508
|
+
this.store.loadUsers();
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
addUser() {
|
|
512
|
+
if (this.newUser.name && this.newUser.email) {
|
|
513
|
+
this.store.addUser(this.newUser);
|
|
514
|
+
this.newUser = { name: '', email: '', active: true };
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
## API Reference
|
|
521
|
+
|
|
522
|
+
### State Interface
|
|
523
|
+
|
|
524
|
+
The service manages state with the following interface:
|
|
525
|
+
|
|
526
|
+
```typescript
|
|
527
|
+
interface State<T> {
|
|
528
|
+
records: T[]; // Array of entities
|
|
529
|
+
record: T | null; // Single selected entity
|
|
530
|
+
page: number; // Current page
|
|
531
|
+
totalPages: number; // Total pages
|
|
532
|
+
isPending: boolean; // Loading state
|
|
533
|
+
error: any; // Error state
|
|
534
|
+
}
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
### WSOptions Model (Updated)
|
|
538
|
+
|
|
539
|
+
The `WSOptions` model has been updated with the following changes:
|
|
540
|
+
|
|
541
|
+
```typescript
|
|
542
|
+
interface WSOptionsInterface {
|
|
543
|
+
id: string,
|
|
544
|
+
wsServer: string,
|
|
545
|
+
jwtToken: string,
|
|
546
|
+
permissions?: string[],
|
|
547
|
+
channels?: string[], // ✅ Available
|
|
548
|
+
user?: any,
|
|
549
|
+
retry?: RetryOptions,
|
|
550
|
+
// wsUpdateChannels?: string[] ❌ Removed
|
|
551
|
+
}
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
**Breaking Changes:**
|
|
555
|
+
- `wsUpdateChannels` property has been **removed** from `WSOptionsInterface` and `WSOptions` class
|
|
556
|
+
- Channel management is now handled through the `channels` array and automatic detection
|
|
557
|
+
- Legacy message format support is maintained for backward compatibility
|
|
558
|
+
|
|
559
|
+
### Selectors
|
|
560
|
+
|
|
561
|
+
#### data$: Observable<T[]>
|
|
562
|
+
|
|
563
|
+
Get all records from the state:
|
|
564
|
+
|
|
565
|
+
```typescript
|
|
566
|
+
users$ = this.store.data$;
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
#### record$: Observable<T | null>
|
|
570
|
+
|
|
571
|
+
Get the currently selected record:
|
|
572
|
+
|
|
573
|
+
```typescript
|
|
574
|
+
selectedUser$ = this.store.record$;
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
#### selectRecord$(id: any): Observable<T | undefined>
|
|
578
|
+
|
|
579
|
+
Get a specific record by ID:
|
|
580
|
+
|
|
581
|
+
```typescript
|
|
582
|
+
user$ = this.store.selectRecord$(123);
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
#### isPending$: Observable<boolean>
|
|
586
|
+
|
|
587
|
+
Check if any operation is in progress:
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
loading$ = this.store.isPending$;
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
#### error$: Observable<any>
|
|
594
|
+
|
|
595
|
+
Get the current error state:
|
|
596
|
+
|
|
597
|
+
```typescript
|
|
598
|
+
error$ = this.store.error$;
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
#### operationSuccess$: Observable\<OperationResultModel | null\>
|
|
602
|
+
|
|
603
|
+
Get feedback after `createRecord`, `updateRecord`, or `deleteRecord` operations. Emits `{ success: true, operation: 'CREATE' | 'UPDATE' | 'DELETE' }` when data is successfully pushed into state.
|
|
604
|
+
|
|
605
|
+
```typescript
|
|
606
|
+
this.store.operationSuccess$.subscribe(result => {
|
|
607
|
+
if (result?.success) {
|
|
608
|
+
// result.operation tells you which CRUD action completed
|
|
609
|
+
showSuccessToast(`${result.operation} succeeded!`);
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
**Example in Component:**
|
|
615
|
+
|
|
616
|
+
```typescript
|
|
617
|
+
@Component({
|
|
618
|
+
selector: 'app-users',
|
|
619
|
+
template: `
|
|
620
|
+
<button (click)="store.deleteUser(user.id)">Delete</button>
|
|
621
|
+
<div *ngIf="lastOperation" class="toast">
|
|
622
|
+
{{ lastOperation.operation }} {{ lastOperation.success ? 'succeeded' : 'failed' }}
|
|
623
|
+
</div>
|
|
624
|
+
`
|
|
625
|
+
})
|
|
626
|
+
export class UsersComponent {
|
|
627
|
+
store = inject(UserStateService);
|
|
628
|
+
lastOperation: OperationResultModel | null = null;
|
|
629
|
+
|
|
630
|
+
constructor() {
|
|
631
|
+
this.store.operationSuccess$.subscribe(result => {
|
|
632
|
+
this.lastOperation = result;
|
|
633
|
+
// Auto-clear after 3 seconds
|
|
634
|
+
setTimeout(() => this.lastOperation = null, 3000);
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
> **Note:** `operationSuccess$` only emits on successful HTTP responses that update local state. It does not emit on errors — use `error$` for error handling.
|
|
641
|
+
|
|
642
|
+
#### page$: Observable<number>
|
|
643
|
+
|
|
644
|
+
Get the current page number:
|
|
645
|
+
|
|
646
|
+
```typescript
|
|
647
|
+
currentPage$ = this.store.page$;
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
#### totalPages$: Observable<number>
|
|
651
|
+
|
|
652
|
+
Get the total number of pages:
|
|
653
|
+
|
|
654
|
+
```typescript
|
|
655
|
+
totalPages$ = this.store.totalPages$;
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
### CRUD Methods
|
|
659
|
+
|
|
660
|
+
#### fetchRecords(params?: any)
|
|
661
|
+
|
|
662
|
+
Fetch records from the API:
|
|
663
|
+
|
|
664
|
+
```typescript
|
|
665
|
+
// Basic fetch
|
|
666
|
+
this.store.fetchRecords();
|
|
667
|
+
|
|
668
|
+
// Fetch with parameters
|
|
669
|
+
this.store.fetchRecords({ page: 1, limit: 10, search: 'john' });
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
#### createRecord(record: T)
|
|
673
|
+
|
|
674
|
+
Create a new record and update state:
|
|
675
|
+
|
|
676
|
+
```typescript
|
|
677
|
+
this.store.createRecord({
|
|
678
|
+
name: 'John Doe',
|
|
679
|
+
email: 'john@example.com',
|
|
680
|
+
active: true
|
|
681
|
+
});
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
#### updateRecord(record: T)
|
|
685
|
+
|
|
686
|
+
Update an existing record and update state:
|
|
687
|
+
|
|
688
|
+
```typescript
|
|
689
|
+
this.store.updateRecord({
|
|
690
|
+
id: 123,
|
|
691
|
+
name: 'John Smith',
|
|
692
|
+
email: 'john.smith@example.com',
|
|
693
|
+
active: true
|
|
694
|
+
});
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
#### deleteRecord(id: any)
|
|
698
|
+
|
|
699
|
+
Delete a record and update state:
|
|
700
|
+
|
|
701
|
+
```typescript
|
|
702
|
+
this.store.deleteRecord(123);
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
#### setPage(page: number)
|
|
706
|
+
|
|
707
|
+
Set the current page:
|
|
708
|
+
|
|
709
|
+
```typescript
|
|
710
|
+
this.store.setPage(2);
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
#### setTotalPages(total: number)
|
|
714
|
+
|
|
715
|
+
Set the total number of pages:
|
|
716
|
+
|
|
717
|
+
```typescript
|
|
718
|
+
this.store.setTotalPages(10);
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
## WebSocket Integration
|
|
722
|
+
|
|
723
|
+
The HTTPManagerStateService automatically handles WebSocket connections for real-time state synchronization.
|
|
724
|
+
|
|
725
|
+
### Channel Types
|
|
726
|
+
|
|
727
|
+
The service uses three types of WebSocket channels:
|
|
728
|
+
|
|
729
|
+
| Channel Type | Prefix | Description | Purpose |
|
|
730
|
+
|--------------|--------|-------------|---------|
|
|
731
|
+
| **STATE** | `SYS-` | State synchronization | CRUD operations sync |
|
|
732
|
+
| **MESSAGE** | `MES-` | Messaging channels | Real-time messaging |
|
|
733
|
+
| **NOTIFICATION** | `PUB-` | Notification channels | System notifications |
|
|
734
|
+
|
|
735
|
+
### Channel Management (Updated)
|
|
736
|
+
|
|
737
|
+
> **Breaking Change:** The `wsUpdateChannels` property has been removed from `WSOptionsInterface` and `WSOptions` class.
|
|
738
|
+
|
|
739
|
+
Channel management has been improved and simplified:
|
|
740
|
+
|
|
741
|
+
1. **Automatic Channel Detection**: The service now automatically constructs the expected channel name from the API path and environment configuration
|
|
742
|
+
2. **Dynamic Channel Matching**: Messages are matched against expected channels based on the pattern: `${env}/${path.join('/')}`
|
|
743
|
+
3. **Fallback Support**: Legacy message formats are still supported for backward compatibility
|
|
744
|
+
|
|
745
|
+
```typescript
|
|
746
|
+
// Before (deprecated)
|
|
747
|
+
ws: {
|
|
748
|
+
id: 'USERS123',
|
|
749
|
+
wsUpdateChannels: ['MES-users', 'MES-updates'], // ❌ No longer supported
|
|
750
|
+
wsServer: 'ws://localhost:8080'
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// After (current)
|
|
754
|
+
ws: {
|
|
755
|
+
id: 'USERS123',
|
|
756
|
+
channels: ['custom-channel-1', 'custom-channel-2'], // ✅ Use channels array
|
|
757
|
+
wsServer: 'ws://localhost:8080'
|
|
758
|
+
}
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
### Configuration
|
|
762
|
+
|
|
763
|
+
```typescript
|
|
764
|
+
constructor() {
|
|
765
|
+
super(
|
|
766
|
+
ApiRequest.adapt({
|
|
767
|
+
server: 'http://localhost:8080',
|
|
768
|
+
path: ['users'],
|
|
769
|
+
ws: {
|
|
770
|
+
id: 'USERS123', // Base name becomes SYS-USERS123
|
|
771
|
+
wsServer: 'ws://localhost:8080',
|
|
772
|
+
jwtToken: 'your-jwt-token',
|
|
773
|
+
retry: { times: 3, delay: 5 }
|
|
774
|
+
}
|
|
775
|
+
}),
|
|
776
|
+
DataType.ARRAY
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
> **Note:** The `wsUpdateChannels` property has been removed from `WSOptions`. WebSocket channel management is now handled dynamically through the `channels` property and automatic channel detection based on the API path and environment configuration.
|
|
782
|
+
|
|
783
|
+
### WebSocket Observables
|
|
784
|
+
|
|
785
|
+
#### connectionStatus$: Observable<boolean>
|
|
786
|
+
|
|
787
|
+
Monitor WebSocket connection status:
|
|
788
|
+
|
|
789
|
+
```typescript
|
|
790
|
+
connectionStatus$ = this.store.connectionStatus$;
|
|
791
|
+
|
|
792
|
+
// Usage
|
|
793
|
+
<div *ngIf="store.connectionStatus$ | async; disconnected" class="connection-status">
|
|
794
|
+
Connected
|
|
795
|
+
</div>
|
|
796
|
+
|
|
797
|
+
<ng-template #disconnected>
|
|
798
|
+
<div class="connection-status offline">
|
|
799
|
+
Reconnecting...
|
|
800
|
+
</div>
|
|
801
|
+
</ng-template>
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
#### communicationMessages$: Observable<any[]>
|
|
805
|
+
|
|
806
|
+
Monitor incoming messages:
|
|
807
|
+
|
|
808
|
+
```typescript
|
|
809
|
+
messages$ = this.store.communicationMessages$;
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
#### notificationMessages$: Observable<any[]>
|
|
813
|
+
|
|
814
|
+
Monitor notifications:
|
|
815
|
+
|
|
816
|
+
```typescript
|
|
817
|
+
notifications$ = this.store.notificationMessages$;
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
### WebSocket Methods
|
|
821
|
+
|
|
822
|
+
#### initializeConnection(wsServer: string, jwtToken: string, user: any)
|
|
823
|
+
|
|
824
|
+
Initialize WebSocket connection:
|
|
825
|
+
|
|
826
|
+
```typescript
|
|
827
|
+
this.store.initializeConnection(
|
|
828
|
+
'ws://localhost:8080',
|
|
829
|
+
'jwt-token-here',
|
|
830
|
+
{ id: 1, name: 'John Doe', email: 'john@example.com' }
|
|
831
|
+
);
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
#### setApiRequestOptions(options: ApiRequest)
|
|
835
|
+
|
|
836
|
+
Update API and WebSocket configuration at runtime:
|
|
837
|
+
|
|
838
|
+
```typescript
|
|
839
|
+
this.store.setApiRequestOptions(
|
|
840
|
+
ApiRequest.adapt({
|
|
841
|
+
...this.store.apiRequestOptions,
|
|
842
|
+
ws: {
|
|
843
|
+
...this.store.apiRequestOptions.ws,
|
|
844
|
+
wsServer: 'wss://new-server.com/ws'
|
|
845
|
+
}
|
|
846
|
+
})
|
|
847
|
+
);
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
#### createChannel(channelName: string)
|
|
851
|
+
|
|
852
|
+
Create a new messaging channel:
|
|
853
|
+
|
|
854
|
+
```typescript
|
|
855
|
+
// Creates PUB-{channelName}
|
|
856
|
+
this.store.createChannel('general-chat');
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
#### subscribeToChannel(channelName: string)
|
|
860
|
+
|
|
861
|
+
Subscribe to a messaging channel:
|
|
862
|
+
|
|
863
|
+
```typescript
|
|
864
|
+
// Subscribes to PUB-{channelName}
|
|
865
|
+
this.store.subscribeToChannel('general-chat');
|
|
866
|
+
```
|
|
867
|
+
|
|
868
|
+
#### unsubscribeFromChannel(channelName: string)
|
|
869
|
+
|
|
870
|
+
Unsubscribe from a channel:
|
|
871
|
+
|
|
872
|
+
```typescript
|
|
873
|
+
this.store.unsubscribeFromChannel('general-chat');
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
#### sendMessage(content: any, channels: string[])
|
|
877
|
+
|
|
878
|
+
Send a message to specific channels:
|
|
879
|
+
|
|
880
|
+
```typescript
|
|
881
|
+
this.store.sendMessage(
|
|
882
|
+
{ message: 'Hello everyone!' },
|
|
883
|
+
['general-chat', 'team-updates']
|
|
884
|
+
);
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
**Implementation Note:** The service now uses optimized batch messaging with `sendChannelMessageToChannels()` for better performance when sending to multiple channels simultaneously.
|
|
888
|
+
|
|
889
|
+
### Real-time CRUD Operations
|
|
890
|
+
|
|
891
|
+
The service automatically syncs state changes via WebSocket:
|
|
892
|
+
|
|
893
|
+
```typescript
|
|
894
|
+
// When createRecord() is called:
|
|
895
|
+
// 1. Makes HTTP POST request
|
|
896
|
+
// 2. On success, updates local state
|
|
897
|
+
// 3. Sends WebSocket message to SYS-{id} channel
|
|
898
|
+
// 4. Other clients receive the message and update their state
|
|
899
|
+
|
|
900
|
+
// When other clients call createRecord:
|
|
901
|
+
// 1. They send WebSocket message to SYS-{id} channel
|
|
902
|
+
// 2. This client receives the message
|
|
903
|
+
// 3. Automatically calls fetchRecords() to get updated data
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
**Updated Message Handling:**
|
|
907
|
+
The service now implements enhanced message processing:
|
|
908
|
+
- **Automatic Channel Detection**: Messages are matched against expected channels using `${env}/${path.join('/')}` pattern
|
|
909
|
+
- **Optimized Batch Messaging**: Uses `sendChannelMessageToChannels()` for better performance
|
|
910
|
+
- **Legacy Format Support**: Maintains backward compatibility with older message formats
|
|
911
|
+
- **Enhanced Error Handling**: Improved error handling during service initialization
|
|
912
|
+
|
|
913
|
+
## Database Storage Integration
|
|
914
|
+
|
|
915
|
+
For offline-first applications, configure IndexedDB caching:
|
|
916
|
+
|
|
917
|
+
```typescript
|
|
918
|
+
@Injectable()
|
|
919
|
+
export class UserStateService extends HTTPManagerStateService<User> {
|
|
920
|
+
|
|
921
|
+
constructor() {
|
|
922
|
+
super(
|
|
923
|
+
ApiRequest.adapt({
|
|
924
|
+
server: 'http://localhost:8080',
|
|
925
|
+
path: ['users'],
|
|
926
|
+
adapter: User.adapt // Required for Database Storage
|
|
927
|
+
}),
|
|
928
|
+
DataType.ARRAY,
|
|
929
|
+
DatabaseStorage.adapt({
|
|
930
|
+
table: 'users-cache',
|
|
931
|
+
expiresIn: '1d' // Cache expires in 1 day
|
|
932
|
+
})
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
```
|
|
937
|
+
|
|
938
|
+
### Caching Behavior
|
|
939
|
+
|
|
940
|
+
1. **First Request**: Fetches from server and saves to IndexedDB
|
|
941
|
+
2. **Subsequent Requests**: Checks IndexedDB first, uses cached data if valid
|
|
942
|
+
3. **Cache Expiry**: Automatically fetches from server when cache expires
|
|
943
|
+
4. **Offline Mode**: Uses cached data when network is unavailable
|
|
944
|
+
|
|
945
|
+
## Advanced Examples
|
|
946
|
+
|
|
947
|
+
### Pagination State Service
|
|
948
|
+
|
|
949
|
+
```typescript
|
|
950
|
+
@Injectable()
|
|
951
|
+
export class PaginatedUserStateService extends HTTPManagerStateService<User> {
|
|
952
|
+
|
|
953
|
+
private currentPage = signal(1);
|
|
954
|
+
private pageSize = signal(10);
|
|
955
|
+
private searchQuery = signal('');
|
|
956
|
+
private sortBy = signal('name');
|
|
957
|
+
private sortOrder = signal<'asc' | 'desc'>('asc');
|
|
958
|
+
|
|
959
|
+
// Computed selectors
|
|
960
|
+
totalPages = computed(() => Math.ceil(this.store.data$().length / this.pageSize()));
|
|
961
|
+
|
|
962
|
+
paginatedData = computed(() => {
|
|
963
|
+
const data = this.store.data$();
|
|
964
|
+
const start = (this.currentPage() - 1) * this.pageSize();
|
|
965
|
+
const end = start + this.pageSize();
|
|
966
|
+
return data.slice(start, end);
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
constructor() {
|
|
970
|
+
super(
|
|
971
|
+
ApiRequest.adapt({
|
|
972
|
+
server: 'http://localhost:8080',
|
|
973
|
+
path: ['users'],
|
|
974
|
+
adapter: User.adapt
|
|
975
|
+
}),
|
|
976
|
+
DataType.ARRAY
|
|
977
|
+
);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
loadUsers() {
|
|
981
|
+
const params = {
|
|
982
|
+
page: this.currentPage(),
|
|
983
|
+
limit: this.pageSize(),
|
|
984
|
+
search: this.searchQuery(),
|
|
985
|
+
sortBy: this.sortBy(),
|
|
986
|
+
sortOrder: this.sortOrder()
|
|
987
|
+
};
|
|
988
|
+
|
|
989
|
+
this.fetchRecords(params);
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
setPage(page: number) {
|
|
993
|
+
this.currentPage.set(page);
|
|
994
|
+
this.loadUsers();
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
setPageSize(size: number) {
|
|
998
|
+
this.pageSize.set(size);
|
|
999
|
+
this.currentPage.set(1); // Reset to first page
|
|
1000
|
+
this.loadUsers();
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
setSearchQuery(query: string) {
|
|
1004
|
+
this.searchQuery.set(query);
|
|
1005
|
+
this.currentPage.set(1); // Reset to first page
|
|
1006
|
+
this.loadUsers();
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
setSorting(sortBy: string, order: 'asc' | 'desc') {
|
|
1010
|
+
this.sortBy.set(sortBy);
|
|
1011
|
+
this.sortOrder.set(order);
|
|
1012
|
+
this.currentPage.set(1); // Reset to first page
|
|
1013
|
+
this.loadUsers();
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
```
|
|
1017
|
+
|
|
1018
|
+
### Real-time Chat with State Management
|
|
1019
|
+
|
|
1020
|
+
```typescript
|
|
1021
|
+
@Injectable()
|
|
1022
|
+
export class ChatStateService extends HTTPManagerStateService<ChatMessage> {
|
|
1023
|
+
|
|
1024
|
+
private currentUser = signal<any>(null);
|
|
1025
|
+
private activeChannel = signal<string>('general');
|
|
1026
|
+
|
|
1027
|
+
// Observables
|
|
1028
|
+
messages$ = this.store.data$;
|
|
1029
|
+
channelUsers$ = this.store.communicationMessages$;
|
|
1030
|
+
connectionStatus$ = this.store.connectionStatus$;
|
|
1031
|
+
|
|
1032
|
+
constructor() {
|
|
1033
|
+
super(
|
|
1034
|
+
ApiRequest.adapt({
|
|
1035
|
+
server: 'http://localhost:8080',
|
|
1036
|
+
path: ['messages'],
|
|
1037
|
+
adapter: ChatMessage.adapt,
|
|
1038
|
+
ws: {
|
|
1039
|
+
id: 'CHAT_WS',
|
|
1040
|
+
wsServer: 'ws://localhost:8080'
|
|
1041
|
+
}
|
|
1042
|
+
}),
|
|
1043
|
+
DataType.ARRAY
|
|
1044
|
+
);
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
initializeChat(user: any) {
|
|
1048
|
+
this.currentUser.set(user);
|
|
1049
|
+
this.store.initializeConnection(
|
|
1050
|
+
'ws://localhost:8080',
|
|
1051
|
+
user.jwtToken,
|
|
1052
|
+
user
|
|
1053
|
+
);
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
joinChannel(channelName: string) {
|
|
1057
|
+
this.activeChannel.set(channelName);
|
|
1058
|
+
this.store.createChannel(channelName);
|
|
1059
|
+
this.store.subscribeToChannel(channelName);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
leaveChannel(channelName: string) {
|
|
1063
|
+
this.store.unsubscribeFromChannel(channelName);
|
|
1064
|
+
if (this.activeChannel() === channelName) {
|
|
1065
|
+
this.activeChannel.set('general');
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
sendMessage(message: string) {
|
|
1070
|
+
const channel = this.activeChannel();
|
|
1071
|
+
const user = this.currentUser();
|
|
1072
|
+
|
|
1073
|
+
if (!channel || !user) return;
|
|
1074
|
+
|
|
1075
|
+
// Create message record (HTTP)
|
|
1076
|
+
this.createRecord({
|
|
1077
|
+
id: Date.now(), // Temporary ID
|
|
1078
|
+
content: message,
|
|
1079
|
+
channel: channel,
|
|
1080
|
+
userId: user.id,
|
|
1081
|
+
userName: user.name,
|
|
1082
|
+
timestamp: new Date()
|
|
1083
|
+
});
|
|
1084
|
+
|
|
1085
|
+
// Send via WebSocket
|
|
1086
|
+
this.store.sendMessage(
|
|
1087
|
+
{
|
|
1088
|
+
message: message,
|
|
1089
|
+
userId: user.id,
|
|
1090
|
+
userName: user.name,
|
|
1091
|
+
channel: channel
|
|
1092
|
+
},
|
|
1093
|
+
[channel]
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
getMessagesForChannel(channelName: string) {
|
|
1098
|
+
return this.store.data$.pipe(
|
|
1099
|
+
map(messages => messages.filter(msg => msg.channel === channelName))
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
```
|
|
1104
|
+
|
|
1105
|
+
### Complex Filter and Search State
|
|
1106
|
+
|
|
1107
|
+
```typescript
|
|
1108
|
+
@Injectable()
|
|
1109
|
+
export class AdvancedUserStateService extends HTTPManagerStateService<User> {
|
|
1110
|
+
|
|
1111
|
+
// Filter state
|
|
1112
|
+
activeFilter = signal<'all' | 'active' | 'inactive'>('all');
|
|
1113
|
+
roleFilter = signal<string>('');
|
|
1114
|
+
searchQuery = signal<string>('');
|
|
1115
|
+
dateRange = signal<{start: Date, end: Date} | null>(null);
|
|
1116
|
+
|
|
1117
|
+
// Computed filters
|
|
1118
|
+
filteredUsers = computed(() => {
|
|
1119
|
+
let users = this.store.data$();
|
|
1120
|
+
|
|
1121
|
+
// Apply active filter
|
|
1122
|
+
if (this.activeFilter() !== 'all') {
|
|
1123
|
+
const isActive = this.activeFilter() === 'active';
|
|
1124
|
+
users = users.filter(user => user.active === isActive);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
// Apply role filter
|
|
1128
|
+
if (this.roleFilter()) {
|
|
1129
|
+
users = users.filter(user => user.role === this.roleFilter());
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
// Apply search query
|
|
1133
|
+
if (this.searchQuery()) {
|
|
1134
|
+
const query = this.searchQuery().toLowerCase();
|
|
1135
|
+
users = users.filter(user =>
|
|
1136
|
+
user.name.toLowerCase().includes(query) ||
|
|
1137
|
+
user.email.toLowerCase().includes(query)
|
|
1138
|
+
);
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
// Apply date range
|
|
1142
|
+
if (this.dateRange()) {
|
|
1143
|
+
const { start, end } = this.dateRange()!;
|
|
1144
|
+
users = users.filter(user => {
|
|
1145
|
+
const userDate = new Date(user.createdAt);
|
|
1146
|
+
return userDate >= start && userDate <= end;
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
return users;
|
|
1151
|
+
});
|
|
1152
|
+
|
|
1153
|
+
filterStats = computed(() => {
|
|
1154
|
+
const allUsers = this.store.data$();
|
|
1155
|
+
return {
|
|
1156
|
+
total: allUsers.length,
|
|
1157
|
+
active: allUsers.filter(u => u.active).length,
|
|
1158
|
+
inactive: allUsers.filter(u => !u.active).length,
|
|
1159
|
+
filtered: this.filteredUsers().length
|
|
1160
|
+
};
|
|
1161
|
+
});
|
|
1162
|
+
|
|
1163
|
+
constructor() {
|
|
1164
|
+
super(
|
|
1165
|
+
ApiRequest.adapt({
|
|
1166
|
+
server: 'http://localhost:8080',
|
|
1167
|
+
path: ['users'],
|
|
1168
|
+
adapter: User.adapt
|
|
1169
|
+
}),
|
|
1170
|
+
DataType.ARRAY,
|
|
1171
|
+
DatabaseStorage.adapt({
|
|
1172
|
+
table: 'users-advanced-cache',
|
|
1173
|
+
expiresIn: '1h'
|
|
1174
|
+
})
|
|
1175
|
+
);
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
// Filter methods
|
|
1179
|
+
setActiveFilter(filter: 'all' | 'active' | 'inactive') {
|
|
1180
|
+
this.activeFilter.set(filter);
|
|
1181
|
+
this.fetchRecords();
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
setRoleFilter(role: string) {
|
|
1185
|
+
this.roleFilter.set(role);
|
|
1186
|
+
this.fetchRecords();
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
setSearchQuery(query: string) {
|
|
1190
|
+
this.searchQuery.set(query);
|
|
1191
|
+
// Debounced search
|
|
1192
|
+
setTimeout(() => this.fetchRecords(), 300);
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
setDateRange(start: Date, end: Date) {
|
|
1196
|
+
this.dateRange.set({ start, end });
|
|
1197
|
+
this.fetchRecords();
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
clearFilters() {
|
|
1201
|
+
this.activeFilter.set('all');
|
|
1202
|
+
this.roleFilter.set('');
|
|
1203
|
+
this.searchQuery.set('');
|
|
1204
|
+
this.dateRange.set(null);
|
|
1205
|
+
this.fetchRecords();
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
```
|
|
1209
|
+
|
|
1210
|
+
## Error Handling and Retry Logic
|
|
1211
|
+
|
|
1212
|
+
### Custom Error Handling
|
|
1213
|
+
|
|
1214
|
+
```typescript
|
|
1215
|
+
@Injectable()
|
|
1216
|
+
export class RobustUserStateService extends HTTPManagerStateService<User> {
|
|
1217
|
+
|
|
1218
|
+
private retryAttempts = signal(0);
|
|
1219
|
+
private maxRetries = signal(3);
|
|
1220
|
+
|
|
1221
|
+
constructor() {
|
|
1222
|
+
super(
|
|
1223
|
+
ApiRequest.adapt({
|
|
1224
|
+
server: 'http://localhost:8080',
|
|
1225
|
+
path: ['users'],
|
|
1226
|
+
retry: { times: 3, delay: 2 },
|
|
1227
|
+
displayError: true
|
|
1228
|
+
}),
|
|
1229
|
+
DataType.ARRAY
|
|
1230
|
+
);
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
// Override to add custom error handling
|
|
1234
|
+
fetchRecords(params?: any) {
|
|
1235
|
+
this.retryAttempts.set(0);
|
|
1236
|
+
super.fetchRecords(params);
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
// Custom retry logic for critical operations
|
|
1240
|
+
criticalUpdate(user: User) {
|
|
1241
|
+
try {
|
|
1242
|
+
this.updateRecord(user);
|
|
1243
|
+
} catch (error) {
|
|
1244
|
+
this.handleCriticalError(error, () => this.criticalUpdate(user));
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
private handleCriticalError(error: any, retryFn: () => void) {
|
|
1249
|
+
const currentAttempts = this.retryAttempts();
|
|
1250
|
+
const maxAttempts = this.maxRetries();
|
|
1251
|
+
|
|
1252
|
+
if (currentAttempts < maxAttempts) {
|
|
1253
|
+
this.retryAttempts.set(currentAttempts + 1);
|
|
1254
|
+
|
|
1255
|
+
// Exponential backoff
|
|
1256
|
+
const delay = Math.pow(2, currentAttempts) * 1000;
|
|
1257
|
+
setTimeout(retryFn, delay);
|
|
1258
|
+
} else {
|
|
1259
|
+
// Max retries reached, show critical error
|
|
1260
|
+
this.showCriticalErrorDialog(error);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
private showCriticalErrorDialog(error: any) {
|
|
1265
|
+
// Implement your critical error handling
|
|
1266
|
+
console.error('Critical operation failed after retries:', error);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
```
|
|
1270
|
+
|
|
1271
|
+
## Best Practices
|
|
1272
|
+
|
|
1273
|
+
### 1. Service Layer Pattern
|
|
1274
|
+
|
|
1275
|
+
```typescript
|
|
1276
|
+
// ✅ Good
|
|
1277
|
+
@Injectable()
|
|
1278
|
+
export class UserStateService extends HTTPManagerStateService<User> {
|
|
1279
|
+
// Business logic methods
|
|
1280
|
+
activateUser(id: number) {
|
|
1281
|
+
this.updateRecord({ id, active: true });
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
deactivateUser(id: number) {
|
|
1285
|
+
this.updateRecord({ id, active: false });
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
// ❌ Avoid
|
|
1290
|
+
@Component({
|
|
1291
|
+
template: `<div (click)="store.updateRecord(...)">`
|
|
1292
|
+
})
|
|
1293
|
+
export class BadComponent {}
|
|
1294
|
+
```
|
|
1295
|
+
|
|
1296
|
+
### 2. Proper State Management
|
|
1297
|
+
|
|
1298
|
+
```typescript
|
|
1299
|
+
// ✅ Good
|
|
1300
|
+
loadUsers() {
|
|
1301
|
+
this.fetchRecords(); // Uses internal state management
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
// ❌ Avoid
|
|
1305
|
+
loadUsers() {
|
|
1306
|
+
this.http.get('users').subscribe(users => {
|
|
1307
|
+
this.users = users; // Manual state management
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
```
|
|
1311
|
+
|
|
1312
|
+
### 3. WebSocket Error Handling
|
|
1313
|
+
|
|
1314
|
+
```typescript
|
|
1315
|
+
// ✅ Good
|
|
1316
|
+
initializeConnection(wsServer: string, jwtToken: string, user: any) {
|
|
1317
|
+
try {
|
|
1318
|
+
this.setApiRequestOptions(
|
|
1319
|
+
ApiRequest.adapt({
|
|
1320
|
+
...this.apiRequestOptions,
|
|
1321
|
+
ws: { ...this.apiRequestOptions.ws, wsServer, jwtToken, user }
|
|
1322
|
+
})
|
|
1323
|
+
);
|
|
1324
|
+
} catch (error) {
|
|
1325
|
+
console.error('WebSocket initialization failed:', error);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
```
|
|
1329
|
+
|
|
1330
|
+
### 4. Memory Management
|
|
1331
|
+
|
|
1332
|
+
```typescript
|
|
1333
|
+
@Component({ template: '...' })
|
|
1334
|
+
export class UserComponent implements OnDestroy {
|
|
1335
|
+
private destroy$ = new Subject<void>();
|
|
1336
|
+
|
|
1337
|
+
ngOnInit() {
|
|
1338
|
+
this.store.data$
|
|
1339
|
+
.pipe(takeUntil(this.destroy$))
|
|
1340
|
+
.subscribe();
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
ngOnDestroy() {
|
|
1344
|
+
this.destroy$.next();
|
|
1345
|
+
this.destroy$.complete();
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
```
|
|
1349
|
+
|
|
1350
|
+
## Troubleshooting
|
|
1351
|
+
|
|
1352
|
+
### Common Issues
|
|
1353
|
+
|
|
1354
|
+
#### 1. WebSocket Not Connecting
|
|
1355
|
+
```typescript
|
|
1356
|
+
// Check configuration
|
|
1357
|
+
console.log('WS Config:', this.store.apiRequestOptions?.ws);
|
|
1358
|
+
|
|
1359
|
+
// Verify connection
|
|
1360
|
+
this.store.connectionStatus$.subscribe(status => {
|
|
1361
|
+
console.log('WebSocket status:', status);
|
|
1362
|
+
});
|
|
1363
|
+
```
|
|
1364
|
+
|
|
1365
|
+
#### 2. State Not Updating
|
|
1366
|
+
```typescript
|
|
1367
|
+
// Ensure adapter is provided for database storage
|
|
1368
|
+
ApiRequest.adapt({
|
|
1369
|
+
path: ['users'],
|
|
1370
|
+
adapter: User.adapt // Required!
|
|
1371
|
+
});
|
|
1372
|
+
```
|
|
1373
|
+
|
|
1374
|
+
#### 3. Infinite Loops
|
|
1375
|
+
```typescript
|
|
1376
|
+
// Avoid calling fetchRecords() in response to WebSocket messages
|
|
1377
|
+
// The service handles this automatically
|
|
1378
|
+
|
|
1379
|
+
// ❌ Don't do this
|
|
1380
|
+
communicationMessages$.subscribe(msg => {
|
|
1381
|
+
this.fetchRecords(); // This can cause loops
|
|
1382
|
+
});
|
|
1383
|
+
```
|
|
1384
|
+
|
|
1385
|
+
## Related Documentation
|
|
1386
|
+
|
|
1387
|
+
- [HTTP Manager Service](http-manager/README.md)
|
|
1388
|
+
- [HTTP Signals Service](http-signals/README.md)
|
|
1389
|
+
- [WebSocket Service](websocket/README.md)
|
|
1390
|
+
- [Database Manager Service](database/README.md)
|
|
1391
|
+
- [Architecture Overview](../architecture/README.md)
|