@cratis/arc 22.0.2 → 22.1.0
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/dist/cjs/queries/ObservableQueryFor.js +6 -1
- package/dist/cjs/queries/ObservableQueryFor.js.map +1 -1
- package/dist/cjs/queries/QueryInstanceCache.js +6 -6
- package/dist/cjs/queries/QueryInstanceCache.js.map +1 -1
- package/dist/cjs/queries/QueryResult.js +23 -4
- package/dist/cjs/queries/QueryResult.js.map +1 -1
- package/dist/cjs/queries/QueryResultWithState.js +20 -7
- package/dist/cjs/queries/QueryResultWithState.js.map +1 -1
- package/dist/cjs/queries/ServerSentEventHubConnection.js +120 -58
- package/dist/cjs/queries/ServerSentEventHubConnection.js.map +1 -1
- package/dist/cjs/queries/WebSocketHubConnection.js +121 -51
- package/dist/cjs/queries/WebSocketHubConnection.js.map +1 -1
- package/dist/esm/queries/IQueryResult.d.ts +1 -0
- package/dist/esm/queries/IQueryResult.d.ts.map +1 -1
- package/dist/esm/queries/ObservableQueryFor.d.ts.map +1 -1
- package/dist/esm/queries/ObservableQueryFor.js +6 -1
- package/dist/esm/queries/ObservableQueryFor.js.map +1 -1
- package/dist/esm/queries/QueryInstanceCache.d.ts +1 -1
- package/dist/esm/queries/QueryInstanceCache.d.ts.map +1 -1
- package/dist/esm/queries/QueryInstanceCache.js +6 -6
- package/dist/esm/queries/QueryInstanceCache.js.map +1 -1
- package/dist/esm/queries/QueryResult.d.ts +2 -0
- package/dist/esm/queries/QueryResult.d.ts.map +1 -1
- package/dist/esm/queries/QueryResult.js +23 -4
- package/dist/esm/queries/QueryResult.js.map +1 -1
- package/dist/esm/queries/QueryResultWithState.d.ts +3 -3
- package/dist/esm/queries/QueryResultWithState.d.ts.map +1 -1
- package/dist/esm/queries/QueryResultWithState.js +20 -7
- package/dist/esm/queries/QueryResultWithState.js.map +1 -1
- package/dist/esm/queries/ServerSentEventHubConnection.d.ts +11 -4
- package/dist/esm/queries/ServerSentEventHubConnection.d.ts.map +1 -1
- package/dist/esm/queries/ServerSentEventHubConnection.js +120 -58
- package/dist/esm/queries/ServerSentEventHubConnection.js.map +1 -1
- package/dist/esm/queries/WebSocketHubConnection.d.ts +13 -2
- package/dist/esm/queries/WebSocketHubConnection.d.ts.map +1 -1
- package/dist/esm/queries/WebSocketHubConnection.js +121 -51
- package/dist/esm/queries/WebSocketHubConnection.js.map +1 -1
- package/dist/esm/queries/for_ObservableQueryFor/when_performing/with_a_not_ready_result.d.ts +2 -0
- package/dist/esm/queries/for_ObservableQueryFor/when_performing/with_a_not_ready_result.d.ts.map +1 -0
- package/dist/esm/queries/for_ObservableQueryFor/when_performing/with_a_not_ready_result.js +34 -0
- package/dist/esm/queries/for_ObservableQueryFor/when_performing/with_a_not_ready_result.js.map +1 -0
- package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_direct_mode_and_sse_transport.js +26 -2
- package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_direct_mode_and_sse_transport.js.map +1 -1
- package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_direct_mode_disabled_and_sse_transport.js +60 -3
- package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_direct_mode_disabled_and_sse_transport.js.map +1 -1
- package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_web_socket_hub_payload_without_readiness.d.ts +2 -0
- package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_web_socket_hub_payload_without_readiness.d.ts.map +1 -0
- package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_web_socket_hub_payload_without_readiness.js +83 -0
- package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_web_socket_hub_payload_without_readiness.js.map +1 -0
- package/dist/esm/queries/for_QueryInstanceCache/when_setting_last_result/with_only_readiness_changed.d.ts +2 -0
- package/dist/esm/queries/for_QueryInstanceCache/when_setting_last_result/with_only_readiness_changed.d.ts.map +1 -0
- package/dist/esm/queries/for_QueryInstanceCache/when_setting_last_result/with_only_readiness_changed.js +22 -0
- package/dist/esm/queries/for_QueryInstanceCache/when_setting_last_result/with_only_readiness_changed.js.map +1 -0
- package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/with_readiness_false.d.ts +2 -0
- package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/with_readiness_false.d.ts.map +1 -0
- package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/with_readiness_false.js +17 -0
- package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/with_readiness_false.js.map +1 -0
- package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/without_readiness.d.ts +2 -0
- package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/without_readiness.d.ts.map +1 -0
- package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/without_readiness.js +16 -0
- package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/without_readiness.js.map +1 -0
- package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/with_readiness_false.d.ts +2 -0
- package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/with_readiness_false.d.ts.map +1 -0
- package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/with_readiness_false.js +19 -0
- package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/with_readiness_false.js.map +1 -0
- package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/without_readiness.d.ts +2 -0
- package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/without_readiness.d.ts.map +1 -0
- package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/without_readiness.js +19 -0
- package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/without_readiness.js.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_deferred_promise.d.ts +7 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_deferred_promise.d.ts.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_deferred_promise.js +12 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_deferred_promise.js.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_server_sent_event_hub_connection.d.ts +2 -1
- package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_server_sent_event_hub_connection.d.ts.map +1 -1
- package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_server_sent_event_hub_connection.js +15 -4
- package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_server_sent_event_hub_connection.js.map +1 -1
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_a_retired_event_source_fires/ignores_connected_and_error_callbacks.d.ts +2 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_a_retired_event_source_fires/ignores_connected_and_error_callbacks.d.ts.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_a_retired_event_source_fires/ignores_connected_and_error_callbacks.js +44 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_a_retired_event_source_fires/ignores_connected_and_error_callbacks.js.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_replaced.d.ts +2 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_replaced.d.ts.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_replaced.js +74 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_replaced.js.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_unsubscribed.d.ts +2 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_unsubscribed.d.ts.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_unsubscribed.js +74 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_unsubscribed.js.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_is_rejected.d.ts +2 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_is_rejected.d.ts.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_is_rejected.js +59 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_is_rejected.js.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_returns_non_ok.d.ts +2 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_returns_non_ok.d.ts.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_returns_non_ok.js +58 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_returns_non_ok.js.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_disposing/cleans_up_resources.js +6 -3
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_disposing/cleans_up_resources.js.map +1 -1
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.js +23 -6
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.js.map +1 -1
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_reconnecting/after_error.js +20 -5
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_reconnecting/after_error.js.map +1 -1
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_reconnecting/and_an_immediate_open_wins_the_backoff_race.d.ts +2 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_reconnecting/and_an_immediate_open_wins_the_backoff_race.d.ts.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_reconnecting/and_an_immediate_open_wins_the_backoff_race.js +20 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_reconnecting/and_an_immediate_open_wins_the_backoff_race.js.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_replacing_a_subscription/ignores_messages_from_the_retired_generation.d.ts +2 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_replacing_a_subscription/ignores_messages_from_the_retired_generation.d.ts.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_replacing_a_subscription/ignores_messages_from_the_retired_generation.js +68 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_replacing_a_subscription/ignores_messages_from_the_retired_generation.js.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_subscribing/sends_subscribe_post.js +38 -13
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_subscribing/sends_subscribe_post.js.map +1 -1
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_subscription_revisions_are_not_advertised/uses_legacy_protocol.d.ts +2 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_subscription_revisions_are_not_advertised/uses_legacy_protocol.d.ts.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_subscription_revisions_are_not_advertised/uses_legacy_protocol.js +54 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_subscription_revisions_are_not_advertised/uses_legacy_protocol.js.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_unsubscribing/the_only_query.js +18 -4
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_unsubscribing/the_only_query.js.map +1 -1
- package/dist/esm/queries/for_WebSocketHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.js +22 -5
- package/dist/esm/queries/for_WebSocketHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.js.map +1 -1
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/ignores_a_pending_reconnect_and_stale_socket_callbacks.d.ts +2 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/ignores_a_pending_reconnect_and_stale_socket_callbacks.d.ts.map +1 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/ignores_a_pending_reconnect_and_stale_socket_callbacks.js +108 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/ignores_a_pending_reconnect_and_stale_socket_callbacks.js.map +1 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/replaces_a_closing_socket_safely.d.ts +2 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/replaces_a_closing_socket_safely.d.ts.map +1 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/replaces_a_closing_socket_safely.js +33 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/replaces_a_closing_socket_safely.js.map +1 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/resubscribes_all_queries.js +26 -4
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/resubscribes_all_queries.js.map +1 -1
- package/dist/esm/queries/for_WebSocketHubConnection/when_replacing_a_subscription/ignores_stale_result_unauthorized_and_error.d.ts +2 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_replacing_a_subscription/ignores_stale_result_unauthorized_and_error.d.ts.map +1 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_replacing_a_subscription/ignores_stale_result_unauthorized_and_error.js +62 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_replacing_a_subscription/ignores_stale_result_unauthorized_and_error.js.map +1 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_subscribing/to_a_query.js +18 -5
- package/dist/esm/queries/for_WebSocketHubConnection/when_subscribing/to_a_query.js.map +1 -1
- package/dist/esm/queries/for_WebSocketHubConnection/when_subscription_revisions_are_supported/upgrades_active_subscriptions_to_strict_mode.d.ts +2 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_subscription_revisions_are_supported/upgrades_active_subscriptions_to_strict_mode.d.ts.map +1 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_subscription_revisions_are_supported/upgrades_active_subscriptions_to_strict_mode.js +56 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_subscription_revisions_are_supported/upgrades_active_subscriptions_to_strict_mode.js.map +1 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_unsubscribing/the_only_query.js +16 -3
- package/dist/esm/queries/for_WebSocketHubConnection/when_unsubscribing/the_only_query.js.map +1 -1
- package/dist/esm/queries/for_WebSocketHubConnection/when_unsubscribing/with_revision_support_not_advertised.d.ts +2 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_unsubscribing/with_revision_support_not_advertised.d.ts.map +1 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_unsubscribing/with_revision_support_not_advertised.js +33 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_unsubscribing/with_revision_support_not_advertised.js.map +1 -0
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/queries/IQueryResult.ts +10 -0
- package/queries/ObservableQueryFor.ts +44 -16
- package/queries/QueryInstanceCache.ts +42 -19
- package/queries/QueryResult.ts +145 -79
- package/queries/QueryResultWithState.ts +32 -9
- package/queries/ServerSentEventHubConnection.ts +270 -83
- package/queries/WebSocketHubConnection.ts +188 -56
- package/queries/for_ObservableQueryFor/when_performing/with_a_not_ready_result.ts +46 -0
- package/queries/for_ObservableQueryFor/when_subscribing/with_direct_mode_and_sse_transport.ts +91 -57
- package/queries/for_ObservableQueryFor/when_subscribing/with_direct_mode_disabled_and_sse_transport.ts +125 -53
- package/queries/for_ObservableQueryFor/when_subscribing/with_web_socket_hub_payload_without_readiness.ts +107 -0
- package/queries/for_QueryInstanceCache/when_setting_last_result/with_only_readiness_changed.ts +63 -0
- package/queries/for_QueryResult/when_constructing_from_a_server_result/with_readiness_false.ts +25 -0
- package/queries/for_QueryResult/when_constructing_from_a_server_result/without_readiness.ts +24 -0
- package/queries/for_QueryResultWithState/when_creating_from_query_result/with_readiness_false.ts +28 -0
- package/queries/for_QueryResultWithState/when_creating_from_query_result/without_readiness.ts +25 -0
- package/queries/for_ServerSentEventHubConnection/given/a_deferred_promise.ts +15 -0
- package/queries/for_ServerSentEventHubConnection/given/a_server_sent_event_hub_connection.ts +18 -7
- package/queries/for_ServerSentEventHubConnection/when_a_retired_event_source_fires/ignores_connected_and_error_callbacks.ts +59 -0
- package/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_replaced.ts +113 -0
- package/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_unsubscribed.ts +109 -0
- package/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_is_rejected.ts +81 -0
- package/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_returns_non_ok.ts +83 -0
- package/queries/for_ServerSentEventHubConnection/when_disposing/cleans_up_resources.ts +30 -21
- package/queries/for_ServerSentEventHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.ts +56 -32
- package/queries/for_ServerSentEventHubConnection/when_reconnecting/after_error.ts +23 -6
- package/queries/for_ServerSentEventHubConnection/when_reconnecting/and_an_immediate_open_wins_the_backoff_race.ts +41 -0
- package/queries/for_ServerSentEventHubConnection/when_replacing_a_subscription/ignores_messages_from_the_retired_generation.ts +92 -0
- package/queries/for_ServerSentEventHubConnection/when_subscribing/sends_subscribe_post.ts +69 -30
- package/queries/for_ServerSentEventHubConnection/when_subscription_revisions_are_not_advertised/uses_legacy_protocol.ts +69 -0
- package/queries/for_ServerSentEventHubConnection/when_unsubscribing/the_only_query.ts +43 -24
- package/queries/for_WebSocketHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.ts +53 -29
- package/queries/for_WebSocketHubConnection/when_reconnecting/ignores_a_pending_reconnect_and_stale_socket_callbacks.ts +131 -0
- package/queries/for_WebSocketHubConnection/when_reconnecting/replaces_a_closing_socket_safely.ts +39 -0
- package/queries/for_WebSocketHubConnection/when_reconnecting/resubscribes_all_queries.ts +72 -36
- package/queries/for_WebSocketHubConnection/when_replacing_a_subscription/ignores_stale_result_unauthorized_and_error.ts +88 -0
- package/queries/for_WebSocketHubConnection/when_subscribing/to_a_query.ts +54 -27
- package/queries/for_WebSocketHubConnection/when_subscription_revisions_are_supported/upgrades_active_subscriptions_to_strict_mode.ts +72 -0
- package/queries/for_WebSocketHubConnection/when_unsubscribing/the_only_query.ts +43 -18
- package/queries/for_WebSocketHubConnection/when_unsubscribing/with_revision_support_not_advertised.ts +44 -0
|
@@ -4,14 +4,17 @@
|
|
|
4
4
|
import { ValidationResult } from '../validation/ValidationResult';
|
|
5
5
|
import { IQueryResult } from './IQueryResult';
|
|
6
6
|
import { PagingInfo } from './PagingInfo';
|
|
7
|
-
import { QueryResult } from './QueryResult';
|
|
8
7
|
import { ChangeSet } from './ChangeSet';
|
|
9
8
|
|
|
10
9
|
/**
|
|
11
10
|
* Represents a specialized {@link QueryResult<TDataType>} that holds state for its execution
|
|
12
11
|
*/
|
|
13
12
|
export class QueryResultWithState<TDataType> implements IQueryResult<TDataType> {
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Creates a settled empty state, such as the state returned by a disabled query.
|
|
15
|
+
* @param defaultValue The query's empty data value.
|
|
16
|
+
* @returns A ready state that is not performing.
|
|
17
|
+
*/
|
|
15
18
|
static empty<TDataType>(defaultValue: TDataType): QueryResultWithState<TDataType> {
|
|
16
19
|
return new QueryResultWithState(
|
|
17
20
|
defaultValue,
|
|
@@ -23,9 +26,18 @@ export class QueryResultWithState<TDataType> implements IQueryResult<TDataType>
|
|
|
23
26
|
false,
|
|
24
27
|
[],
|
|
25
28
|
'',
|
|
26
|
-
false
|
|
29
|
+
false,
|
|
30
|
+
undefined,
|
|
31
|
+
true,
|
|
32
|
+
);
|
|
27
33
|
}
|
|
28
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Creates the local initial state before any server result has been received. This state is not ready because
|
|
37
|
+
* no result exists yet; its performing state independently describes the active client request.
|
|
38
|
+
* @param defaultValue The query's initial data value.
|
|
39
|
+
* @returns A not-ready state that is performing.
|
|
40
|
+
*/
|
|
29
41
|
static initial<TDataType>(defaultValue: TDataType): QueryResultWithState<TDataType> {
|
|
30
42
|
return new QueryResultWithState(
|
|
31
43
|
defaultValue,
|
|
@@ -37,7 +49,10 @@ export class QueryResultWithState<TDataType> implements IQueryResult<TDataType>
|
|
|
37
49
|
false,
|
|
38
50
|
[],
|
|
39
51
|
'',
|
|
40
|
-
true
|
|
52
|
+
true,
|
|
53
|
+
undefined,
|
|
54
|
+
false,
|
|
55
|
+
);
|
|
41
56
|
}
|
|
42
57
|
|
|
43
58
|
/**
|
|
@@ -53,6 +68,8 @@ export class QueryResultWithState<TDataType> implements IQueryResult<TDataType>
|
|
|
53
68
|
* @param {string} exceptionStackTrace Exception stack trace, if any.
|
|
54
69
|
* @param {boolean} isPerforming Whether or not the query is being performed. True if its performing, false if it is done.
|
|
55
70
|
* @param {ChangeSet<unknown> | undefined} changeSet Optional change set from the server describing the delta since the last update.
|
|
71
|
+
* @param {boolean} isReady Whether the query has produced a result. Defaults to true so existing constructor calls
|
|
72
|
+
* represent settled synthetic states; {@link initial} explicitly uses false because no server result exists yet.
|
|
56
73
|
*/
|
|
57
74
|
constructor(
|
|
58
75
|
readonly data: TDataType,
|
|
@@ -65,8 +82,9 @@ export class QueryResultWithState<TDataType> implements IQueryResult<TDataType>
|
|
|
65
82
|
readonly exceptionMessages: string[],
|
|
66
83
|
readonly exceptionStackTrace: string,
|
|
67
84
|
readonly isPerforming: boolean,
|
|
68
|
-
readonly changeSet?: ChangeSet<unknown
|
|
69
|
-
|
|
85
|
+
readonly changeSet?: ChangeSet<unknown>,
|
|
86
|
+
readonly isReady: boolean = true,
|
|
87
|
+
) {}
|
|
70
88
|
|
|
71
89
|
/** @inheritdoc */
|
|
72
90
|
get hasData(): boolean {
|
|
@@ -77,12 +95,15 @@ export class QueryResultWithState<TDataType> implements IQueryResult<TDataType>
|
|
|
77
95
|
}
|
|
78
96
|
|
|
79
97
|
/**
|
|
80
|
-
* Create a new {@link QueryResultWithState<TDataType>} from {@link
|
|
98
|
+
* Create a new {@link QueryResultWithState<TDataType>} from an {@link IQueryResult<TDataType>}.
|
|
81
99
|
* @param queryResult The original query result.
|
|
82
100
|
* @param isPerforming Whether or not the query is performing.
|
|
83
101
|
* @returns A new {@link QueryResultWithState<TDataType>}
|
|
84
102
|
*/
|
|
85
|
-
static fromQueryResult<TDataType>(
|
|
103
|
+
static fromQueryResult<TDataType>(
|
|
104
|
+
queryResult: IQueryResult<TDataType>,
|
|
105
|
+
isPerforming: boolean,
|
|
106
|
+
) {
|
|
86
107
|
return new QueryResultWithState<TDataType>(
|
|
87
108
|
queryResult.data,
|
|
88
109
|
queryResult.paging,
|
|
@@ -94,6 +115,8 @@ export class QueryResultWithState<TDataType> implements IQueryResult<TDataType>
|
|
|
94
115
|
queryResult.exceptionMessages,
|
|
95
116
|
queryResult.exceptionStackTrace,
|
|
96
117
|
isPerforming,
|
|
97
|
-
queryResult.changeSet
|
|
118
|
+
queryResult.changeSet,
|
|
119
|
+
queryResult.isReady ?? true,
|
|
120
|
+
);
|
|
98
121
|
}
|
|
99
122
|
}
|
|
@@ -2,19 +2,24 @@
|
|
|
2
2
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
3
|
|
|
4
4
|
import { Globals } from '../Globals';
|
|
5
|
-
import { IObservableQueryHubConnection } from './IObservableQueryHubConnection';
|
|
6
|
-
import { DataReceived } from './ObservableQueryConnection';
|
|
5
|
+
import type { IObservableQueryHubConnection } from './IObservableQueryHubConnection';
|
|
6
|
+
import type { DataReceived } from './ObservableQueryConnection';
|
|
7
7
|
import { HubConnectionKeepAlive } from './HubConnectionKeepAlive';
|
|
8
|
-
import { IReconnectPolicy } from './IReconnectPolicy';
|
|
8
|
+
import type { IReconnectPolicy } from './IReconnectPolicy';
|
|
9
9
|
import { ReconnectPolicy } from './ReconnectPolicy';
|
|
10
10
|
import { QueryResult } from './QueryResult';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
type HubMessage,
|
|
13
|
+
HubMessageType,
|
|
14
|
+
type SubscriptionRequest,
|
|
15
|
+
} from './WebSocketHubConnection';
|
|
12
16
|
|
|
13
17
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
14
18
|
|
|
15
19
|
interface ActiveSubscription {
|
|
16
20
|
request: SubscriptionRequest;
|
|
17
21
|
callback: DataReceived<any>;
|
|
22
|
+
revision: number;
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
/**
|
|
@@ -46,6 +51,8 @@ export class ServerSentEventHubConnection implements IObservableQueryHubConnecti
|
|
|
46
51
|
private _lastPongLatency: number = 0;
|
|
47
52
|
private _latencySamples: number[] = [];
|
|
48
53
|
private _connectTimeoutTimer?: ReturnType<typeof setTimeout>;
|
|
54
|
+
private _nextRevision = 0;
|
|
55
|
+
private _supportsSubscriptionRevisions = false;
|
|
49
56
|
private readonly _keepAlive: HubConnectionKeepAlive;
|
|
50
57
|
|
|
51
58
|
/**
|
|
@@ -57,7 +64,7 @@ export class ServerSentEventHubConnection implements IObservableQueryHubConnecti
|
|
|
57
64
|
* @param {number} keepAliveIntervalMs The keep-alive cadence to assume until the server advertises
|
|
58
65
|
* its own on the {@link HubMessageType.Connected} message (default: 30 000 ms). The connection is
|
|
59
66
|
* considered stale after {@link IDLE_THRESHOLD_FACTOR} times this without any server message.
|
|
60
|
-
* @param {number}
|
|
67
|
+
* @param {number} _connectTimeoutMs How long to wait for the {@link HubMessageType.Connected}
|
|
61
68
|
* message after the HTTP connection opens before giving up and retrying (default: 15 000 ms).
|
|
62
69
|
* @param {IReconnectPolicy} _policy The reconnect policy to use (default: {@link ReconnectPolicy}).
|
|
63
70
|
*/
|
|
@@ -68,7 +75,7 @@ export class ServerSentEventHubConnection implements IObservableQueryHubConnecti
|
|
|
68
75
|
private readonly _microservice: string,
|
|
69
76
|
keepAliveIntervalMs: number = 30000,
|
|
70
77
|
private readonly _connectTimeoutMs: number = 15000,
|
|
71
|
-
private readonly _policy: IReconnectPolicy = new ReconnectPolicy()
|
|
78
|
+
private readonly _policy: IReconnectPolicy = new ReconnectPolicy(),
|
|
72
79
|
) {
|
|
73
80
|
// SSE is server→client only: the client cannot send pings. Instead we watch for
|
|
74
81
|
// inactivity — if the server stops sending messages (including its own keep-alive
|
|
@@ -82,12 +89,20 @@ export class ServerSentEventHubConnection implements IObservableQueryHubConnecti
|
|
|
82
89
|
//
|
|
83
90
|
// The interval below is only the starting assumption; the server advertises its real
|
|
84
91
|
// cadence on the Connected message and {@link handleConnected} reconfigures from it.
|
|
85
|
-
this._keepAlive = new HubConnectionKeepAlive(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
this.
|
|
89
|
-
|
|
90
|
-
|
|
92
|
+
this._keepAlive = new HubConnectionKeepAlive(
|
|
93
|
+
keepAliveIntervalMs,
|
|
94
|
+
() => {
|
|
95
|
+
if (!this._disconnected && this._subscriptions.size > 0) {
|
|
96
|
+
console.warn(
|
|
97
|
+
'SSE hub: no messages received; reconnecting',
|
|
98
|
+
this._keepAlive.idleThresholdMs,
|
|
99
|
+
this._sseUrl,
|
|
100
|
+
);
|
|
101
|
+
this.reconnect();
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
keepAliveIntervalMs * IDLE_THRESHOLD_FACTOR,
|
|
105
|
+
);
|
|
91
106
|
}
|
|
92
107
|
|
|
93
108
|
/** @inheritdoc */
|
|
@@ -97,7 +112,10 @@ export class ServerSentEventHubConnection implements IObservableQueryHubConnecti
|
|
|
97
112
|
|
|
98
113
|
/** @inheritdoc */
|
|
99
114
|
get isConnected(): boolean {
|
|
100
|
-
return
|
|
115
|
+
return (
|
|
116
|
+
this._connectionId !== undefined &&
|
|
117
|
+
this._eventSource?.readyState === EventSource.OPEN
|
|
118
|
+
);
|
|
101
119
|
}
|
|
102
120
|
|
|
103
121
|
/** @inheritdoc */
|
|
@@ -108,18 +126,28 @@ export class ServerSentEventHubConnection implements IObservableQueryHubConnecti
|
|
|
108
126
|
/** @inheritdoc */
|
|
109
127
|
get averageLatency(): number {
|
|
110
128
|
if (this._latencySamples.length === 0) return 0;
|
|
111
|
-
return
|
|
129
|
+
return (
|
|
130
|
+
this._latencySamples.reduce((a, b) => a + b, 0) / this._latencySamples.length
|
|
131
|
+
);
|
|
112
132
|
}
|
|
113
133
|
|
|
114
134
|
/** @inheritdoc */
|
|
115
|
-
subscribe(
|
|
116
|
-
|
|
135
|
+
subscribe(
|
|
136
|
+
queryId: string,
|
|
137
|
+
request: SubscriptionRequest,
|
|
138
|
+
callback: DataReceived<any>,
|
|
139
|
+
): void {
|
|
140
|
+
const sub: ActiveSubscription = {
|
|
141
|
+
request,
|
|
142
|
+
callback,
|
|
143
|
+
revision: this.getNextRevision(),
|
|
144
|
+
};
|
|
117
145
|
this._subscriptions.set(queryId, sub);
|
|
118
146
|
|
|
119
147
|
this.ensureConnected();
|
|
120
148
|
|
|
121
149
|
if (this._connectionId) {
|
|
122
|
-
this.sendSubscribe(queryId,
|
|
150
|
+
this.sendSubscribe(queryId, sub);
|
|
123
151
|
} else {
|
|
124
152
|
// Not yet connected, queue for when Connected message arrives.
|
|
125
153
|
this._pendingSubscriptions.set(queryId, sub);
|
|
@@ -128,11 +156,12 @@ export class ServerSentEventHubConnection implements IObservableQueryHubConnecti
|
|
|
128
156
|
|
|
129
157
|
/** @inheritdoc */
|
|
130
158
|
unsubscribe(queryId: string): void {
|
|
159
|
+
const subscription = this._subscriptions.get(queryId);
|
|
131
160
|
this._subscriptions.delete(queryId);
|
|
132
161
|
this._pendingSubscriptions.delete(queryId);
|
|
133
162
|
|
|
134
|
-
if (this._connectionId) {
|
|
135
|
-
this.sendUnsubscribe(queryId);
|
|
163
|
+
if (this._connectionId && subscription) {
|
|
164
|
+
this.sendUnsubscribe(queryId, subscription.revision);
|
|
136
165
|
}
|
|
137
166
|
|
|
138
167
|
if (this._subscriptions.size === 0) {
|
|
@@ -148,11 +177,24 @@ export class ServerSentEventHubConnection implements IObservableQueryHubConnecti
|
|
|
148
177
|
this._policy.cancel();
|
|
149
178
|
this._keepAlive.stop();
|
|
150
179
|
this.clearConnectTimeout();
|
|
151
|
-
this.
|
|
152
|
-
this._eventSource = undefined;
|
|
180
|
+
this.detachAndCloseEventSource();
|
|
153
181
|
this._connectionId = undefined;
|
|
154
182
|
}
|
|
155
183
|
|
|
184
|
+
private detachAndCloseEventSource(): void {
|
|
185
|
+
const eventSource = this._eventSource;
|
|
186
|
+
if (!eventSource) return;
|
|
187
|
+
|
|
188
|
+
eventSource.onopen = null;
|
|
189
|
+
eventSource.onmessage = null;
|
|
190
|
+
eventSource.onerror = null;
|
|
191
|
+
eventSource.close();
|
|
192
|
+
|
|
193
|
+
if (this._eventSource === eventSource) {
|
|
194
|
+
this._eventSource = undefined;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
156
198
|
private ensureConnected(): void {
|
|
157
199
|
if (this._disconnected) {
|
|
158
200
|
this._disconnected = false;
|
|
@@ -162,6 +204,9 @@ export class ServerSentEventHubConnection implements IObservableQueryHubConnecti
|
|
|
162
204
|
return;
|
|
163
205
|
}
|
|
164
206
|
|
|
207
|
+
// An immediate subscription can win while a back-off callback is pending. Cancel that callback before
|
|
208
|
+
// opening, and let openEventSource perform the same live-source guard for already-queued callbacks.
|
|
209
|
+
this._policy.cancel();
|
|
165
210
|
this.openEventSource();
|
|
166
211
|
}
|
|
167
212
|
|
|
@@ -170,21 +215,16 @@ export class ServerSentEventHubConnection implements IObservableQueryHubConnecti
|
|
|
170
215
|
this._policy.cancel();
|
|
171
216
|
this._keepAlive.stop();
|
|
172
217
|
this.clearConnectTimeout();
|
|
173
|
-
|
|
174
|
-
// Detach all handlers BEFORE closing so that the async onerror / onmessage
|
|
175
|
-
// events that fire after close() cannot observe the new _disconnected=false
|
|
176
|
-
// state set by an immediately following ensureConnected() call. Without this,
|
|
177
|
-
// a stale onerror triggers an unintended reconnect with exponential back-off.
|
|
178
|
-
this._eventSource.onopen = null;
|
|
179
|
-
this._eventSource.onmessage = null;
|
|
180
|
-
this._eventSource.onerror = null;
|
|
181
|
-
this._eventSource.close();
|
|
182
|
-
}
|
|
183
|
-
this._eventSource = undefined;
|
|
218
|
+
this.detachAndCloseEventSource();
|
|
184
219
|
this._connectionId = undefined;
|
|
185
220
|
}
|
|
186
221
|
|
|
187
222
|
private openEventSource(): void {
|
|
223
|
+
if (this._eventSource && this._eventSource.readyState !== EventSource.CLOSED) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
this._policy.cancel();
|
|
188
228
|
let url = this._sseUrl;
|
|
189
229
|
if (this._microservice?.length > 0) {
|
|
190
230
|
const param = `${Globals.microserviceWSQueryArgument}=${encodeURIComponent(this._microservice)}`;
|
|
@@ -192,11 +232,15 @@ export class ServerSentEventHubConnection implements IObservableQueryHubConnecti
|
|
|
192
232
|
}
|
|
193
233
|
|
|
194
234
|
this._connectionId = undefined;
|
|
195
|
-
this.
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
235
|
+
this._supportsSubscriptionRevisions = false;
|
|
236
|
+
const eventSource = Globals.eventSourceFactory
|
|
237
|
+
? Globals.eventSourceFactory(url)
|
|
238
|
+
: new EventSource(url);
|
|
239
|
+
this._eventSource = eventSource;
|
|
240
|
+
|
|
241
|
+
eventSource.onopen = () => {
|
|
242
|
+
if (this._disconnected || this._eventSource !== eventSource) return;
|
|
243
|
+
console.log('SSE hub connection established', url);
|
|
200
244
|
this._policy.reset();
|
|
201
245
|
this._keepAlive.start();
|
|
202
246
|
|
|
@@ -204,22 +248,30 @@ export class ServerSentEventHubConnection implements IObservableQueryHubConnecti
|
|
|
204
248
|
// connection is broken. Close and retry via the reconnect policy.
|
|
205
249
|
this.clearConnectTimeout();
|
|
206
250
|
this._connectTimeoutTimer = setTimeout(() => {
|
|
207
|
-
if (
|
|
208
|
-
|
|
251
|
+
if (
|
|
252
|
+
!this._disconnected &&
|
|
253
|
+
this._eventSource === eventSource &&
|
|
254
|
+
!this._connectionId
|
|
255
|
+
) {
|
|
256
|
+
console.warn(
|
|
257
|
+
'SSE hub: no Connected message within timeout; retrying',
|
|
258
|
+
this._connectTimeoutMs,
|
|
259
|
+
url,
|
|
260
|
+
);
|
|
209
261
|
this.reconnect();
|
|
210
262
|
}
|
|
211
263
|
}, this._connectTimeoutMs);
|
|
212
264
|
};
|
|
213
265
|
|
|
214
|
-
|
|
215
|
-
if (this._disconnected) return;
|
|
266
|
+
eventSource.onmessage = (event: MessageEvent) => {
|
|
267
|
+
if (this._disconnected || this._eventSource !== eventSource) return;
|
|
216
268
|
this._keepAlive.recordActivity();
|
|
217
269
|
this.handleMessage(event.data as string);
|
|
218
270
|
};
|
|
219
271
|
|
|
220
|
-
|
|
221
|
-
if (this._disconnected) return;
|
|
222
|
-
console.warn(
|
|
272
|
+
eventSource.onerror = () => {
|
|
273
|
+
if (this._disconnected || this._eventSource !== eventSource) return;
|
|
274
|
+
console.warn('SSE hub connection error', url);
|
|
223
275
|
this.reconnect();
|
|
224
276
|
};
|
|
225
277
|
}
|
|
@@ -228,9 +280,8 @@ export class ServerSentEventHubConnection implements IObservableQueryHubConnecti
|
|
|
228
280
|
this._keepAlive.stop();
|
|
229
281
|
this.clearConnectTimeout();
|
|
230
282
|
|
|
231
|
-
//
|
|
232
|
-
this.
|
|
233
|
-
this._eventSource = undefined;
|
|
283
|
+
// Detach before closing so callbacks already queued by the retired source are inert.
|
|
284
|
+
this.detachAndCloseEventSource();
|
|
234
285
|
this._connectionId = undefined;
|
|
235
286
|
|
|
236
287
|
// Move all active subscriptions to pending so they re-subscribe when
|
|
@@ -243,7 +294,7 @@ export class ServerSentEventHubConnection implements IObservableQueryHubConnecti
|
|
|
243
294
|
|
|
244
295
|
this._policy.schedule(() => {
|
|
245
296
|
if (!this._disconnected && this._subscriptions.size > 0) {
|
|
246
|
-
this.
|
|
297
|
+
this.ensureConnected();
|
|
247
298
|
}
|
|
248
299
|
}, this._sseUrl);
|
|
249
300
|
}
|
|
@@ -270,11 +321,19 @@ export class ServerSentEventHubConnection implements IObservableQueryHubConnecti
|
|
|
270
321
|
// Server-sent keep-alive ping — activity already recorded in onmessage.
|
|
271
322
|
break;
|
|
272
323
|
case HubMessageType.Unauthorized:
|
|
273
|
-
|
|
274
|
-
|
|
324
|
+
if (this.isMessageForCurrentSubscription(message)) {
|
|
325
|
+
console.warn('SSE hub: query unauthorized', message.queryId);
|
|
326
|
+
this.handleUnauthorized(message);
|
|
327
|
+
}
|
|
275
328
|
break;
|
|
276
329
|
case HubMessageType.Error:
|
|
277
|
-
|
|
330
|
+
if (this.isMessageForCurrentSubscription(message)) {
|
|
331
|
+
console.error(
|
|
332
|
+
'SSE hub: query error',
|
|
333
|
+
message.queryId,
|
|
334
|
+
message.payload,
|
|
335
|
+
);
|
|
336
|
+
}
|
|
278
337
|
break;
|
|
279
338
|
}
|
|
280
339
|
} catch (error) {
|
|
@@ -283,17 +342,25 @@ export class ServerSentEventHubConnection implements IObservableQueryHubConnecti
|
|
|
283
342
|
}
|
|
284
343
|
|
|
285
344
|
private handleConnected(message: HubMessage): void {
|
|
286
|
-
|
|
287
|
-
|
|
345
|
+
const connectionId = message.payload as string;
|
|
346
|
+
const connectionIdChanged = this._connectionId !== connectionId;
|
|
347
|
+
this._connectionId = connectionId;
|
|
348
|
+
this._supportsSubscriptionRevisions =
|
|
349
|
+
message.supportsSubscriptionRevisions === true;
|
|
350
|
+
console.log('SSE hub: connected', this._connectionId);
|
|
288
351
|
|
|
289
352
|
// Connected message arrived — cancel the connect timeout.
|
|
290
353
|
this.clearConnectTimeout();
|
|
291
354
|
|
|
292
355
|
this.applyServerKeepAliveInterval(message.keepAliveIntervalMs);
|
|
293
356
|
|
|
294
|
-
//
|
|
295
|
-
|
|
296
|
-
|
|
357
|
+
// The active collection is authoritative. A previous connection's subscribe POST may
|
|
358
|
+
// still be in flight, so relying only on pending subscriptions can orphan a query when
|
|
359
|
+
// the server assigns a new connection ID before that POST completes.
|
|
360
|
+
if (connectionIdChanged) {
|
|
361
|
+
for (const [queryId, subscription] of this._subscriptions) {
|
|
362
|
+
this.sendSubscribe(queryId, subscription);
|
|
363
|
+
}
|
|
297
364
|
}
|
|
298
365
|
this._pendingSubscriptions.clear();
|
|
299
366
|
}
|
|
@@ -316,14 +383,17 @@ export class ServerSentEventHubConnection implements IObservableQueryHubConnecti
|
|
|
316
383
|
return;
|
|
317
384
|
}
|
|
318
385
|
|
|
319
|
-
this._keepAlive.reconfigure(
|
|
386
|
+
this._keepAlive.reconfigure(
|
|
387
|
+
serverIntervalMs,
|
|
388
|
+
serverIntervalMs * IDLE_THRESHOLD_FACTOR,
|
|
389
|
+
);
|
|
320
390
|
}
|
|
321
391
|
|
|
322
392
|
private handleQueryResult(message: HubMessage): void {
|
|
323
393
|
if (!message.queryId) return;
|
|
324
394
|
|
|
325
395
|
const sub = this._subscriptions.get(message.queryId);
|
|
326
|
-
if (!sub) return;
|
|
396
|
+
if (!sub || !this.isMessageForCurrentSubscription(message)) return;
|
|
327
397
|
|
|
328
398
|
const result = message.payload as QueryResult<any>;
|
|
329
399
|
sub.callback(result);
|
|
@@ -333,25 +403,37 @@ export class ServerSentEventHubConnection implements IObservableQueryHubConnecti
|
|
|
333
403
|
if (!message.queryId) return;
|
|
334
404
|
|
|
335
405
|
const sub = this._subscriptions.get(message.queryId);
|
|
336
|
-
if (!sub) return;
|
|
406
|
+
if (!sub || !this.isMessageForCurrentSubscription(message)) return;
|
|
337
407
|
|
|
338
408
|
this._subscriptions.delete(message.queryId);
|
|
339
409
|
this._pendingSubscriptions.delete(message.queryId);
|
|
340
410
|
sub.callback(QueryResult.unauthorized());
|
|
341
411
|
}
|
|
342
412
|
|
|
343
|
-
private sendSubscribe(
|
|
413
|
+
private sendSubscribe(
|
|
414
|
+
queryId: string,
|
|
415
|
+
subscription: ActiveSubscription,
|
|
416
|
+
attempt: number = 0,
|
|
417
|
+
): void {
|
|
344
418
|
if (!this._connectionId || this._disconnected) return;
|
|
345
419
|
|
|
346
|
-
// Capture the connection ID so retries can detect
|
|
347
|
-
//
|
|
420
|
+
// Capture the connection ID and subscription instance so retries can detect whether
|
|
421
|
+
// either has been replaced while this request is in flight.
|
|
348
422
|
const connectionId = this._connectionId;
|
|
349
423
|
|
|
350
|
-
const body
|
|
424
|
+
const body: {
|
|
425
|
+
connectionId: string;
|
|
426
|
+
queryId: string;
|
|
427
|
+
request: SubscriptionRequest;
|
|
428
|
+
revision?: number;
|
|
429
|
+
} = {
|
|
351
430
|
connectionId,
|
|
352
431
|
queryId,
|
|
353
|
-
request,
|
|
432
|
+
request: subscription.request,
|
|
354
433
|
};
|
|
434
|
+
if (this._supportsSubscriptionRevisions) {
|
|
435
|
+
body.revision = subscription.revision;
|
|
436
|
+
}
|
|
355
437
|
|
|
356
438
|
const customHeaders = Globals.httpHeadersCallback?.() ?? {};
|
|
357
439
|
|
|
@@ -368,32 +450,137 @@ export class ServerSentEventHubConnection implements IObservableQueryHubConnecti
|
|
|
368
450
|
method: 'POST',
|
|
369
451
|
headers: { 'Content-Type': 'application/json', ...customHeaders },
|
|
370
452
|
body: JSON.stringify(body),
|
|
371
|
-
})
|
|
372
|
-
|
|
373
|
-
if (
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
453
|
+
})
|
|
454
|
+
.then((response) => {
|
|
455
|
+
if (!response.ok) {
|
|
456
|
+
if (
|
|
457
|
+
!this.isCurrentSubscription(queryId, subscription, connectionId)
|
|
458
|
+
) {
|
|
459
|
+
console.warn(
|
|
460
|
+
'SSE hub: subscribe POST returned a failure for an obsolete connection or subscription; ignoring',
|
|
461
|
+
queryId,
|
|
462
|
+
response.status,
|
|
463
|
+
);
|
|
464
|
+
} else if (attempt < maxRetries) {
|
|
465
|
+
setTimeout(
|
|
466
|
+
() => {
|
|
467
|
+
if (
|
|
468
|
+
this.isCurrentSubscription(
|
|
469
|
+
queryId,
|
|
470
|
+
subscription,
|
|
471
|
+
connectionId,
|
|
472
|
+
)
|
|
473
|
+
) {
|
|
474
|
+
this.sendSubscribe(
|
|
475
|
+
queryId,
|
|
476
|
+
subscription,
|
|
477
|
+
attempt + 1,
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
retryDelayMs * (attempt + 1),
|
|
482
|
+
);
|
|
483
|
+
} else {
|
|
484
|
+
console.warn(
|
|
485
|
+
'SSE hub: subscribe POST failed after retries; reconnecting',
|
|
486
|
+
queryId,
|
|
487
|
+
response.status,
|
|
488
|
+
attempt + 1,
|
|
489
|
+
);
|
|
490
|
+
this.reconnect();
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
})
|
|
494
|
+
.catch((error) => {
|
|
495
|
+
if (!this.isCurrentSubscription(queryId, subscription, connectionId)) {
|
|
496
|
+
console.warn(
|
|
497
|
+
'SSE hub: subscribe POST failed for an obsolete connection or subscription; ignoring',
|
|
498
|
+
queryId,
|
|
499
|
+
error,
|
|
500
|
+
);
|
|
501
|
+
} else if (attempt < maxRetries) {
|
|
502
|
+
setTimeout(
|
|
503
|
+
() => {
|
|
504
|
+
if (
|
|
505
|
+
this.isCurrentSubscription(
|
|
506
|
+
queryId,
|
|
507
|
+
subscription,
|
|
508
|
+
connectionId,
|
|
509
|
+
)
|
|
510
|
+
) {
|
|
511
|
+
this.sendSubscribe(queryId, subscription, attempt + 1);
|
|
512
|
+
}
|
|
513
|
+
},
|
|
514
|
+
retryDelayMs * (attempt + 1),
|
|
515
|
+
);
|
|
516
|
+
} else {
|
|
517
|
+
console.error(
|
|
518
|
+
'SSE hub: subscribe POST failed after retries; reconnecting',
|
|
519
|
+
queryId,
|
|
520
|
+
attempt + 1,
|
|
521
|
+
error,
|
|
522
|
+
);
|
|
377
523
|
this.reconnect();
|
|
378
524
|
}
|
|
379
|
-
}
|
|
380
|
-
}).catch(error => {
|
|
381
|
-
if (attempt < maxRetries && this._connectionId === connectionId && !this._disconnected) {
|
|
382
|
-
setTimeout(() => this.sendSubscribe(queryId, request, attempt + 1), retryDelayMs * (attempt + 1));
|
|
383
|
-
} else if (this._connectionId === connectionId && !this._disconnected) {
|
|
384
|
-
console.error(`SSE hub: subscribe POST failed for '${queryId}' after ${attempt + 1} attempt(s), reconnecting`, error);
|
|
385
|
-
this.reconnect();
|
|
386
|
-
}
|
|
387
|
-
});
|
|
525
|
+
});
|
|
388
526
|
}
|
|
389
527
|
|
|
390
|
-
private
|
|
528
|
+
private isMessageForCurrentSubscription(message: HubMessage): boolean {
|
|
529
|
+
if (!message.queryId) return false;
|
|
530
|
+
|
|
531
|
+
const subscription = this._subscriptions.get(message.queryId);
|
|
532
|
+
if (!subscription) return false;
|
|
533
|
+
|
|
534
|
+
if (!this._supportsSubscriptionRevisions) {
|
|
535
|
+
return message.revision === undefined;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
return (
|
|
539
|
+
this.isValidRevision(message.revision) &&
|
|
540
|
+
message.revision === subscription.revision
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
private isCurrentSubscription(
|
|
545
|
+
queryId: string,
|
|
546
|
+
subscription: ActiveSubscription,
|
|
547
|
+
connectionId: string,
|
|
548
|
+
): boolean {
|
|
549
|
+
return (
|
|
550
|
+
!this._disconnected &&
|
|
551
|
+
this._connectionId === connectionId &&
|
|
552
|
+
this._subscriptions.get(queryId) === subscription
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
private getNextRevision(): number {
|
|
557
|
+
if (this._nextRevision >= Number.MAX_SAFE_INTEGER) {
|
|
558
|
+
throw new RangeError(
|
|
559
|
+
'SSE hub subscription revision exhausted the safe integer range',
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
return ++this._nextRevision;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
private isValidRevision(revision: number | undefined): revision is number {
|
|
567
|
+
return revision !== undefined && Number.isSafeInteger(revision) && revision > 0;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
private sendUnsubscribe(queryId: string, revision: number): void {
|
|
391
571
|
if (!this._connectionId) return;
|
|
392
572
|
|
|
393
|
-
const body
|
|
573
|
+
const body: {
|
|
574
|
+
connectionId: string;
|
|
575
|
+
queryId: string;
|
|
576
|
+
revision?: number;
|
|
577
|
+
} = {
|
|
394
578
|
connectionId: this._connectionId,
|
|
395
579
|
queryId,
|
|
396
580
|
};
|
|
581
|
+
if (this._supportsSubscriptionRevisions) {
|
|
582
|
+
body.revision = revision;
|
|
583
|
+
}
|
|
397
584
|
|
|
398
585
|
const customHeaders = Globals.httpHeadersCallback?.() ?? {};
|
|
399
586
|
|
|
@@ -401,8 +588,8 @@ export class ServerSentEventHubConnection implements IObservableQueryHubConnecti
|
|
|
401
588
|
method: 'POST',
|
|
402
589
|
headers: { 'Content-Type': 'application/json', ...customHeaders },
|
|
403
590
|
body: JSON.stringify(body),
|
|
404
|
-
}).catch(error => {
|
|
405
|
-
console.error(
|
|
591
|
+
}).catch((error) => {
|
|
592
|
+
console.error('SSE hub: unsubscribe POST failed', queryId, error);
|
|
406
593
|
});
|
|
407
594
|
}
|
|
408
595
|
}
|