@jumoo/usync 17.5.1 → 17.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,3 +8,9 @@ show things like changes, and updates ,etc.
8
8
  as a npm package, unless your client library is also going
9
9
  to be used by child packages.
10
10
 
11
+ ### SignalR negotiation
12
+
13
+ The uSync SignalR client respects `Umbraco:Cms:SignalR:ClientShouldSkipNegotiation`
14
+ when exposed by Umbraco's server context. When enabled, the client connects using
15
+ WebSockets without negotiation. When disabled, or on older Umbraco versions
16
+ that do not expose the setting, negotiation and transport fallback remain enabled.
@@ -1,19 +1,20 @@
1
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
- };
6
1
  var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
7
2
  if (kind === "m") throw new TypeError("Private method is not writable");
8
3
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
9
4
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
10
5
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
6
  };
12
- var _uSyncSignalRContext_instances, _uSyncSignalRContext_connection, _uSyncSignalRContext_connected, _uSyncSignalRContext_update, _uSyncSignalRContext_add, _uSyncSignalRContext_complete, _uSyncSignalRContext_setupConnection;
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _uSyncSignalRContext_instances, _uSyncSignalRContext_connection, _uSyncSignalRContext_auth, _uSyncSignalRContext_server, _uSyncSignalRContext_connect, _uSyncSignalRContext_clientId, _uSyncSignalRContext_connected, _uSyncSignalRContext_update, _uSyncSignalRContext_add, _uSyncSignalRContext_complete, _uSyncSignalRContext_setupConnection;
13
13
  import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
14
14
  import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
15
15
  import { USYNC_SIGNALR_CONTEXT_TOKEN, } from '@jumoo/uSync';
16
16
  import { UMB_AUTH_CONTEXT } from '@umbraco-cms/backoffice/auth';
17
+ import { UMB_SERVER_CONTEXT } from '@umbraco-cms/backoffice/server';
17
18
  import * as signalR from '@jumoo/uSync/external/signalr';
18
19
  import { uSyncSignalRReconnectPolicy } from './signalr-reconnect-policy.js';
19
20
  export class uSyncSignalRContext extends UmbControllerBase {
@@ -21,6 +22,9 @@ export class uSyncSignalRContext extends UmbControllerBase {
21
22
  super(host);
22
23
  _uSyncSignalRContext_instances.add(this);
23
24
  _uSyncSignalRContext_connection.set(this, void 0);
25
+ _uSyncSignalRContext_auth.set(this, void 0);
26
+ _uSyncSignalRContext_server.set(this, void 0);
27
+ _uSyncSignalRContext_clientId.set(this, null);
24
28
  _uSyncSignalRContext_connected.set(this, new UmbObjectState(false));
25
29
  this.connected = __classPrivateFieldGet(this, _uSyncSignalRContext_connected, "f").asObservable();
26
30
  _uSyncSignalRContext_update.set(this, new UmbObjectState(undefined));
@@ -31,12 +35,12 @@ export class uSyncSignalRContext extends UmbControllerBase {
31
35
  this.complete = __classPrivateFieldGet(this, _uSyncSignalRContext_complete, "f").asObservable();
32
36
  this.provideContext(USYNC_SIGNALR_CONTEXT_TOKEN, this);
33
37
  this.consumeContext(UMB_AUTH_CONTEXT, (auth) => {
34
- if (!auth)
35
- return;
36
- const authConfig = auth?.getOpenApiConfiguration();
37
- if (!authConfig)
38
- return;
39
- __classPrivateFieldGet(this, _uSyncSignalRContext_instances, "m", _uSyncSignalRContext_setupConnection).call(this, '/umbraco/SyncHub', auth);
38
+ __classPrivateFieldSet(this, _uSyncSignalRContext_auth, auth, "f");
39
+ __classPrivateFieldGet(this, _uSyncSignalRContext_instances, "m", _uSyncSignalRContext_connect).call(this);
40
+ });
41
+ this.consumeContext(UMB_SERVER_CONTEXT, (server) => {
42
+ __classPrivateFieldSet(this, _uSyncSignalRContext_server, server, "f");
43
+ __classPrivateFieldGet(this, _uSyncSignalRContext_instances, "m", _uSyncSignalRContext_connect).call(this);
40
44
  });
41
45
  }
42
46
  hostConnected() {
@@ -49,7 +53,7 @@ export class uSyncSignalRContext extends UmbControllerBase {
49
53
  });
50
54
  }
51
55
  getClientId() {
52
- return __classPrivateFieldGet(this, _uSyncSignalRContext_connection, "f")?.connectionId ?? null;
56
+ return __classPrivateFieldGet(this, _uSyncSignalRContext_clientId, "f");
53
57
  }
54
58
  /**
55
59
  * Synchronous read of the current connection state - used by callers
@@ -75,11 +79,34 @@ export class uSyncSignalRContext extends UmbControllerBase {
75
79
  __classPrivateFieldGet(this, _uSyncSignalRContext_update, "f").setValue({ message, count: 0, total: 1 });
76
80
  }
77
81
  }
78
- _uSyncSignalRContext_connection = new WeakMap(), _uSyncSignalRContext_connected = new WeakMap(), _uSyncSignalRContext_update = new WeakMap(), _uSyncSignalRContext_add = new WeakMap(), _uSyncSignalRContext_complete = new WeakMap(), _uSyncSignalRContext_instances = new WeakSet(), _uSyncSignalRContext_setupConnection = function _uSyncSignalRContext_setupConnection(url, auth) {
82
+ _uSyncSignalRContext_connection = new WeakMap(), _uSyncSignalRContext_auth = new WeakMap(), _uSyncSignalRContext_server = new WeakMap(), _uSyncSignalRContext_clientId = new WeakMap(), _uSyncSignalRContext_connected = new WeakMap(), _uSyncSignalRContext_update = new WeakMap(), _uSyncSignalRContext_add = new WeakMap(), _uSyncSignalRContext_complete = new WeakMap(), _uSyncSignalRContext_instances = new WeakSet(), _uSyncSignalRContext_connect = function _uSyncSignalRContext_connect() {
83
+ // either context being re-provided replaces the connection, so
84
+ // stop the old one rather than leave it running alongside.
85
+ if (__classPrivateFieldGet(this, _uSyncSignalRContext_connection, "f")) {
86
+ __classPrivateFieldGet(this, _uSyncSignalRContext_connection, "f").stop();
87
+ __classPrivateFieldSet(this, _uSyncSignalRContext_connection, undefined, "f");
88
+ __classPrivateFieldSet(this, _uSyncSignalRContext_clientId, null, "f");
89
+ __classPrivateFieldGet(this, _uSyncSignalRContext_connected, "f").setValue(false);
90
+ }
91
+ if (!__classPrivateFieldGet(this, _uSyncSignalRContext_auth, "f") || !__classPrivateFieldGet(this, _uSyncSignalRContext_server, "f"))
92
+ return;
93
+ if (!__classPrivateFieldGet(this, _uSyncSignalRContext_auth, "f").getOpenApiConfiguration())
94
+ return;
95
+ const connection = __classPrivateFieldGet(this, _uSyncSignalRContext_server, "f").getServerConnection();
96
+ // Older Umbraco versions do not expose this setting.
97
+ const skipNegotiation = 'getSignalRSkipNegotiation' in connection &&
98
+ typeof connection.getSignalRSkipNegotiation === 'function' &&
99
+ connection.getSignalRSkipNegotiation() === true;
100
+ __classPrivateFieldGet(this, _uSyncSignalRContext_instances, "m", _uSyncSignalRContext_setupConnection).call(this, '/umbraco/SyncHub', __classPrivateFieldGet(this, _uSyncSignalRContext_auth, "f"), skipNegotiation);
101
+ }, _uSyncSignalRContext_setupConnection = function _uSyncSignalRContext_setupConnection(url, auth, skipNegotiation) {
79
102
  // the token is fetched on every (re)connect, so a reconnect after
80
103
  // the original token has expired still authenticates.
81
104
  __classPrivateFieldSet(this, _uSyncSignalRContext_connection, new signalR.HubConnectionBuilder()
82
- .withUrl(url, { accessTokenFactory: () => auth.getLatestToken() })
105
+ .withUrl(url, {
106
+ accessTokenFactory: () => auth.getLatestToken(),
107
+ skipNegotiation,
108
+ transport: skipNegotiation ? signalR.HttpTransportType.WebSockets : undefined,
109
+ })
83
110
  .withAutomaticReconnect(new uSyncSignalRReconnectPolicy())
84
111
  .configureLogging(signalR.LogLevel.Warning)
85
112
  .build(), "f");
@@ -92,19 +119,39 @@ _uSyncSignalRContext_connection = new WeakMap(), _uSyncSignalRContext_connected
92
119
  __classPrivateFieldGet(this, _uSyncSignalRContext_connection, "f").on('complete', (data) => {
93
120
  __classPrivateFieldGet(this, _uSyncSignalRContext_complete, "f").setValue(data);
94
121
  });
95
- __classPrivateFieldGet(this, _uSyncSignalRContext_connection, "f").start().then(() => {
96
- __classPrivateFieldGet(this, _uSyncSignalRContext_connected, "f").setValue(true);
122
+ const connection = __classPrivateFieldGet(this, _uSyncSignalRContext_connection, "f");
123
+ // a stopped connection can still report state while it winds down,
124
+ // only the current one may update connected.
125
+ const setConnected = (connected) => {
126
+ if (__classPrivateFieldGet(this, _uSyncSignalRContext_connection, "f") !== connection)
127
+ return;
128
+ if (!connected)
129
+ __classPrivateFieldSet(this, _uSyncSignalRContext_clientId, null, "f");
130
+ __classPrivateFieldGet(this, _uSyncSignalRContext_connected, "f").setValue(connected);
131
+ };
132
+ // the connection id comes from the negotiate response, so when
133
+ // negotiation is skipped we have to ask the hub for it.
134
+ const onConnected = async () => {
135
+ const clientId = connection.connectionId ??
136
+ (await connection.invoke('GetConnectionId').catch(() => null));
137
+ if (__classPrivateFieldGet(this, _uSyncSignalRContext_connection, "f") !== connection)
138
+ return;
139
+ __classPrivateFieldSet(this, _uSyncSignalRContext_clientId, clientId, "f");
140
+ setConnected(true);
141
+ };
142
+ connection
143
+ .start()
144
+ .then(onConnected)
145
+ .catch((error) => {
146
+ // with negotiation skipped there is no transport fallback, so a
147
+ // blocked WebSocket ends here; status polling covers progress.
148
+ console.warn('uSync: SignalR connection failed', error);
149
+ setConnected(false);
97
150
  });
98
151
  // the client id changes on reconnect, requests made after this
99
152
  // pick up the new one via getClientId().
100
- __classPrivateFieldGet(this, _uSyncSignalRContext_connection, "f").onreconnecting(() => {
101
- __classPrivateFieldGet(this, _uSyncSignalRContext_connected, "f").setValue(false);
102
- });
103
- __classPrivateFieldGet(this, _uSyncSignalRContext_connection, "f").onreconnected(() => {
104
- __classPrivateFieldGet(this, _uSyncSignalRContext_connected, "f").setValue(true);
105
- });
106
- __classPrivateFieldGet(this, _uSyncSignalRContext_connection, "f").onclose(() => {
107
- __classPrivateFieldGet(this, _uSyncSignalRContext_connected, "f").setValue(false);
108
- });
153
+ connection.onreconnecting(() => setConnected(false));
154
+ connection.onreconnected(onConnected);
155
+ connection.onclose(() => setConnected(false));
109
156
  };
110
157
  export default uSyncSignalRContext;
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "homepage": "https://jumoo.co.uk/uSync",
9
9
  "license": "MPL-2.0",
10
10
  "type": "module",
11
- "version": "17.5.1",
11
+ "version": "17.5.2",
12
12
  "main": "./dist/usync.js",
13
13
  "types": "./dist/index.d.ts",
14
14
  "module": "./dist/usync.js",