@cratis/arc 22.0.1 → 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.
Files changed (205) hide show
  1. package/dist/cjs/queries/ObservableQueryFor.js +9 -4
  2. package/dist/cjs/queries/ObservableQueryFor.js.map +1 -1
  3. package/dist/cjs/queries/QueryInstanceCache.js +6 -6
  4. package/dist/cjs/queries/QueryInstanceCache.js.map +1 -1
  5. package/dist/cjs/queries/QueryResult.js +23 -4
  6. package/dist/cjs/queries/QueryResult.js.map +1 -1
  7. package/dist/cjs/queries/QueryResultWithState.js +20 -7
  8. package/dist/cjs/queries/QueryResultWithState.js.map +1 -1
  9. package/dist/cjs/queries/ServerSentEventHubConnection.js +120 -58
  10. package/dist/cjs/queries/ServerSentEventHubConnection.js.map +1 -1
  11. package/dist/cjs/queries/WebSocketHubConnection.js +121 -51
  12. package/dist/cjs/queries/WebSocketHubConnection.js.map +1 -1
  13. package/dist/esm/queries/IQueryResult.d.ts +1 -0
  14. package/dist/esm/queries/IQueryResult.d.ts.map +1 -1
  15. package/dist/esm/queries/ObservableQueryFor.d.ts +6 -6
  16. package/dist/esm/queries/ObservableQueryFor.d.ts.map +1 -1
  17. package/dist/esm/queries/ObservableQueryFor.js +9 -4
  18. package/dist/esm/queries/ObservableQueryFor.js.map +1 -1
  19. package/dist/esm/queries/QueryInstanceCache.d.ts +1 -1
  20. package/dist/esm/queries/QueryInstanceCache.d.ts.map +1 -1
  21. package/dist/esm/queries/QueryInstanceCache.js +6 -6
  22. package/dist/esm/queries/QueryInstanceCache.js.map +1 -1
  23. package/dist/esm/queries/QueryResult.d.ts +2 -0
  24. package/dist/esm/queries/QueryResult.d.ts.map +1 -1
  25. package/dist/esm/queries/QueryResult.js +23 -4
  26. package/dist/esm/queries/QueryResult.js.map +1 -1
  27. package/dist/esm/queries/QueryResultWithState.d.ts +3 -3
  28. package/dist/esm/queries/QueryResultWithState.d.ts.map +1 -1
  29. package/dist/esm/queries/QueryResultWithState.js +20 -7
  30. package/dist/esm/queries/QueryResultWithState.js.map +1 -1
  31. package/dist/esm/queries/ServerSentEventHubConnection.d.ts +11 -4
  32. package/dist/esm/queries/ServerSentEventHubConnection.d.ts.map +1 -1
  33. package/dist/esm/queries/ServerSentEventHubConnection.js +120 -58
  34. package/dist/esm/queries/ServerSentEventHubConnection.js.map +1 -1
  35. package/dist/esm/queries/WebSocketHubConnection.d.ts +13 -2
  36. package/dist/esm/queries/WebSocketHubConnection.d.ts.map +1 -1
  37. package/dist/esm/queries/WebSocketHubConnection.js +121 -51
  38. package/dist/esm/queries/WebSocketHubConnection.js.map +1 -1
  39. package/dist/esm/queries/for_ObservableQueryFor/given/TestQueries.d.ts +17 -0
  40. package/dist/esm/queries/for_ObservableQueryFor/given/TestQueries.d.ts.map +1 -1
  41. package/dist/esm/queries/for_ObservableQueryFor/given/TestQueries.js +34 -0
  42. package/dist/esm/queries/for_ObservableQueryFor/given/TestQueries.js.map +1 -1
  43. package/dist/esm/queries/for_ObservableQueryFor/given/an_observable_query_for.d.ts +2 -1
  44. package/dist/esm/queries/for_ObservableQueryFor/given/an_observable_query_for.d.ts.map +1 -1
  45. package/dist/esm/queries/for_ObservableQueryFor/given/an_observable_query_for.js +3 -1
  46. package/dist/esm/queries/for_ObservableQueryFor/given/an_observable_query_for.js.map +1 -1
  47. package/dist/esm/queries/for_ObservableQueryFor/when_performing/with_a_not_ready_result.d.ts +2 -0
  48. package/dist/esm/queries/for_ObservableQueryFor/when_performing/with_a_not_ready_result.d.ts.map +1 -0
  49. package/dist/esm/queries/for_ObservableQueryFor/when_performing/with_a_not_ready_result.js +34 -0
  50. package/dist/esm/queries/for_ObservableQueryFor/when_performing/with_a_not_ready_result.js.map +1 -0
  51. package/dist/esm/queries/for_ObservableQueryFor/when_receiving_a_result/and_the_model_has_a_calendar_collection.d.ts +2 -0
  52. package/dist/esm/queries/for_ObservableQueryFor/when_receiving_a_result/and_the_model_has_a_calendar_collection.d.ts.map +1 -0
  53. package/dist/esm/queries/for_ObservableQueryFor/when_receiving_a_result/and_the_model_has_a_calendar_collection.js +52 -0
  54. package/dist/esm/queries/for_ObservableQueryFor/when_receiving_a_result/and_the_model_has_a_calendar_collection.js.map +1 -0
  55. package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_direct_mode_and_sse_transport.js +26 -2
  56. package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_direct_mode_and_sse_transport.js.map +1 -1
  57. package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_direct_mode_disabled_and_sse_transport.js +60 -3
  58. package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_direct_mode_disabled_and_sse_transport.js.map +1 -1
  59. package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_web_socket_hub_payload_without_readiness.d.ts +2 -0
  60. package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_web_socket_hub_payload_without_readiness.d.ts.map +1 -0
  61. package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_web_socket_hub_payload_without_readiness.js +83 -0
  62. package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_web_socket_hub_payload_without_readiness.js.map +1 -0
  63. package/dist/esm/queries/for_QueryInstanceCache/when_setting_last_result/with_only_readiness_changed.d.ts +2 -0
  64. package/dist/esm/queries/for_QueryInstanceCache/when_setting_last_result/with_only_readiness_changed.d.ts.map +1 -0
  65. package/dist/esm/queries/for_QueryInstanceCache/when_setting_last_result/with_only_readiness_changed.js +22 -0
  66. package/dist/esm/queries/for_QueryInstanceCache/when_setting_last_result/with_only_readiness_changed.js.map +1 -0
  67. package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/with_readiness_false.d.ts +2 -0
  68. package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/with_readiness_false.d.ts.map +1 -0
  69. package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/with_readiness_false.js +17 -0
  70. package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/with_readiness_false.js.map +1 -0
  71. package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/without_readiness.d.ts +2 -0
  72. package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/without_readiness.d.ts.map +1 -0
  73. package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/without_readiness.js +16 -0
  74. package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/without_readiness.js.map +1 -0
  75. package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/with_readiness_false.d.ts +2 -0
  76. package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/with_readiness_false.d.ts.map +1 -0
  77. package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/with_readiness_false.js +19 -0
  78. package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/with_readiness_false.js.map +1 -0
  79. package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/without_readiness.d.ts +2 -0
  80. package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/without_readiness.d.ts.map +1 -0
  81. package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/without_readiness.js +19 -0
  82. package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/without_readiness.js.map +1 -0
  83. package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_deferred_promise.d.ts +7 -0
  84. package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_deferred_promise.d.ts.map +1 -0
  85. package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_deferred_promise.js +12 -0
  86. package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_deferred_promise.js.map +1 -0
  87. package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_server_sent_event_hub_connection.d.ts +2 -1
  88. package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_server_sent_event_hub_connection.d.ts.map +1 -1
  89. package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_server_sent_event_hub_connection.js +15 -4
  90. package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_server_sent_event_hub_connection.js.map +1 -1
  91. package/dist/esm/queries/for_ServerSentEventHubConnection/when_a_retired_event_source_fires/ignores_connected_and_error_callbacks.d.ts +2 -0
  92. package/dist/esm/queries/for_ServerSentEventHubConnection/when_a_retired_event_source_fires/ignores_connected_and_error_callbacks.d.ts.map +1 -0
  93. package/dist/esm/queries/for_ServerSentEventHubConnection/when_a_retired_event_source_fires/ignores_connected_and_error_callbacks.js +44 -0
  94. package/dist/esm/queries/for_ServerSentEventHubConnection/when_a_retired_event_source_fires/ignores_connected_and_error_callbacks.js.map +1 -0
  95. package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_replaced.d.ts +2 -0
  96. package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_replaced.d.ts.map +1 -0
  97. package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_replaced.js +74 -0
  98. package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_replaced.js.map +1 -0
  99. package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_unsubscribed.d.ts +2 -0
  100. package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_unsubscribed.d.ts.map +1 -0
  101. package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_unsubscribed.js +74 -0
  102. package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_unsubscribed.js.map +1 -0
  103. package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_is_rejected.d.ts +2 -0
  104. 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
  105. package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_is_rejected.js +59 -0
  106. package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_is_rejected.js.map +1 -0
  107. 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
  108. 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
  109. package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_returns_non_ok.js +58 -0
  110. 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
  111. package/dist/esm/queries/for_ServerSentEventHubConnection/when_disposing/cleans_up_resources.js +6 -3
  112. package/dist/esm/queries/for_ServerSentEventHubConnection/when_disposing/cleans_up_resources.js.map +1 -1
  113. package/dist/esm/queries/for_ServerSentEventHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.js +23 -6
  114. package/dist/esm/queries/for_ServerSentEventHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.js.map +1 -1
  115. package/dist/esm/queries/for_ServerSentEventHubConnection/when_reconnecting/after_error.js +20 -5
  116. package/dist/esm/queries/for_ServerSentEventHubConnection/when_reconnecting/after_error.js.map +1 -1
  117. package/dist/esm/queries/for_ServerSentEventHubConnection/when_reconnecting/and_an_immediate_open_wins_the_backoff_race.d.ts +2 -0
  118. package/dist/esm/queries/for_ServerSentEventHubConnection/when_reconnecting/and_an_immediate_open_wins_the_backoff_race.d.ts.map +1 -0
  119. package/dist/esm/queries/for_ServerSentEventHubConnection/when_reconnecting/and_an_immediate_open_wins_the_backoff_race.js +20 -0
  120. package/dist/esm/queries/for_ServerSentEventHubConnection/when_reconnecting/and_an_immediate_open_wins_the_backoff_race.js.map +1 -0
  121. package/dist/esm/queries/for_ServerSentEventHubConnection/when_replacing_a_subscription/ignores_messages_from_the_retired_generation.d.ts +2 -0
  122. package/dist/esm/queries/for_ServerSentEventHubConnection/when_replacing_a_subscription/ignores_messages_from_the_retired_generation.d.ts.map +1 -0
  123. package/dist/esm/queries/for_ServerSentEventHubConnection/when_replacing_a_subscription/ignores_messages_from_the_retired_generation.js +68 -0
  124. package/dist/esm/queries/for_ServerSentEventHubConnection/when_replacing_a_subscription/ignores_messages_from_the_retired_generation.js.map +1 -0
  125. package/dist/esm/queries/for_ServerSentEventHubConnection/when_subscribing/sends_subscribe_post.js +38 -13
  126. package/dist/esm/queries/for_ServerSentEventHubConnection/when_subscribing/sends_subscribe_post.js.map +1 -1
  127. package/dist/esm/queries/for_ServerSentEventHubConnection/when_subscription_revisions_are_not_advertised/uses_legacy_protocol.d.ts +2 -0
  128. package/dist/esm/queries/for_ServerSentEventHubConnection/when_subscription_revisions_are_not_advertised/uses_legacy_protocol.d.ts.map +1 -0
  129. package/dist/esm/queries/for_ServerSentEventHubConnection/when_subscription_revisions_are_not_advertised/uses_legacy_protocol.js +54 -0
  130. package/dist/esm/queries/for_ServerSentEventHubConnection/when_subscription_revisions_are_not_advertised/uses_legacy_protocol.js.map +1 -0
  131. package/dist/esm/queries/for_ServerSentEventHubConnection/when_unsubscribing/the_only_query.js +18 -4
  132. package/dist/esm/queries/for_ServerSentEventHubConnection/when_unsubscribing/the_only_query.js.map +1 -1
  133. package/dist/esm/queries/for_WebSocketHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.js +22 -5
  134. package/dist/esm/queries/for_WebSocketHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.js.map +1 -1
  135. package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/ignores_a_pending_reconnect_and_stale_socket_callbacks.d.ts +2 -0
  136. package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/ignores_a_pending_reconnect_and_stale_socket_callbacks.d.ts.map +1 -0
  137. package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/ignores_a_pending_reconnect_and_stale_socket_callbacks.js +108 -0
  138. package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/ignores_a_pending_reconnect_and_stale_socket_callbacks.js.map +1 -0
  139. package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/replaces_a_closing_socket_safely.d.ts +2 -0
  140. package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/replaces_a_closing_socket_safely.d.ts.map +1 -0
  141. package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/replaces_a_closing_socket_safely.js +33 -0
  142. package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/replaces_a_closing_socket_safely.js.map +1 -0
  143. package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/resubscribes_all_queries.js +26 -4
  144. package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/resubscribes_all_queries.js.map +1 -1
  145. package/dist/esm/queries/for_WebSocketHubConnection/when_replacing_a_subscription/ignores_stale_result_unauthorized_and_error.d.ts +2 -0
  146. package/dist/esm/queries/for_WebSocketHubConnection/when_replacing_a_subscription/ignores_stale_result_unauthorized_and_error.d.ts.map +1 -0
  147. package/dist/esm/queries/for_WebSocketHubConnection/when_replacing_a_subscription/ignores_stale_result_unauthorized_and_error.js +62 -0
  148. package/dist/esm/queries/for_WebSocketHubConnection/when_replacing_a_subscription/ignores_stale_result_unauthorized_and_error.js.map +1 -0
  149. package/dist/esm/queries/for_WebSocketHubConnection/when_subscribing/to_a_query.js +18 -5
  150. package/dist/esm/queries/for_WebSocketHubConnection/when_subscribing/to_a_query.js.map +1 -1
  151. package/dist/esm/queries/for_WebSocketHubConnection/when_subscription_revisions_are_supported/upgrades_active_subscriptions_to_strict_mode.d.ts +2 -0
  152. package/dist/esm/queries/for_WebSocketHubConnection/when_subscription_revisions_are_supported/upgrades_active_subscriptions_to_strict_mode.d.ts.map +1 -0
  153. package/dist/esm/queries/for_WebSocketHubConnection/when_subscription_revisions_are_supported/upgrades_active_subscriptions_to_strict_mode.js +56 -0
  154. package/dist/esm/queries/for_WebSocketHubConnection/when_subscription_revisions_are_supported/upgrades_active_subscriptions_to_strict_mode.js.map +1 -0
  155. package/dist/esm/queries/for_WebSocketHubConnection/when_unsubscribing/the_only_query.js +16 -3
  156. package/dist/esm/queries/for_WebSocketHubConnection/when_unsubscribing/the_only_query.js.map +1 -1
  157. package/dist/esm/queries/for_WebSocketHubConnection/when_unsubscribing/with_revision_support_not_advertised.d.ts +2 -0
  158. package/dist/esm/queries/for_WebSocketHubConnection/when_unsubscribing/with_revision_support_not_advertised.d.ts.map +1 -0
  159. package/dist/esm/queries/for_WebSocketHubConnection/when_unsubscribing/with_revision_support_not_advertised.js +33 -0
  160. package/dist/esm/queries/for_WebSocketHubConnection/when_unsubscribing/with_revision_support_not_advertised.js.map +1 -0
  161. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  162. package/package.json +1 -1
  163. package/queries/IQueryResult.ts +10 -0
  164. package/queries/ObservableQueryFor.ts +54 -26
  165. package/queries/QueryInstanceCache.ts +42 -19
  166. package/queries/QueryResult.ts +145 -79
  167. package/queries/QueryResultWithState.ts +32 -9
  168. package/queries/ServerSentEventHubConnection.ts +270 -83
  169. package/queries/WebSocketHubConnection.ts +188 -56
  170. package/queries/for_ObservableQueryFor/given/TestQueries.ts +30 -1
  171. package/queries/for_ObservableQueryFor/given/an_observable_query_for.ts +3 -0
  172. package/queries/for_ObservableQueryFor/when_performing/with_a_not_ready_result.ts +46 -0
  173. package/queries/for_ObservableQueryFor/when_receiving_a_result/and_the_model_has_a_calendar_collection.ts +76 -0
  174. package/queries/for_ObservableQueryFor/when_subscribing/with_direct_mode_and_sse_transport.ts +91 -57
  175. package/queries/for_ObservableQueryFor/when_subscribing/with_direct_mode_disabled_and_sse_transport.ts +125 -53
  176. package/queries/for_ObservableQueryFor/when_subscribing/with_web_socket_hub_payload_without_readiness.ts +107 -0
  177. package/queries/for_QueryInstanceCache/when_setting_last_result/with_only_readiness_changed.ts +63 -0
  178. package/queries/for_QueryResult/when_constructing_from_a_server_result/with_readiness_false.ts +25 -0
  179. package/queries/for_QueryResult/when_constructing_from_a_server_result/without_readiness.ts +24 -0
  180. package/queries/for_QueryResultWithState/when_creating_from_query_result/with_readiness_false.ts +28 -0
  181. package/queries/for_QueryResultWithState/when_creating_from_query_result/without_readiness.ts +25 -0
  182. package/queries/for_ServerSentEventHubConnection/given/a_deferred_promise.ts +15 -0
  183. package/queries/for_ServerSentEventHubConnection/given/a_server_sent_event_hub_connection.ts +18 -7
  184. package/queries/for_ServerSentEventHubConnection/when_a_retired_event_source_fires/ignores_connected_and_error_callbacks.ts +59 -0
  185. package/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_replaced.ts +113 -0
  186. package/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_unsubscribed.ts +109 -0
  187. package/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_is_rejected.ts +81 -0
  188. package/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_returns_non_ok.ts +83 -0
  189. package/queries/for_ServerSentEventHubConnection/when_disposing/cleans_up_resources.ts +30 -21
  190. package/queries/for_ServerSentEventHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.ts +56 -32
  191. package/queries/for_ServerSentEventHubConnection/when_reconnecting/after_error.ts +23 -6
  192. package/queries/for_ServerSentEventHubConnection/when_reconnecting/and_an_immediate_open_wins_the_backoff_race.ts +41 -0
  193. package/queries/for_ServerSentEventHubConnection/when_replacing_a_subscription/ignores_messages_from_the_retired_generation.ts +92 -0
  194. package/queries/for_ServerSentEventHubConnection/when_subscribing/sends_subscribe_post.ts +69 -30
  195. package/queries/for_ServerSentEventHubConnection/when_subscription_revisions_are_not_advertised/uses_legacy_protocol.ts +69 -0
  196. package/queries/for_ServerSentEventHubConnection/when_unsubscribing/the_only_query.ts +43 -24
  197. package/queries/for_WebSocketHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.ts +53 -29
  198. package/queries/for_WebSocketHubConnection/when_reconnecting/ignores_a_pending_reconnect_and_stale_socket_callbacks.ts +131 -0
  199. package/queries/for_WebSocketHubConnection/when_reconnecting/replaces_a_closing_socket_safely.ts +39 -0
  200. package/queries/for_WebSocketHubConnection/when_reconnecting/resubscribes_all_queries.ts +72 -36
  201. package/queries/for_WebSocketHubConnection/when_replacing_a_subscription/ignores_stale_result_unauthorized_and_error.ts +88 -0
  202. package/queries/for_WebSocketHubConnection/when_subscribing/to_a_query.ts +54 -27
  203. package/queries/for_WebSocketHubConnection/when_subscription_revisions_are_supported/upgrades_active_subscriptions_to_strict_mode.ts +72 -0
  204. package/queries/for_WebSocketHubConnection/when_unsubscribing/the_only_query.ts +43 -18
  205. package/queries/for_WebSocketHubConnection/when_unsubscribing/with_revision_support_not_advertised.ts +44 -0
@@ -16,6 +16,7 @@ export class a_server_sent_event_hub_connection {
16
16
  close: sinon.SinonStub;
17
17
  readyState: number;
18
18
  };
19
+ eventSources!: Array<typeof this.fakeEventSource>;
19
20
  policy!: sinon.SinonStubbedInstance<IReconnectPolicy>;
20
21
  fetchStub!: sinon.SinonStub;
21
22
 
@@ -29,20 +30,28 @@ export class a_server_sent_event_hub_connection {
29
30
  * accumulation across tests sharing the same context instance.
30
31
  */
31
32
  setup(): void {
33
+ this.eventSources = [];
32
34
  this.fakeEventSource = {
33
35
  onopen: null,
34
36
  onmessage: null,
35
37
  onerror: null,
36
38
  close: sinon.stub(),
37
- readyState: 0, // CONNECTING
39
+ readyState: 0,
38
40
  };
39
41
 
40
42
  // eslint-disable-next-line @typescript-eslint/no-this-alias
41
43
  const self = this;
42
44
  const FakeEventSourceClass = function (this: any) {
43
- Object.assign(self.fakeEventSource, { onopen: null, onmessage: null, onerror: null });
44
- self.fakeEventSource.readyState = 0; // CONNECTING
45
- return self.fakeEventSource;
45
+ const eventSource = {
46
+ onopen: null,
47
+ onmessage: null,
48
+ onerror: null,
49
+ close: sinon.stub(),
50
+ readyState: 0, // CONNECTING
51
+ };
52
+ self.eventSources.push(eventSource);
53
+ self.fakeEventSource = eventSource;
54
+ return eventSource;
46
55
  };
47
56
  (globalThis as any)['EventSource'] = FakeEventSourceClass;
48
57
  (globalThis as any)['EventSource'].CONNECTING = 0;
@@ -66,7 +75,7 @@ export class a_server_sent_event_hub_connection {
66
75
  '',
67
76
  undefined,
68
77
  undefined,
69
- this.policy
78
+ this.policy,
70
79
  );
71
80
  }
72
81
 
@@ -75,8 +84,10 @@ export class a_server_sent_event_hub_connection {
75
84
  this.fakeEventSource.onopen?.();
76
85
  }
77
86
 
78
- simulateMessage(payload: object): void {
79
- this.fakeEventSource.onmessage?.({ data: JSON.stringify(payload) } as MessageEvent);
87
+ simulateMessage(payload: Record<string, unknown>): void {
88
+ this.fakeEventSource.onmessage?.({
89
+ data: JSON.stringify(payload),
90
+ } as MessageEvent);
80
91
  }
81
92
 
82
93
  simulateError(): void {
@@ -0,0 +1,59 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import sinon from 'sinon';
5
+ import { a_server_sent_event_hub_connection } from '../given/a_server_sent_event_hub_connection';
6
+ import { given } from '../../../given';
7
+ import { HubMessageType } from '../../WebSocketHubConnection';
8
+
9
+ describe(
10
+ 'when a retired EventSource fires connected and error callbacks',
11
+ given(a_server_sent_event_hub_connection, (context) => {
12
+ let staleMessage: (event: MessageEvent) => void;
13
+ let staleError: () => void;
14
+ let currentSource: typeof context.fakeEventSource;
15
+
16
+ beforeEach(() => {
17
+ context.setup();
18
+ sinon.stub(console, 'warn');
19
+ context.connection.subscribe('q1', { queryName: 'MyQuery' }, sinon.stub());
20
+ context.simulateOpen();
21
+ context.simulateMessage({
22
+ type: HubMessageType.Connected,
23
+ payload: 'connection-a',
24
+ });
25
+
26
+ const retiredSource = context.eventSources[0];
27
+ staleMessage = retiredSource.onmessage!;
28
+ staleError = retiredSource.onerror!;
29
+ staleError();
30
+
31
+ const reconnect = (context.policy.schedule as sinon.SinonStub).firstCall
32
+ .args[0] as () => void;
33
+ reconnect();
34
+ currentSource = context.eventSources[1];
35
+ context.simulateOpen();
36
+ context.simulateMessage({
37
+ type: HubMessageType.Connected,
38
+ payload: 'connection-b',
39
+ });
40
+
41
+ staleMessage({
42
+ data: JSON.stringify({
43
+ type: HubMessageType.Connected,
44
+ payload: 'stale-connection',
45
+ }),
46
+ } as MessageEvent);
47
+ staleError();
48
+ });
49
+
50
+ afterEach(() => sinon.restore());
51
+
52
+ it('should not send another subscription for stale connected', () =>
53
+ context.fetchStub.callCount.should.equal(2));
54
+ it('should keep the current EventSource open', () =>
55
+ currentSource.close.called.should.be.false);
56
+ it('should not schedule another reconnect for stale error', () =>
57
+ (context.policy.schedule as sinon.SinonStub).calledOnce.should.be.true);
58
+ }),
59
+ );
@@ -0,0 +1,113 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import sinon from 'sinon';
5
+ import { a_deferred_promise } from '../given/a_deferred_promise';
6
+ import { a_server_sent_event_hub_connection } from '../given/a_server_sent_event_hub_connection';
7
+ import { given } from '../../../given';
8
+ import { HubMessageType } from '../../WebSocketHubConnection';
9
+
10
+ describe(
11
+ 'when a query is replaced while its subscribe request is in flight and the connection changes',
12
+ given(a_server_sent_event_hub_connection, (context) => {
13
+ const queryId = 'q1';
14
+ const replacementRequest = { queryName: 'ReplacementQuery' };
15
+ let clock: sinon.SinonFakeTimers;
16
+ let originalCallback: sinon.SinonStub;
17
+ let replacementCallback: sinon.SinonStub;
18
+
19
+ beforeEach(async () => {
20
+ context.setup();
21
+ clock = sinon.useFakeTimers({ toFake: ['setTimeout'] });
22
+ sinon.stub(console, 'warn');
23
+ const firstSubscribe = new a_deferred_promise<{
24
+ ok: boolean;
25
+ status: number;
26
+ }>();
27
+ context.fetchStub.onFirstCall().returns(firstSubscribe.promise);
28
+ originalCallback = sinon.stub();
29
+ replacementCallback = sinon.stub();
30
+
31
+ context.connection.subscribe(
32
+ queryId,
33
+ { queryName: 'OriginalQuery' },
34
+ originalCallback,
35
+ );
36
+ context.simulateOpen();
37
+ context.simulateMessage({
38
+ type: HubMessageType.Connected,
39
+ payload: 'connection-a',
40
+ supportsSubscriptionRevisions: true,
41
+ });
42
+ context.connection.subscribe(
43
+ queryId,
44
+ replacementRequest,
45
+ replacementCallback,
46
+ );
47
+ context.simulateMessage({
48
+ type: HubMessageType.Connected,
49
+ payload: 'connection-b',
50
+ supportsSubscriptionRevisions: true,
51
+ });
52
+
53
+ firstSubscribe.resolve({ ok: false, status: 404 });
54
+ await Promise.resolve();
55
+ clock.tick(1000);
56
+ await Promise.resolve();
57
+ context.simulateMessage({
58
+ type: HubMessageType.QueryResult,
59
+ queryId,
60
+ revision: getSubscribeBody(2).revision,
61
+ payload: { isSuccess: true },
62
+ });
63
+ });
64
+
65
+ afterEach(() => {
66
+ clock.restore();
67
+ sinon.restore();
68
+ });
69
+
70
+ it('should send only the replacement on connection B', () => {
71
+ const body = getSubscribeBody(2);
72
+ body.connectionId.should.equal('connection-b');
73
+ body.queryId.should.equal(queryId);
74
+ body.request.should.deep.equal(replacementRequest);
75
+ body.revision.should.be.greaterThan(0);
76
+ });
77
+
78
+ it('should not retry the obsolete subscription', () =>
79
+ context.fetchStub.callCount.should.equal(3));
80
+
81
+ it('should notify only the replacement callback', () => {
82
+ originalCallback.called.should.equal(false);
83
+ replacementCallback.calledOnce.should.equal(true);
84
+ });
85
+
86
+ it('should not reconnect', () =>
87
+ (context.policy.schedule as sinon.SinonStub).called.should.be.false);
88
+
89
+ it('should retain one active subscription', () =>
90
+ context.connection.queryCount.should.equal(1));
91
+
92
+ function getSubscribeBody(callIndex: number): {
93
+ connectionId: string;
94
+ queryId: string;
95
+ request: object;
96
+ revision: number;
97
+ } {
98
+ const rawBody = context.fetchStub.getCall(callIndex).args[1].body as string;
99
+ try {
100
+ return JSON.parse(rawBody) as {
101
+ connectionId: string;
102
+ queryId: string;
103
+ request: object;
104
+ revision: number;
105
+ };
106
+ } catch (error) {
107
+ throw new Error('Expected a valid subscribe request body', {
108
+ cause: error,
109
+ });
110
+ }
111
+ }
112
+ }),
113
+ );
@@ -0,0 +1,109 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import sinon from 'sinon';
5
+ import { a_deferred_promise } from '../given/a_deferred_promise';
6
+ import { a_server_sent_event_hub_connection } from '../given/a_server_sent_event_hub_connection';
7
+ import { given } from '../../../given';
8
+ import { HubMessageType } from '../../WebSocketHubConnection';
9
+
10
+ const subscribeUrl = 'http://localhost/.cratis/queries/sse/subscribe';
11
+
12
+ describe(
13
+ 'when a query is unsubscribed while its subscribe request is in flight and the connection changes',
14
+ given(a_server_sent_event_hub_connection, (context) => {
15
+ let clock: sinon.SinonFakeTimers;
16
+
17
+ beforeEach(async () => {
18
+ context.setup();
19
+ clock = sinon.useFakeTimers({ toFake: ['setTimeout'] });
20
+ sinon.stub(console, 'warn');
21
+ const firstSubscribe = new a_deferred_promise<{
22
+ ok: boolean;
23
+ status: number;
24
+ }>();
25
+ context.fetchStub.onFirstCall().returns(firstSubscribe.promise);
26
+
27
+ context.connection.subscribe(
28
+ 'removed-query',
29
+ { queryName: 'RemovedQuery' },
30
+ sinon.stub(),
31
+ );
32
+ context.simulateOpen();
33
+ context.simulateMessage({
34
+ type: HubMessageType.Connected,
35
+ payload: 'connection-a',
36
+ supportsSubscriptionRevisions: true,
37
+ });
38
+ context.connection.subscribe(
39
+ 'active-query',
40
+ { queryName: 'ActiveQuery' },
41
+ sinon.stub(),
42
+ );
43
+ context.connection.unsubscribe('removed-query');
44
+ context.simulateMessage({
45
+ type: HubMessageType.Connected,
46
+ payload: 'connection-b',
47
+ supportsSubscriptionRevisions: true,
48
+ });
49
+
50
+ firstSubscribe.resolve({ ok: false, status: 404 });
51
+ await Promise.resolve();
52
+ clock.tick(1000);
53
+ await Promise.resolve();
54
+ });
55
+
56
+ afterEach(() => {
57
+ clock.restore();
58
+ sinon.restore();
59
+ });
60
+
61
+ it('should not subscribe the removed query on connection B', () => {
62
+ const removedQueryCalls = context.fetchStub
63
+ .getCalls()
64
+ .filter((call) => call.args[0] === subscribeUrl)
65
+ .filter((call) =>
66
+ (call.args[1].body as string).includes('"queryId":"removed-query"'),
67
+ );
68
+ removedQueryCalls.length.should.equal(1);
69
+ const body = parseBody(removedQueryCalls[0].args[1].body as string);
70
+ body.connectionId.should.equal('connection-a');
71
+ body.queryId.should.equal('removed-query');
72
+ body.request.should.deep.equal({ queryName: 'RemovedQuery' });
73
+ body.revision.should.be.greaterThan(0);
74
+ });
75
+
76
+ it('should subscribe the remaining query on connection B', () => {
77
+ const activeQueryBodies = context.fetchStub
78
+ .getCalls()
79
+ .filter((call) => call.args[0] === subscribeUrl)
80
+ .map((call) => parseBody(call.args[1].body as string))
81
+ .filter((body) => body.queryId === 'active-query');
82
+ activeQueryBodies
83
+ .map((body) => body.connectionId)
84
+ .should.deep.equal(['connection-a', 'connection-b']);
85
+ activeQueryBodies[0].revision.should.equal(activeQueryBodies[1].revision);
86
+ });
87
+
88
+ it('should not reconnect', () =>
89
+ (context.policy.schedule as sinon.SinonStub).called.should.be.false);
90
+
91
+ it('should retain only the remaining subscription', () =>
92
+ context.connection.queryCount.should.equal(1));
93
+
94
+ function parseBody(rawBody: string): {
95
+ connectionId: string;
96
+ queryId: string;
97
+ request: { queryName: string };
98
+ revision: number;
99
+ } {
100
+ try {
101
+ return JSON.parse(rawBody) as ReturnType<typeof parseBody>;
102
+ } catch (error) {
103
+ throw new Error('Expected a valid subscribe request body', {
104
+ cause: error,
105
+ });
106
+ }
107
+ }
108
+ }),
109
+ );
@@ -0,0 +1,81 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import sinon from 'sinon';
5
+ import { a_deferred_promise } from '../given/a_deferred_promise';
6
+ import { a_server_sent_event_hub_connection } from '../given/a_server_sent_event_hub_connection';
7
+ import { given } from '../../../given';
8
+ import { HubMessageType } from '../../WebSocketHubConnection';
9
+
10
+ describe(
11
+ 'when the connection changes while a subscribe request is in flight and the stale request is rejected',
12
+ given(a_server_sent_event_hub_connection, (context) => {
13
+ const queryId = 'q1';
14
+ const request = { queryName: 'MyQuery' };
15
+ let clock: sinon.SinonFakeTimers;
16
+
17
+ beforeEach(async () => {
18
+ context.setup();
19
+ clock = sinon.useFakeTimers({ toFake: ['setTimeout'] });
20
+ sinon.stub(console, 'warn');
21
+ const firstSubscribe = new a_deferred_promise<{ ok: boolean }>();
22
+ context.fetchStub.onFirstCall().returns(firstSubscribe.promise);
23
+
24
+ context.connection.subscribe(queryId, request, sinon.stub());
25
+ context.simulateOpen();
26
+ context.simulateMessage({
27
+ type: HubMessageType.Connected,
28
+ payload: 'connection-a',
29
+ supportsSubscriptionRevisions: true,
30
+ });
31
+ context.simulateMessage({
32
+ type: HubMessageType.Connected,
33
+ payload: 'connection-b',
34
+ supportsSubscriptionRevisions: true,
35
+ });
36
+
37
+ firstSubscribe.reject(new Error('Connection A failed'));
38
+ await Promise.resolve();
39
+ await Promise.resolve();
40
+ clock.tick(1000);
41
+ await Promise.resolve();
42
+ });
43
+
44
+ afterEach(() => {
45
+ clock.restore();
46
+ sinon.restore();
47
+ });
48
+
49
+ it('should send the active subscription on connection B', () => {
50
+ const body = parseBody(context.fetchStub.getCall(1).args[1].body as string);
51
+ body.connectionId.should.equal('connection-b');
52
+ body.queryId.should.equal(queryId);
53
+ body.request.should.deep.equal(request);
54
+ body.revision.should.be.greaterThan(0);
55
+ });
56
+
57
+ it('should not retry the obsolete connection A request', () =>
58
+ context.fetchStub.callCount.should.equal(2));
59
+
60
+ it('should not reconnect', () =>
61
+ (context.policy.schedule as sinon.SinonStub).called.should.be.false);
62
+
63
+ it('should retain the active subscription', () =>
64
+ context.connection.queryCount.should.equal(1));
65
+
66
+ function parseBody(rawBody: string): {
67
+ connectionId: string;
68
+ queryId: string;
69
+ request: object;
70
+ revision: number;
71
+ } {
72
+ try {
73
+ return JSON.parse(rawBody) as ReturnType<typeof parseBody>;
74
+ } catch (error) {
75
+ throw new Error('Expected a valid subscribe request body', {
76
+ cause: error,
77
+ });
78
+ }
79
+ }
80
+ }),
81
+ );
@@ -0,0 +1,83 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import sinon from 'sinon';
5
+ import { a_deferred_promise } from '../given/a_deferred_promise';
6
+ import { a_server_sent_event_hub_connection } from '../given/a_server_sent_event_hub_connection';
7
+ import { given } from '../../../given';
8
+ import { HubMessageType } from '../../WebSocketHubConnection';
9
+
10
+ describe(
11
+ 'when the connection changes while a subscribe request is in flight and the stale request returns non-OK',
12
+ given(a_server_sent_event_hub_connection, (context) => {
13
+ const queryId = 'q1';
14
+ const request = { queryName: 'MyQuery' };
15
+ let clock: sinon.SinonFakeTimers;
16
+
17
+ beforeEach(async () => {
18
+ context.setup();
19
+ clock = sinon.useFakeTimers({ toFake: ['setTimeout'] });
20
+ sinon.stub(console, 'warn');
21
+ const firstSubscribe = new a_deferred_promise<{
22
+ ok: boolean;
23
+ status: number;
24
+ }>();
25
+ context.fetchStub.onFirstCall().returns(firstSubscribe.promise);
26
+
27
+ context.connection.subscribe(queryId, request, sinon.stub());
28
+ context.simulateOpen();
29
+ context.simulateMessage({
30
+ type: HubMessageType.Connected,
31
+ payload: 'connection-a',
32
+ supportsSubscriptionRevisions: true,
33
+ });
34
+ context.simulateMessage({
35
+ type: HubMessageType.Connected,
36
+ payload: 'connection-b',
37
+ supportsSubscriptionRevisions: true,
38
+ });
39
+
40
+ firstSubscribe.resolve({ ok: false, status: 404 });
41
+ await Promise.resolve();
42
+ clock.tick(1000);
43
+ await Promise.resolve();
44
+ });
45
+
46
+ afterEach(() => {
47
+ clock.restore();
48
+ sinon.restore();
49
+ });
50
+
51
+ it('should send the active subscription on connection B', () => {
52
+ const body = parseBody(context.fetchStub.getCall(1).args[1].body as string);
53
+ body.connectionId.should.equal('connection-b');
54
+ body.queryId.should.equal(queryId);
55
+ body.request.should.deep.equal(request);
56
+ body.revision.should.be.greaterThan(0);
57
+ });
58
+
59
+ it('should not retry the obsolete connection A request', () =>
60
+ context.fetchStub.callCount.should.equal(2));
61
+
62
+ it('should not reconnect', () =>
63
+ (context.policy.schedule as sinon.SinonStub).called.should.be.false);
64
+
65
+ it('should retain the active subscription', () =>
66
+ context.connection.queryCount.should.equal(1));
67
+
68
+ function parseBody(rawBody: string): {
69
+ connectionId: string;
70
+ queryId: string;
71
+ request: object;
72
+ revision: number;
73
+ } {
74
+ try {
75
+ return JSON.parse(rawBody) as ReturnType<typeof parseBody>;
76
+ } catch (error) {
77
+ throw new Error('Expected a valid subscribe request body', {
78
+ cause: error,
79
+ });
80
+ }
81
+ }
82
+ }),
83
+ );
@@ -6,29 +6,38 @@ import { a_server_sent_event_hub_connection } from '../given/a_server_sent_event
6
6
  import { given } from '../../../given';
7
7
  import { HubMessageType } from '../../WebSocketHubConnection';
8
8
 
9
- describe('when disposing the hub connection', given(a_server_sent_event_hub_connection, context => {
10
- beforeEach(() => {
11
- context.setup();
12
- context.connection.subscribe('q1', { queryName: 'MyQuery' }, sinon.stub());
13
- context.simulateOpen();
14
- context.simulateMessage({ type: HubMessageType.Connected, payload: 'conn-1' });
15
- context.connection.dispose();
16
- });
9
+ describe(
10
+ 'when disposing the hub connection',
11
+ given(a_server_sent_event_hub_connection, (context) => {
12
+ beforeEach(() => {
13
+ context.setup();
14
+ context.connection.subscribe('q1', { queryName: 'MyQuery' }, sinon.stub());
15
+ context.simulateOpen();
16
+ context.simulateMessage({
17
+ type: HubMessageType.Connected,
18
+ payload: 'conn-1',
19
+ });
20
+ context.connection.dispose();
21
+ });
17
22
 
18
- afterEach(() => sinon.restore());
23
+ afterEach(() => sinon.restore());
19
24
 
20
- it('should close the EventSource', () => context.fakeEventSource.close.calledOnce.should.be.true);
21
- it('should cancel the reconnect policy', () => (context.policy.cancel as sinon.SinonStub).calledOnce.should.be.true);
22
- it('should report zero active subscriptions', () => context.connection.queryCount.should.equal(0));
25
+ it('should close the EventSource', () =>
26
+ context.fakeEventSource.close.calledOnce.should.be.true);
27
+ it('should cancel the reconnect policy', () =>
28
+ (context.policy.cancel as sinon.SinonStub).called.should.equal(true));
29
+ it('should report zero active subscriptions', () =>
30
+ context.connection.queryCount.should.equal(0));
23
31
 
24
- describe('when an error event fires after dispose', () => {
25
- beforeEach(() => {
26
- (context.policy.schedule as sinon.SinonStub).reset();
27
- context.simulateError();
28
- });
32
+ describe('when an error event fires after dispose', () => {
33
+ beforeEach(() => {
34
+ (context.policy.schedule as sinon.SinonStub).reset();
35
+ context.simulateError();
36
+ });
29
37
 
30
- it('should not schedule a reconnect', () => {
31
- (context.policy.schedule as sinon.SinonStub).callCount.should.equal(0);
38
+ it('should not schedule a reconnect', () => {
39
+ (context.policy.schedule as sinon.SinonStub).callCount.should.equal(0);
40
+ });
32
41
  });
33
- });
34
- }));
42
+ }),
43
+ );
@@ -7,45 +7,69 @@ import { given } from '../../../given';
7
7
  import { HubMessageType } from '../../WebSocketHubConnection';
8
8
  import { QueryResult } from '../../QueryResult';
9
9
 
10
- describe('when receiving unauthorized for a subscribed query', given(a_server_sent_event_hub_connection, context => {
11
- let callback: sinon.SinonStub;
12
- let receivedResult: QueryResult<unknown> | undefined;
13
- const queryId = 'q-auth-1';
14
- const connectionId = 'conn-123';
10
+ describe(
11
+ 'when receiving unauthorized for a subscribed query',
12
+ given(a_server_sent_event_hub_connection, (context) => {
13
+ let callback: sinon.SinonStub;
14
+ let receivedResult: QueryResult<unknown> | undefined;
15
+ const queryId = 'q-auth-1';
16
+ const connectionId = 'conn-123';
15
17
 
16
- beforeEach(() => {
17
- context.setup();
18
- callback = sinon.stub();
18
+ beforeEach(() => {
19
+ context.setup();
20
+ callback = sinon.stub();
19
21
 
20
- context.connection.subscribe(queryId, { queryName: 'SecureQuery' }, callback);
21
- context.simulateOpen();
22
+ context.connection.subscribe(queryId, { queryName: 'SecureQuery' }, callback);
23
+ context.simulateOpen();
22
24
 
23
- // Server sends Connected message with the connection ID.
24
- context.simulateMessage({ type: HubMessageType.Connected, payload: connectionId });
25
+ // Server sends Connected message with the connection ID.
26
+ context.simulateMessage({
27
+ type: HubMessageType.Connected,
28
+ payload: connectionId,
29
+ supportsSubscriptionRevisions: true,
30
+ });
31
+ const body = getSubscribeBody();
25
32
 
26
- // Server replies with Unauthorized for this subscription.
27
- context.simulateMessage({ type: HubMessageType.Unauthorized, queryId });
33
+ // Server replies with Unauthorized for this exact subscription.
34
+ context.simulateMessage({
35
+ type: HubMessageType.Unauthorized,
36
+ queryId,
37
+ revision: body.revision,
38
+ });
28
39
 
29
- receivedResult = callback.firstCall?.args[0] as QueryResult<unknown> | undefined;
30
- });
40
+ receivedResult = callback.firstCall?.args[0] as
41
+ | QueryResult<unknown>
42
+ | undefined;
43
+ });
31
44
 
32
- afterEach(() => {
33
- sinon.restore();
34
- });
45
+ afterEach(() => {
46
+ sinon.restore();
47
+ });
35
48
 
36
- it('should invoke the subscriber callback', () => {
37
- callback.calledOnce.should.be.true;
38
- });
49
+ function getSubscribeBody(): { revision: number } {
50
+ try {
51
+ return JSON.parse(context.fetchStub.firstCall.args[1].body as string) as {
52
+ revision: number;
53
+ };
54
+ } catch (error) {
55
+ throw new Error('Expected a valid subscribe body', { cause: error });
56
+ }
57
+ }
39
58
 
40
- it('should report isAuthorized as false', () => {
41
- receivedResult!.isAuthorized.should.be.false;
42
- });
59
+ it('should invoke the subscriber callback', () => {
60
+ callback.calledOnce.should.equal(true);
61
+ });
43
62
 
44
- it('should report isSuccess as false', () => {
45
- receivedResult!.isSuccess.should.be.false;
46
- });
63
+ it('should report isAuthorized as false', () => {
64
+ receivedResult!.isAuthorized.should.equal(false);
65
+ });
47
66
 
48
- it('should remove the subscription', () => {
49
- context.connection.queryCount.should.equal(0);
50
- });
51
- }));
67
+ it('should report isSuccess as false', () => {
68
+ receivedResult!.isSuccess.should.equal(false);
69
+ });
70
+
71
+ it('should remove the subscription', () => {
72
+ context.connection.queryCount.should.equal(0);
73
+ });
74
+ }),
75
+ );