@effect.healthcare/messenger 0.3.1 → 0.4.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/.claude/CLAUDE.md +88 -88
- package/.claude/context/composition.md +921 -921
- package/.claude/context/data.md +1036 -1036
- package/.claude/context/event.md +686 -686
- package/.claude/context/gesture.md +614 -614
- package/.claude/context/operation.md +596 -596
- package/.claude/context/state.md +864 -864
- package/.claude/context/testing.md +876 -876
- package/.claude/context/validation.md +350 -350
- package/.claude/context/workflow.md +608 -608
- package/.claude/settings.json +7 -0
- package/.mcp.json +12 -12
- package/LICENSE +144 -144
- package/dist/messenger.d.ts +9 -1
- package/dist/messenger.js +22 -3
- package/dist/messenger.meta.d.ts +1 -0
- package/dist/messenger.meta.js +1 -0
- package/package.json +55 -55
- package/src/index.ts +16 -16
- package/src/messenger.meta.ts +132 -131
- package/src/messenger.ts +386 -347
- package/src/messenger.validation.ts +25 -25
- package/web-dev-server.config.mjs +8 -8
- package/.claude/settings.local.json +0 -11
- package/dist/component.style.css +0 -6
- package/dist/component.template.html +0 -1
- package/dist/messenger.style.css +0 -6
- package/dist/messenger.template.html +0 -1
package/src/messenger.ts
CHANGED
|
@@ -1,347 +1,386 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module Component
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
// Base Component
|
|
6
|
-
import { Component } from "@scalable.software/component";
|
|
7
|
-
import { type Configuration } from "@scalable.software/component";
|
|
8
|
-
|
|
9
|
-
// Component Metadata
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
*
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
*
|
|
48
|
-
* @
|
|
49
|
-
*/
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
*
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
*
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
*
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
private
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
*
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
* @
|
|
95
|
-
*/
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
this.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
//
|
|
143
|
-
this.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
this.
|
|
181
|
-
this._socket =
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
*
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
this.
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
this.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
this.
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
*
|
|
284
|
-
* @category
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
*
|
|
323
|
-
* @category
|
|
324
|
-
* @hidden
|
|
325
|
-
*/
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @module Component
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Base Component
|
|
6
|
+
import { Component } from "@scalable.software/component";
|
|
7
|
+
import { type Configuration } from "@scalable.software/component";
|
|
8
|
+
|
|
9
|
+
// Component Metadata
|
|
10
|
+
import {
|
|
11
|
+
Tag,
|
|
12
|
+
Attributes,
|
|
13
|
+
Connection,
|
|
14
|
+
Event,
|
|
15
|
+
Gesture,
|
|
16
|
+
Session,
|
|
17
|
+
Protocol,
|
|
18
|
+
Message,
|
|
19
|
+
} from "./messenger.meta.js";
|
|
20
|
+
|
|
21
|
+
import { type Endpoint } from "./messenger.meta.js";
|
|
22
|
+
|
|
23
|
+
// Component Validation
|
|
24
|
+
import { Validate } from "./messenger.validation.js";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Configuration for the headless component — no template or stylesheet.
|
|
28
|
+
* @category Configuration
|
|
29
|
+
*/
|
|
30
|
+
export const configuration: Configuration = {
|
|
31
|
+
url: import.meta.url,
|
|
32
|
+
} as const;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @category Component
|
|
36
|
+
*/
|
|
37
|
+
export class Messenger extends Component {
|
|
38
|
+
/**
|
|
39
|
+
* The tag name of the component
|
|
40
|
+
* @category Configuration
|
|
41
|
+
*/
|
|
42
|
+
public static get Tag() {
|
|
43
|
+
return Tag;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Only attributes defined the Attributes object will be observed in DOM
|
|
48
|
+
* @category State
|
|
49
|
+
*/
|
|
50
|
+
public static get Attributes(): Attributes {
|
|
51
|
+
return Attributes;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The live connection status of the messenger. Internally owned.
|
|
56
|
+
* @category State
|
|
57
|
+
* @hidden
|
|
58
|
+
*/
|
|
59
|
+
private _connection: Connection = Connection.DISCONNECTED;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The negotiate endpoint URL. When absent the messenger stays disconnected.
|
|
63
|
+
* @category State
|
|
64
|
+
* @hidden
|
|
65
|
+
*/
|
|
66
|
+
private _endpoint: Endpoint | null = null;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The live Web PubSub socket. Held so it can be closed and replaced.
|
|
70
|
+
* @category State
|
|
71
|
+
* @hidden
|
|
72
|
+
*/
|
|
73
|
+
private _socket: WebSocket | null = null;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Event subscription handler slots.
|
|
77
|
+
* @category Event
|
|
78
|
+
* @hidden
|
|
79
|
+
*/
|
|
80
|
+
private _onendpointchange: EventListener | null = null;
|
|
81
|
+
private _onconnecting: EventListener | null = null;
|
|
82
|
+
private _onconnected: EventListener | null = null;
|
|
83
|
+
private _ondisconnected: EventListener | null = null;
|
|
84
|
+
private _onmasterdatachange: EventListener | null = null;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Cache element references to improve performance
|
|
88
|
+
* @category State
|
|
89
|
+
* @hidden
|
|
90
|
+
*/
|
|
91
|
+
protected elements: {} = {};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @hidden
|
|
95
|
+
*/
|
|
96
|
+
constructor() {
|
|
97
|
+
super(configuration);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The live connection status of the messenger. Internally owned — readable
|
|
102
|
+
* externally, but only mutable from within (protected setter).
|
|
103
|
+
* @category State
|
|
104
|
+
*/
|
|
105
|
+
public get connection(): Connection {
|
|
106
|
+
return this._connection;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
protected set connection(connection: Connection) {
|
|
110
|
+
// Guard
|
|
111
|
+
if (this._connection === connection) return;
|
|
112
|
+
|
|
113
|
+
// Mutate
|
|
114
|
+
this._connection = connection;
|
|
115
|
+
this.setAttribute(Attributes.CONNECTION, connection);
|
|
116
|
+
|
|
117
|
+
// Dispatch
|
|
118
|
+
const event = { detail: { connection } };
|
|
119
|
+
connection === Connection.CONNECTING &&
|
|
120
|
+
this._dispatchEvent(Event.ON_CONNECTING, event);
|
|
121
|
+
connection === Connection.CONNECTED &&
|
|
122
|
+
this._dispatchEvent(Event.ON_CONNECTED, event);
|
|
123
|
+
connection === Connection.DISCONNECTED &&
|
|
124
|
+
this._dispatchEvent(Event.ON_DISCONNECTED, event);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The negotiate endpoint URL. When absent the messenger stays disconnected.
|
|
129
|
+
* @category State
|
|
130
|
+
*/
|
|
131
|
+
public get endpoint(): Endpoint | null {
|
|
132
|
+
return this._endpoint;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public set endpoint(endpoint: Endpoint | null) {
|
|
136
|
+
// Normalize
|
|
137
|
+
endpoint = endpoint ?? null;
|
|
138
|
+
|
|
139
|
+
// Validate
|
|
140
|
+
endpoint = endpoint ? Validate.endpoint(endpoint) : null;
|
|
141
|
+
|
|
142
|
+
// Guard
|
|
143
|
+
if (this._endpoint === endpoint) return;
|
|
144
|
+
|
|
145
|
+
// Mutate
|
|
146
|
+
this._endpoint = endpoint;
|
|
147
|
+
endpoint === null
|
|
148
|
+
? this.removeAttribute(Attributes.ENDPOINT)
|
|
149
|
+
: this.setAttribute(Attributes.ENDPOINT, endpoint);
|
|
150
|
+
|
|
151
|
+
// Dispatch
|
|
152
|
+
this._dispatchEvent(Event.ON_ENDPOINT_CHANGE, { detail: { endpoint } });
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* The session token, read fresh from local storage on every access — never
|
|
157
|
+
* cached — because the License component renews and rewrites the key.
|
|
158
|
+
* @category State
|
|
159
|
+
* @hidden
|
|
160
|
+
*/
|
|
161
|
+
private get _token(): string | null {
|
|
162
|
+
return localStorage.getItem(Session);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Negotiates a client access URL from the configured endpoint, authenticating
|
|
167
|
+
* with the session token, then opens the live connection. No-op when the
|
|
168
|
+
* endpoint or token is unavailable, or when already connecting or connected.
|
|
169
|
+
* @category Operation
|
|
170
|
+
*/
|
|
171
|
+
public connect = async (): Promise<void> => {
|
|
172
|
+
const token = this._token;
|
|
173
|
+
|
|
174
|
+
// Gate + guard
|
|
175
|
+
if (!this.endpoint || !token) return;
|
|
176
|
+
if (this._connection !== Connection.DISCONNECTED) return;
|
|
177
|
+
|
|
178
|
+
// Establish
|
|
179
|
+
this.connection = Connection.CONNECTING;
|
|
180
|
+
const url = await this._negotiate(token);
|
|
181
|
+
this._socket = this._open(url);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Closes the live connection.
|
|
186
|
+
* @category Operation
|
|
187
|
+
*/
|
|
188
|
+
public disconnect = (): void => {
|
|
189
|
+
this._socket?.close();
|
|
190
|
+
this._socket = null;
|
|
191
|
+
this.connection = Connection.DISCONNECTED;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Sends a JSON message to the server over the open Web PubSub connection by
|
|
196
|
+
* wrapping it in an `event` frame — the client-to-server mirror of the inbound
|
|
197
|
+
* `masterData.changed` notification. No-op returning `false` when there is no
|
|
198
|
+
* open connection; returns `true` when a frame is sent.
|
|
199
|
+
* @category Operation
|
|
200
|
+
*/
|
|
201
|
+
public send = (message: object): boolean => {
|
|
202
|
+
// Gate
|
|
203
|
+
if (!this._socket || this._connection !== Connection.CONNECTED)
|
|
204
|
+
return false;
|
|
205
|
+
|
|
206
|
+
// Send — wrap in an Azure Web PubSub `event` frame, message rides as `data`
|
|
207
|
+
this._socket.send(
|
|
208
|
+
JSON.stringify({
|
|
209
|
+
type: "event",
|
|
210
|
+
event: "message",
|
|
211
|
+
dataType: "json",
|
|
212
|
+
data: message,
|
|
213
|
+
}),
|
|
214
|
+
);
|
|
215
|
+
return true;
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Subscribes to the `onendpointchange` event. Assigning replaces the previous
|
|
220
|
+
* handler; assigning `null` removes it.
|
|
221
|
+
* @category Event
|
|
222
|
+
*/
|
|
223
|
+
public set onendpointchange(handler: EventListener | null) {
|
|
224
|
+
this._onendpointchange &&
|
|
225
|
+
this.removeEventListener(
|
|
226
|
+
Event.ON_ENDPOINT_CHANGE,
|
|
227
|
+
this._onendpointchange,
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
this._onendpointchange = handler;
|
|
231
|
+
|
|
232
|
+
this._onendpointchange &&
|
|
233
|
+
this.addEventListener(Event.ON_ENDPOINT_CHANGE, this._onendpointchange);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Subscribes to the `onconnecting` event. Assigning replaces the previous
|
|
238
|
+
* handler; assigning `null` removes it.
|
|
239
|
+
* @category Event
|
|
240
|
+
*/
|
|
241
|
+
public set onconnecting(handler: EventListener | null) {
|
|
242
|
+
this._onconnecting &&
|
|
243
|
+
this.removeEventListener(Event.ON_CONNECTING, this._onconnecting);
|
|
244
|
+
|
|
245
|
+
this._onconnecting = handler;
|
|
246
|
+
|
|
247
|
+
this._onconnecting &&
|
|
248
|
+
this.addEventListener(Event.ON_CONNECTING, this._onconnecting);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Subscribes to the `onconnected` event. Assigning replaces the previous
|
|
253
|
+
* handler; assigning `null` removes it.
|
|
254
|
+
* @category Event
|
|
255
|
+
*/
|
|
256
|
+
public set onconnected(handler: EventListener | null) {
|
|
257
|
+
this._onconnected &&
|
|
258
|
+
this.removeEventListener(Event.ON_CONNECTED, this._onconnected);
|
|
259
|
+
|
|
260
|
+
this._onconnected = handler;
|
|
261
|
+
|
|
262
|
+
this._onconnected &&
|
|
263
|
+
this.addEventListener(Event.ON_CONNECTED, this._onconnected);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Subscribes to the `ondisconnected` event. Assigning replaces the previous
|
|
268
|
+
* handler; assigning `null` removes it.
|
|
269
|
+
* @category Event
|
|
270
|
+
*/
|
|
271
|
+
public set ondisconnected(handler: EventListener | null) {
|
|
272
|
+
this._ondisconnected &&
|
|
273
|
+
this.removeEventListener(Event.ON_DISCONNECTED, this._ondisconnected);
|
|
274
|
+
|
|
275
|
+
this._ondisconnected = handler;
|
|
276
|
+
|
|
277
|
+
this._ondisconnected &&
|
|
278
|
+
this.addEventListener(Event.ON_DISCONNECTED, this._ondisconnected);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Subscribes to the `onmasterdatachange` event. Assigning replaces the
|
|
283
|
+
* previous handler; assigning `null` removes it.
|
|
284
|
+
* @category Event
|
|
285
|
+
*/
|
|
286
|
+
public set onmasterdatachange(handler: EventListener | null) {
|
|
287
|
+
this._onmasterdatachange &&
|
|
288
|
+
this.removeEventListener(
|
|
289
|
+
Event.ON_MASTERDATACHANGE,
|
|
290
|
+
this._onmasterdatachange,
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
this._onmasterdatachange = handler;
|
|
294
|
+
|
|
295
|
+
this._onmasterdatachange &&
|
|
296
|
+
this.addEventListener(
|
|
297
|
+
Event.ON_MASTERDATACHANGE,
|
|
298
|
+
this._onmasterdatachange,
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* List operations to perform for selected attributes being observed in the DOM.
|
|
304
|
+
* @category Configuration
|
|
305
|
+
* @hidden
|
|
306
|
+
*/
|
|
307
|
+
protected _attributeHandlers = {
|
|
308
|
+
[Attributes.CONNECTION]: (value: Connection) =>
|
|
309
|
+
value !== this._connection &&
|
|
310
|
+
this.setAttribute(Attributes.CONNECTION, this._connection),
|
|
311
|
+
[Attributes.ENDPOINT]: (value: Endpoint) => (this.endpoint = value),
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Cache element references to improve performance
|
|
316
|
+
* @category Configuration
|
|
317
|
+
* @hidden
|
|
318
|
+
*/
|
|
319
|
+
protected _cache = () => {};
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Initialize component attributes with default values
|
|
323
|
+
* @category Configuration
|
|
324
|
+
* @hidden
|
|
325
|
+
*/
|
|
326
|
+
protected _initialize = () => {
|
|
327
|
+
!this.hasAttribute(Attributes.CONNECTION) &&
|
|
328
|
+
this.setAttribute(Attributes.CONNECTION, this._connection);
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Called by the connectedCallback prototypical method
|
|
333
|
+
* @category Configuration
|
|
334
|
+
* @hidden
|
|
335
|
+
*/
|
|
336
|
+
protected _addEventListeners = () => {};
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Called by the disconnectedCallback prototypical method
|
|
340
|
+
* @category Configuration
|
|
341
|
+
* @hidden
|
|
342
|
+
*/
|
|
343
|
+
protected _removeEventListeners = () => {};
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Negotiates a short-lived Web PubSub client access URL from the endpoint,
|
|
347
|
+
* authenticating the request with the session token.
|
|
348
|
+
* @category Operation
|
|
349
|
+
* @hidden
|
|
350
|
+
*/
|
|
351
|
+
private _negotiate = async (token: string): Promise<string> => {
|
|
352
|
+
const response = await fetch(this.endpoint as string, {
|
|
353
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
354
|
+
});
|
|
355
|
+
const { url } = await response.json();
|
|
356
|
+
return url;
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Opens a Web PubSub WebSocket with the required subprotocol and wires the
|
|
361
|
+
* connection lifecycle handlers.
|
|
362
|
+
* @category Operation
|
|
363
|
+
* @hidden
|
|
364
|
+
*/
|
|
365
|
+
private _open = (url: string): WebSocket => {
|
|
366
|
+
const socket = new WebSocket(url, Protocol);
|
|
367
|
+
socket.onopen = () => (this.connection = Connection.CONNECTED);
|
|
368
|
+
socket.onclose = () => (this.connection = Connection.DISCONNECTED);
|
|
369
|
+
socket.addEventListener(Gesture.MESSAGE, this._handleMessage);
|
|
370
|
+
return socket;
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Handles an inbound Web PubSub message (the `message` gesture). Dispatches
|
|
375
|
+
* `onmasterdatachange` when the payload's `data.type` is `masterData.changed`.
|
|
376
|
+
* @category Gesture
|
|
377
|
+
* @hidden
|
|
378
|
+
*/
|
|
379
|
+
private _handleMessage = (event: MessageEvent) => {
|
|
380
|
+
const message = JSON.parse(event.data);
|
|
381
|
+
message.data?.type === Message.MASTER_DATA_CHANGED &&
|
|
382
|
+
this._dispatchEvent(Event.ON_MASTERDATACHANGE, {
|
|
383
|
+
detail: { datetime: new Date().toISOString() },
|
|
384
|
+
});
|
|
385
|
+
};
|
|
386
|
+
}
|