@vuu-ui/vuu-data-remote 3.3.10 → 4.0.0-alpha.1
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/package.json +9 -8
- package/src/ConnectionManager.js +158 -65
- package/src/DedicatedWorker.js +4 -1
- package/src/VuuDataSource.js +11 -4
- package/src/WebSocketConnection.js +8 -1
- package/src/index.js +0 -4
- package/src/inlined-worker.js +19 -9
- package/src/server-proxy/server-proxy.js +5 -4
- package/src/worker.js +3 -2
- package/types/ConnectionManager.d.ts +46 -18
- package/types/DedicatedWorker.d.ts +10 -3
- package/types/VuuDataSource.d.ts +3 -3
- package/types/WebSocketConnection.d.ts +3 -3
- package/types/index.d.ts +0 -4
- package/types/inlined-worker.d.ts +1 -1
- package/types/server-proxy/server-proxy.d.ts +4 -2
- package/types/server-proxy/viewport.d.ts +1 -1
- package/src/LostConnectionHandler.js +0 -89
- package/src/VuuAuthProvider.js +0 -78
- package/src/VuuAuthenticator.js +0 -36
- package/src/authenticate.js +0 -50
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "
|
|
2
|
+
"version": "4.0.0-alpha.1",
|
|
3
3
|
"main": "./src/index.js",
|
|
4
4
|
"author": "heswell",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"scripts": {
|
|
7
8
|
"build": "node ../../scripts/run-build-worker.mjs && node ../../scripts/run-build-rslib.ts",
|
|
8
9
|
"build:dev": "node ../../scripts/run-build-worker.mjs && node ../../scripts/run-build.mjs -- --dev",
|
|
@@ -11,14 +12,15 @@
|
|
|
11
12
|
"type-defs": "node ../../scripts/build-type-defs.mjs"
|
|
12
13
|
},
|
|
13
14
|
"devDependencies": {
|
|
14
|
-
"@vuu-ui/vuu-data-types": "
|
|
15
|
-
"@vuu-ui/vuu-table-types": "
|
|
16
|
-
"@vuu-ui/vuu-filter-types": "
|
|
17
|
-
"@vuu-ui/vuu-protocol-types": "
|
|
15
|
+
"@vuu-ui/vuu-data-types": "4.0.0-alpha.1",
|
|
16
|
+
"@vuu-ui/vuu-table-types": "4.0.0-alpha.1",
|
|
17
|
+
"@vuu-ui/vuu-filter-types": "4.0.0-alpha.1",
|
|
18
|
+
"@vuu-ui/vuu-protocol-types": "4.0.0-alpha.1"
|
|
18
19
|
},
|
|
19
20
|
"dependencies": {
|
|
20
|
-
"@vuu-ui/vuu-
|
|
21
|
-
"@vuu-ui/vuu-
|
|
21
|
+
"@vuu-ui/vuu-data-editing": "4.0.0-alpha.1",
|
|
22
|
+
"@vuu-ui/vuu-filter-parser": "4.0.0-alpha.1",
|
|
23
|
+
"@vuu-ui/vuu-utils": "4.0.0-alpha.1"
|
|
22
24
|
},
|
|
23
25
|
"files": [
|
|
24
26
|
"README.md",
|
|
@@ -33,6 +35,5 @@
|
|
|
33
35
|
},
|
|
34
36
|
"module": "./src/index.js",
|
|
35
37
|
"name": "@vuu-ui/vuu-data-remote",
|
|
36
|
-
"type": "module",
|
|
37
38
|
"types": "types/index.d.ts"
|
|
38
39
|
}
|
package/src/ConnectionManager.js
CHANGED
|
@@ -2,20 +2,55 @@ import { DeferredPromise, EventEmitter, isConnectionQualityMetrics, isRequestRes
|
|
|
2
2
|
import { isWebSocketConnectionStatus } from "./WebSocketConnection.js";
|
|
3
3
|
import { DedicatedWorker } from "./DedicatedWorker.js";
|
|
4
4
|
import { shouldMessageBeRoutedToDataSource } from "./data-source.js";
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const DEFAULT_CONNECTION_ID = "portal";
|
|
6
|
+
class ConnectionChannel {
|
|
7
|
+
onConnectionStatus;
|
|
8
|
+
onConnectionMetrics;
|
|
7
9
|
#connectionStatus = "closed";
|
|
8
10
|
#deferredServerAPI = new DeferredPromise();
|
|
9
11
|
#pendingRequests = new Map();
|
|
10
12
|
#viewports = new Map();
|
|
11
13
|
#worker;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
#serverAPI;
|
|
15
|
+
constructor(onConnectionStatus, onConnectionMetrics){
|
|
16
|
+
this.onConnectionStatus = onConnectionStatus;
|
|
17
|
+
this.onConnectionMetrics = onConnectionMetrics;
|
|
14
18
|
this.#worker = new DedicatedWorker(this.handleMessageFromWorker);
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
this.#serverAPI = {
|
|
20
|
+
subscribe: (message, callback)=>{
|
|
21
|
+
if (this.#viewports.get(message.viewport)) throw Error("ConnectionManager attempting to subscribe with an existing viewport id");
|
|
22
|
+
this.#viewports.set(message.viewport, {
|
|
23
|
+
status: "subscribing",
|
|
24
|
+
request: message,
|
|
25
|
+
postMessageToClientDataSource: callback
|
|
26
|
+
});
|
|
27
|
+
this.#worker.send({
|
|
28
|
+
type: "subscribe",
|
|
29
|
+
...message
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
unsubscribe: (viewport)=>{
|
|
33
|
+
this.#worker.send({
|
|
34
|
+
type: "unsubscribe",
|
|
35
|
+
viewport
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
send: (message)=>{
|
|
39
|
+
this.#worker.send(message);
|
|
40
|
+
},
|
|
41
|
+
destroy: (viewportId)=>{
|
|
42
|
+
if (viewportId && this.#viewports.has(viewportId)) this.#viewports.delete(viewportId);
|
|
43
|
+
},
|
|
44
|
+
rpcCall: async (message)=>this.asyncRequest(message),
|
|
45
|
+
select: async (selectRequest)=>this.asyncRequest(selectRequest),
|
|
46
|
+
getTableList: async ()=>this.asyncRequest({
|
|
47
|
+
type: "GET_TABLE_LIST"
|
|
48
|
+
}),
|
|
49
|
+
getTableSchema: async (table)=>this.asyncRequest({
|
|
50
|
+
type: "GET_TABLE_META",
|
|
51
|
+
table
|
|
52
|
+
})
|
|
53
|
+
};
|
|
19
54
|
}
|
|
20
55
|
get connectionStatus() {
|
|
21
56
|
return this.#connectionStatus;
|
|
@@ -23,28 +58,45 @@ class ConnectionManager extends EventEmitter {
|
|
|
23
58
|
get connected() {
|
|
24
59
|
return "connected" === this.#connectionStatus || "reconnected" === this.#connectionStatus;
|
|
25
60
|
}
|
|
61
|
+
get serverAPI() {
|
|
62
|
+
return this.#deferredServerAPI.promise;
|
|
63
|
+
}
|
|
26
64
|
async connect(options, throwOnRejected = false) {
|
|
27
65
|
try {
|
|
28
66
|
const result = await this.#worker.connect(options);
|
|
29
|
-
if ("connected" === result) this.#deferredServerAPI.resolve(this
|
|
67
|
+
if ("connected" === result.status || "reconnected" === result.status) this.#deferredServerAPI.resolve(this.#serverAPI);
|
|
30
68
|
return result;
|
|
31
69
|
} catch (err) {
|
|
32
|
-
if (
|
|
33
|
-
|
|
70
|
+
if (throwOnRejected) throw err;
|
|
71
|
+
return {
|
|
72
|
+
status: "rejected"
|
|
73
|
+
};
|
|
34
74
|
}
|
|
35
75
|
}
|
|
36
76
|
disableActiveSubscriptions() {
|
|
37
|
-
console.log("[ConnectionManager] disableActiveSubscriptions");
|
|
38
77
|
this.#worker.send({
|
|
39
78
|
type: "disable-all-active"
|
|
40
79
|
});
|
|
41
80
|
}
|
|
42
81
|
enableActiveSubscriptions() {
|
|
43
|
-
console.log("[ConnectionManager] enableActiveSubscriptions");
|
|
44
82
|
this.#worker.send({
|
|
45
83
|
type: "enable-all-active"
|
|
46
84
|
});
|
|
47
85
|
}
|
|
86
|
+
async disconnect() {
|
|
87
|
+
try {
|
|
88
|
+
this.#worker.send({
|
|
89
|
+
type: "disconnect"
|
|
90
|
+
});
|
|
91
|
+
this.#deferredServerAPI = new DeferredPromise();
|
|
92
|
+
return "disconnected";
|
|
93
|
+
} catch {
|
|
94
|
+
return "rejected";
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
destroy() {
|
|
98
|
+
this.#worker.terminate();
|
|
99
|
+
}
|
|
48
100
|
handleMessageFromWorker = (message)=>{
|
|
49
101
|
if (shouldMessageBeRoutedToDataSource(message)) {
|
|
50
102
|
const viewport = this.#viewports.get(message.clientViewportId);
|
|
@@ -52,8 +104,8 @@ class ConnectionManager extends EventEmitter {
|
|
|
52
104
|
else console.error(`[ConnectionManager] ${message.type} message received, viewport not found`);
|
|
53
105
|
} else if (isWebSocketConnectionStatus(message)) {
|
|
54
106
|
this.#connectionStatus = message;
|
|
55
|
-
this.
|
|
56
|
-
} else if (isConnectionQualityMetrics(message)) this.
|
|
107
|
+
this.onConnectionStatus(message);
|
|
108
|
+
} else if (isConnectionQualityMetrics(message)) this.onConnectionMetrics(message);
|
|
57
109
|
else if (isRequestResponse(message)) {
|
|
58
110
|
const { requestId } = message;
|
|
59
111
|
if (this.#pendingRequests.has(requestId)) {
|
|
@@ -64,71 +116,112 @@ class ConnectionManager extends EventEmitter {
|
|
|
64
116
|
} else console.warn("%cConnectionManager Unexpected message from the worker", "color:red;font-weight:bold;");
|
|
65
117
|
}
|
|
66
118
|
};
|
|
67
|
-
get serverAPI() {
|
|
68
|
-
return this.#deferredServerAPI.promise;
|
|
69
|
-
}
|
|
70
|
-
connectedServerAPI = {
|
|
71
|
-
subscribe: (message, callback)=>{
|
|
72
|
-
if (this.#viewports.get(message.viewport)) throw Error("ConnectionManager attempting to subscribe with an existing viewport id");
|
|
73
|
-
this.#viewports.set(message.viewport, {
|
|
74
|
-
status: "subscribing",
|
|
75
|
-
request: message,
|
|
76
|
-
postMessageToClientDataSource: callback
|
|
77
|
-
});
|
|
78
|
-
this.#worker.send({
|
|
79
|
-
type: "subscribe",
|
|
80
|
-
...message
|
|
81
|
-
});
|
|
82
|
-
},
|
|
83
|
-
unsubscribe: (viewport)=>{
|
|
84
|
-
this.#worker.send({
|
|
85
|
-
type: "unsubscribe",
|
|
86
|
-
viewport
|
|
87
|
-
});
|
|
88
|
-
},
|
|
89
|
-
send: (message)=>{
|
|
90
|
-
this.#worker.send(message);
|
|
91
|
-
},
|
|
92
|
-
destroy: (viewportId)=>{
|
|
93
|
-
if (viewportId && this.#viewports.has(viewportId)) this.#viewports.delete(viewportId);
|
|
94
|
-
},
|
|
95
|
-
rpcCall: async (message)=>this.asyncRequest(message),
|
|
96
|
-
select: async (selectRequest)=>this.asyncRequest(selectRequest),
|
|
97
|
-
getTableList: async ()=>this.asyncRequest({
|
|
98
|
-
type: "GET_TABLE_LIST"
|
|
99
|
-
}),
|
|
100
|
-
getTableSchema: async (table)=>this.asyncRequest({
|
|
101
|
-
type: "GET_TABLE_META",
|
|
102
|
-
table
|
|
103
|
-
})
|
|
104
|
-
};
|
|
105
119
|
asyncRequest = (msg)=>{
|
|
106
120
|
const requestId = uuid();
|
|
107
121
|
this.#worker.send({
|
|
108
122
|
requestId,
|
|
109
123
|
...msg
|
|
110
124
|
});
|
|
111
|
-
return new Promise((resolve
|
|
125
|
+
return new Promise((resolve)=>{
|
|
112
126
|
this.#pendingRequests.set(requestId, {
|
|
113
|
-
resolve
|
|
114
|
-
reject
|
|
127
|
+
resolve: (value)=>resolve(value)
|
|
115
128
|
});
|
|
116
129
|
});
|
|
117
130
|
};
|
|
131
|
+
}
|
|
132
|
+
class ConnectionManager extends EventEmitter {
|
|
133
|
+
static #instance;
|
|
134
|
+
#connections = new Map();
|
|
135
|
+
#connectionStatusListeners = new Map();
|
|
136
|
+
constructor(){
|
|
137
|
+
super();
|
|
138
|
+
}
|
|
139
|
+
static get instance() {
|
|
140
|
+
if (!ConnectionManager.#instance) ConnectionManager.#instance = new ConnectionManager();
|
|
141
|
+
return ConnectionManager.#instance;
|
|
142
|
+
}
|
|
143
|
+
getConnection(connectionId = DEFAULT_CONNECTION_ID) {
|
|
144
|
+
const existing = this.#connections.get(connectionId);
|
|
145
|
+
if (existing) return existing;
|
|
146
|
+
const connection = new ConnectionChannel((status)=>{
|
|
147
|
+
this.#connectionStatusListeners.get(connectionId)?.forEach((listener)=>listener(status));
|
|
148
|
+
if (connectionId === DEFAULT_CONNECTION_ID) this.emit("connection-status", status);
|
|
149
|
+
}, (metrics)=>{
|
|
150
|
+
if (connectionId === DEFAULT_CONNECTION_ID) this.emit("connection-metrics", metrics);
|
|
151
|
+
});
|
|
152
|
+
this.#connections.set(connectionId, connection);
|
|
153
|
+
return connection;
|
|
154
|
+
}
|
|
155
|
+
get connectionStatus() {
|
|
156
|
+
return this.connectionStatusFor(DEFAULT_CONNECTION_ID);
|
|
157
|
+
}
|
|
158
|
+
get connected() {
|
|
159
|
+
return this.connectedFor(DEFAULT_CONNECTION_ID);
|
|
160
|
+
}
|
|
161
|
+
connectionStatusFor(connectionId) {
|
|
162
|
+
return this.getConnection(connectionId).connectionStatus;
|
|
163
|
+
}
|
|
164
|
+
connectedFor(connectionId) {
|
|
165
|
+
return this.getConnection(connectionId).connected;
|
|
166
|
+
}
|
|
167
|
+
onConnectionStatus(connectionId, listener) {
|
|
168
|
+
const listeners = this.#connectionStatusListeners.get(connectionId) ?? new Set();
|
|
169
|
+
listeners.add(listener);
|
|
170
|
+
this.#connectionStatusListeners.set(connectionId, listeners);
|
|
171
|
+
return ()=>{
|
|
172
|
+
listeners.delete(listener);
|
|
173
|
+
if (0 === listeners.size) this.#connectionStatusListeners.delete(connectionId);
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
async connect(options, throwOnRejected = false) {
|
|
177
|
+
return this.connectTo(DEFAULT_CONNECTION_ID, options, throwOnRejected);
|
|
178
|
+
}
|
|
179
|
+
async connectTo(connectionId, options, throwOnRejected = false) {
|
|
180
|
+
const result = await this.connectWithLoginResponseTo(connectionId, options, throwOnRejected);
|
|
181
|
+
return "rejected" === result.status ? "connection-failed" : result.status;
|
|
182
|
+
}
|
|
183
|
+
async connectWithLoginResponseTo(connectionId, options, throwOnRejected = false) {
|
|
184
|
+
return this.getConnection(connectionId).connect(options, throwOnRejected);
|
|
185
|
+
}
|
|
186
|
+
disableActiveSubscriptions() {
|
|
187
|
+
for (const connection of this.#connections.values())connection.disableActiveSubscriptions();
|
|
188
|
+
}
|
|
189
|
+
enableActiveSubscriptions() {
|
|
190
|
+
for (const connection of this.#connections.values())connection.enableActiveSubscriptions();
|
|
191
|
+
}
|
|
192
|
+
disableActiveSubscriptionsFor(connectionId) {
|
|
193
|
+
this.getConnection(connectionId).disableActiveSubscriptions();
|
|
194
|
+
}
|
|
195
|
+
enableActiveSubscriptionsFor(connectionId) {
|
|
196
|
+
this.getConnection(connectionId).enableActiveSubscriptions();
|
|
197
|
+
}
|
|
198
|
+
get serverAPI() {
|
|
199
|
+
return this.serverAPIFor(DEFAULT_CONNECTION_ID);
|
|
200
|
+
}
|
|
201
|
+
serverAPIFor(connectionId) {
|
|
202
|
+
return this.getConnection(connectionId).serverAPI;
|
|
203
|
+
}
|
|
118
204
|
async disconnect() {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
205
|
+
return this.disconnectFrom(DEFAULT_CONNECTION_ID);
|
|
206
|
+
}
|
|
207
|
+
async disconnectFrom(connectionId) {
|
|
208
|
+
return this.getConnection(connectionId).disconnect();
|
|
209
|
+
}
|
|
210
|
+
async destroyConnection(connectionId) {
|
|
211
|
+
const connection = this.#connections.get(connectionId);
|
|
212
|
+
if (connection) {
|
|
213
|
+
await connection.disconnect();
|
|
214
|
+
connection.destroy();
|
|
215
|
+
this.#connections.delete(connectionId);
|
|
127
216
|
}
|
|
217
|
+
this.#connectionStatusListeners.delete(connectionId);
|
|
128
218
|
}
|
|
129
219
|
destroy() {
|
|
130
|
-
this.#
|
|
220
|
+
for (const connection of this.#connections.values())connection.destroy();
|
|
221
|
+
this.#connections.clear();
|
|
222
|
+
this.#connectionStatusListeners.clear();
|
|
131
223
|
}
|
|
132
224
|
}
|
|
133
225
|
var src_ConnectionManager = ConnectionManager.instance;
|
|
134
226
|
export default src_ConnectionManager;
|
|
227
|
+
export { DEFAULT_CONNECTION_ID };
|
package/src/DedicatedWorker.js
CHANGED
|
@@ -21,7 +21,10 @@ class DedicatedWorker {
|
|
|
21
21
|
if ("ready" === message.type) {
|
|
22
22
|
window.clearTimeout(timer);
|
|
23
23
|
deferredWorker.resolve(worker);
|
|
24
|
-
} else if ("connected" === message.type) this.#deferredConnection?.resolve(
|
|
24
|
+
} else if ("connected" === message.type) this.#deferredConnection?.resolve({
|
|
25
|
+
loginResponse: message.loginResponse,
|
|
26
|
+
status: "connected"
|
|
27
|
+
});
|
|
25
28
|
else if ("connection-failed" === message.type) this.#deferredConnection?.reject(message.reason);
|
|
26
29
|
else onMessage(message);
|
|
27
30
|
};
|
package/src/VuuDataSource.js
CHANGED
|
@@ -16,6 +16,7 @@ class VuuDataSource extends BaseDataSource {
|
|
|
16
16
|
bufferSize;
|
|
17
17
|
server = null;
|
|
18
18
|
rangeRequest;
|
|
19
|
+
#connectionId;
|
|
19
20
|
#allColumns;
|
|
20
21
|
#autosubscribeColumns = [];
|
|
21
22
|
#pendingVisualLink;
|
|
@@ -32,12 +33,13 @@ class VuuDataSource extends BaseDataSource {
|
|
|
32
33
|
#tableSchema;
|
|
33
34
|
isRemote = true;
|
|
34
35
|
table;
|
|
35
|
-
constructor({ session, sessionTableMessageColumn, ...props }){
|
|
36
|
+
constructor({ session, connectionId = "portal", sessionTableMessageColumn, ...props }){
|
|
36
37
|
super(props);
|
|
37
38
|
const { bufferSize = 100, table, visualLink } = props;
|
|
38
39
|
if (!table) throw Error("RemoteDataSource constructor called without table");
|
|
39
40
|
this.bufferSize = bufferSize;
|
|
40
41
|
this.table = table;
|
|
42
|
+
this.#connectionId = connectionId;
|
|
41
43
|
this.#pendingVisualLink = visualLink;
|
|
42
44
|
this.#session = session;
|
|
43
45
|
this.#sessionTableMessageColumn = sessionTableMessageColumn;
|
|
@@ -49,11 +51,15 @@ class VuuDataSource extends BaseDataSource {
|
|
|
49
51
|
}
|
|
50
52
|
async subscribe(subscribeProps, callback) {
|
|
51
53
|
super.subscribe(subscribeProps, callback);
|
|
52
|
-
|
|
54
|
+
let viewport = subscribeProps.viewport ?? this.viewport;
|
|
55
|
+
if (!viewport) {
|
|
56
|
+
viewport = uuid();
|
|
57
|
+
this.viewport = viewport;
|
|
58
|
+
}
|
|
53
59
|
if ("disabled" === this.#status || "disabling" === this.#status || "enabling" === this.#status) return void this.enable(callback);
|
|
54
60
|
if ("initialising" !== this.#status && "unsubscribed" !== this.#status) throw Error(`[VuuDataSource] invalid status ${this.#status} for subscribe`);
|
|
55
61
|
this.#status = "subscribing";
|
|
56
|
-
this.server = await ConnectionManager.
|
|
62
|
+
this.server = await ConnectionManager.serverAPIFor(this.#connectionId);
|
|
57
63
|
const { bufferSize } = this;
|
|
58
64
|
const { columns, ...dataSourceConfig } = combineFilters(this.config);
|
|
59
65
|
this.server?.subscribe({
|
|
@@ -434,6 +440,7 @@ class VuuDataSource extends BaseDataSource {
|
|
|
434
440
|
assertExpectedSessionTable(sessionTable, effectiveOverrides?.table);
|
|
435
441
|
const sessionDataSource = new VuuDataSource({
|
|
436
442
|
...sessionDataSourceConfig(this.config, effectiveOverrides?.columns),
|
|
443
|
+
connectionId: this.#connectionId,
|
|
437
444
|
table: sessionTable,
|
|
438
445
|
viewport: sessionTable.table
|
|
439
446
|
});
|
|
@@ -544,7 +551,7 @@ class VuuDataSource extends BaseDataSource {
|
|
|
544
551
|
};
|
|
545
552
|
}
|
|
546
553
|
async addRow(rowData = {}) {
|
|
547
|
-
const response = await this.rpcRequest
|
|
554
|
+
const response = await this.rpcRequest({
|
|
548
555
|
type: "RPC_REQUEST",
|
|
549
556
|
rpcName: "addRow",
|
|
550
557
|
params: {
|
|
@@ -81,7 +81,14 @@ class WebSocketConnection extends EventEmitter {
|
|
|
81
81
|
const timer = setTimeout(()=>{
|
|
82
82
|
throw Error(`Failed to open WebSocket connection to ${url}, timed out after ${connectionTimeout}ms`);
|
|
83
83
|
}, connectionTimeout);
|
|
84
|
-
|
|
84
|
+
console.log(`create websocket ${url} ${protocols}`);
|
|
85
|
+
try {
|
|
86
|
+
this.#ws = new WebSocket(url, protocols);
|
|
87
|
+
} catch (e) {
|
|
88
|
+
console.error(e);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const ws = this.#ws;
|
|
85
92
|
ws.onopen = ()=>{
|
|
86
93
|
this.connectionStatus = "websocket-open";
|
|
87
94
|
clearTimeout(timer);
|
package/src/index.js
CHANGED
|
@@ -3,9 +3,5 @@ export * from "./constants.js";
|
|
|
3
3
|
export * from "./data-source.js";
|
|
4
4
|
export * from "./message-utils.js";
|
|
5
5
|
export * from "./VuuDataSource.js";
|
|
6
|
-
export { authenticate, getVuuAuthToken, parseVuuUserFromToken } from "./authenticate.js";
|
|
7
6
|
export { default as ConnectionManager } from "./ConnectionManager.js";
|
|
8
|
-
export { LostConnectionHandler, RetryOptions } from "./LostConnectionHandler.js";
|
|
9
|
-
export { VuuAuthTokenIssuePolicy, VuuAuthenticator } from "./VuuAuthenticator.js";
|
|
10
|
-
export { VuuAuthProvider } from "./VuuAuthProvider.js";
|
|
11
7
|
export { isConnected } from "./WebSocketConnection.js";
|
package/src/inlined-worker.js
CHANGED
|
@@ -109,7 +109,9 @@ var EventEmitter = class {
|
|
|
109
109
|
}
|
|
110
110
|
invokeHandler(handler, args) {
|
|
111
111
|
if (isArrayOfListeners(handler)) {
|
|
112
|
-
handler.slice().forEach((listener) =>
|
|
112
|
+
handler.slice().forEach((listener) => {
|
|
113
|
+
this.invokeHandler(listener, args);
|
|
114
|
+
});
|
|
113
115
|
} else {
|
|
114
116
|
switch (args.length) {
|
|
115
117
|
case 0:
|
|
@@ -1648,7 +1650,14 @@ var WebSocketConnection = class extends EventEmitter {
|
|
|
1648
1650
|
\`Failed to open WebSocket connection to \${url}, timed out after \${connectionTimeout}ms\`
|
|
1649
1651
|
);
|
|
1650
1652
|
}, connectionTimeout);
|
|
1651
|
-
|
|
1653
|
+
console.log(\`create websocket \${url} \${protocols}\`);
|
|
1654
|
+
try {
|
|
1655
|
+
__privateSet(this, _ws, new WebSocket(url, protocols));
|
|
1656
|
+
} catch (e) {
|
|
1657
|
+
console.error(e);
|
|
1658
|
+
return;
|
|
1659
|
+
}
|
|
1660
|
+
const ws = __privateGet(this, _ws);
|
|
1652
1661
|
ws.onopen = () => {
|
|
1653
1662
|
this.connectionStatus = "websocket-open";
|
|
1654
1663
|
clearTimeout(timer);
|
|
@@ -2359,12 +2368,10 @@ var ServerProxy = class {
|
|
|
2359
2368
|
case "LOGIN_SUCCESS":
|
|
2360
2369
|
if (sessionId) {
|
|
2361
2370
|
this.sessionId = sessionId;
|
|
2362
|
-
|
|
2371
|
+
const loginResponse = { ...body, sessionId };
|
|
2372
|
+
(_a = this.pendingLogin) == null ? void 0 : _a.resolve(loginResponse);
|
|
2363
2373
|
this.pendingLogin = void 0;
|
|
2364
|
-
this.postMessageToClient(
|
|
2365
|
-
...body,
|
|
2366
|
-
sessionId
|
|
2367
|
-
});
|
|
2374
|
+
this.postMessageToClient(loginResponse);
|
|
2368
2375
|
} else {
|
|
2369
2376
|
throw Error("LOGIN_SUCCESS did not provide sessionId");
|
|
2370
2377
|
}
|
|
@@ -2758,12 +2765,15 @@ var handleMessageFromClient = async ({
|
|
|
2758
2765
|
switch (message.type) {
|
|
2759
2766
|
case "connect":
|
|
2760
2767
|
try {
|
|
2761
|
-
const
|
|
2768
|
+
const loginResponse = await connectToServer(
|
|
2762
2769
|
message.url,
|
|
2763
2770
|
message.protocol,
|
|
2764
2771
|
message.token
|
|
2765
2772
|
);
|
|
2766
|
-
|
|
2773
|
+
if (!loginResponse) {
|
|
2774
|
+
throw Error("VUU server did not return a LOGIN_SUCCESS response");
|
|
2775
|
+
}
|
|
2776
|
+
postMessage({ type: "connected", loginResponse });
|
|
2767
2777
|
} catch (err) {
|
|
2768
2778
|
postMessage({ type: "connection-failed", reason: String(err) });
|
|
2769
2779
|
}
|
|
@@ -514,12 +514,13 @@ class ServerProxy {
|
|
|
514
514
|
case "LOGIN_SUCCESS":
|
|
515
515
|
if (sessionId) {
|
|
516
516
|
this.sessionId = sessionId;
|
|
517
|
-
|
|
518
|
-
this.pendingLogin = void 0;
|
|
519
|
-
this.postMessageToClient({
|
|
517
|
+
const loginResponse = {
|
|
520
518
|
...body,
|
|
521
519
|
sessionId
|
|
522
|
-
}
|
|
520
|
+
};
|
|
521
|
+
this.pendingLogin?.resolve(loginResponse);
|
|
522
|
+
this.pendingLogin = void 0;
|
|
523
|
+
this.postMessageToClient(loginResponse);
|
|
523
524
|
} else throw Error("LOGIN_SUCCESS did not provide sessionId");
|
|
524
525
|
break;
|
|
525
526
|
case "REMOVE_VP_SUCCESS":
|
package/src/worker.js
CHANGED
|
@@ -30,10 +30,11 @@ const handleMessageFromClient = async ({ data: message })=>{
|
|
|
30
30
|
switch(message.type){
|
|
31
31
|
case "connect":
|
|
32
32
|
try {
|
|
33
|
-
const
|
|
33
|
+
const loginResponse = await connectToServer(message.url, message.protocol, message.token);
|
|
34
|
+
if (!loginResponse) throw Error("VUU server did not return a LOGIN_SUCCESS response");
|
|
34
35
|
postMessage({
|
|
35
36
|
type: "connected",
|
|
36
|
-
|
|
37
|
+
loginResponse
|
|
37
38
|
});
|
|
38
39
|
} catch (err) {
|
|
39
40
|
postMessage({
|
|
@@ -1,35 +1,63 @@
|
|
|
1
|
-
import { ConnectOptions, DataSourceCallbackMessage, ServerAPI } from "@vuu-ui/vuu-data-types";
|
|
1
|
+
import type { ConnectOptions, DataSourceCallbackMessage, ServerAPI } from "@vuu-ui/vuu-data-types";
|
|
2
|
+
import type { VuuLoginSuccessResponse } from "@vuu-ui/vuu-protocol-types";
|
|
2
3
|
import { EventEmitter } from "@vuu-ui/vuu-utils";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
4
|
+
import type { ConnectionQualityMetrics } from "@vuu-ui/vuu-data-types";
|
|
5
|
+
import type { ConnectionStatus, WebSocketConnectionEvents } from "./WebSocketConnection";
|
|
6
|
+
export declare const DEFAULT_CONNECTION_ID = "portal";
|
|
5
7
|
export type PostMessageToClientCallback = (msg: DataSourceCallbackMessage) => void;
|
|
6
8
|
export type ConnectionEvents = WebSocketConnectionEvents & {
|
|
7
9
|
"connection-metrics": (message: ConnectionQualityMetrics) => void;
|
|
8
10
|
};
|
|
11
|
+
export type ConnectionStatusListener = (status: ConnectionStatus) => void;
|
|
12
|
+
export type VuuConnectionResult = {
|
|
13
|
+
loginResponse: VuuLoginSuccessResponse & {
|
|
14
|
+
sessionId: string;
|
|
15
|
+
};
|
|
16
|
+
status: "connected" | "reconnected";
|
|
17
|
+
} | {
|
|
18
|
+
status: "rejected";
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Process-wide registry for VUU server connections.
|
|
22
|
+
*
|
|
23
|
+
* Consumers use the exported singleton to connect, obtain a deferred
|
|
24
|
+
* `ServerAPI`, and control subscriptions. The unscoped methods operate on the
|
|
25
|
+
* legacy `portal` connection, while the `*To` and `*For` methods select an
|
|
26
|
+
* independent connection by ID. This allows remote modules to share the manager
|
|
27
|
+
* without sharing authentication, worker state, or viewport subscriptions.
|
|
28
|
+
*
|
|
29
|
+
* Connection status and metrics events are emitted for the default connection
|
|
30
|
+
* to preserve the existing application-level monitoring API.
|
|
31
|
+
*/
|
|
9
32
|
declare class ConnectionManager extends EventEmitter<ConnectionEvents> {
|
|
10
33
|
#private;
|
|
11
34
|
private constructor();
|
|
12
35
|
static get instance(): ConnectionManager;
|
|
36
|
+
private getConnection;
|
|
13
37
|
get connectionStatus(): ConnectionStatus;
|
|
14
38
|
get connected(): boolean;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
39
|
+
connectionStatusFor(connectionId: string): ConnectionStatus;
|
|
40
|
+
connectedFor(connectionId: string): boolean;
|
|
41
|
+
onConnectionStatus(connectionId: string, listener: ConnectionStatusListener): () => void;
|
|
42
|
+
connect(options: ConnectOptions, throwOnRejected?: boolean): Promise<"connected" | "connection-failed" | "reconnected">;
|
|
43
|
+
connectTo(connectionId: string, options: ConnectOptions, throwOnRejected?: boolean): Promise<"connected" | "connection-failed" | "reconnected">;
|
|
44
|
+
connectWithLoginResponseTo(connectionId: string, options: ConnectOptions, throwOnRejected?: boolean): Promise<{
|
|
45
|
+
loginResponse: VuuLoginSuccessResponse & {
|
|
46
|
+
sessionId: string;
|
|
47
|
+
};
|
|
48
|
+
status: "connected" | "reconnected";
|
|
49
|
+
} | {
|
|
50
|
+
readonly status: "rejected";
|
|
51
|
+
}>;
|
|
26
52
|
disableActiveSubscriptions(): void;
|
|
27
53
|
enableActiveSubscriptions(): void;
|
|
28
|
-
|
|
54
|
+
disableActiveSubscriptionsFor(connectionId: string): void;
|
|
55
|
+
enableActiveSubscriptionsFor(connectionId: string): void;
|
|
29
56
|
get serverAPI(): Promise<ServerAPI>;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
57
|
+
serverAPIFor(connectionId: string): Promise<ServerAPI>;
|
|
58
|
+
disconnect(): Promise<"disconnected" | "rejected">;
|
|
59
|
+
disconnectFrom(connectionId: string): Promise<"disconnected" | "rejected">;
|
|
60
|
+
destroyConnection(connectionId: string): Promise<void>;
|
|
33
61
|
destroy(): void;
|
|
34
62
|
}
|
|
35
63
|
declare const _default: ConnectionManager;
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
import { ConnectOptions, VuuUIMessageIn, VuuUIMessageOut, WithRequestId } from "@vuu-ui/vuu-data-types";
|
|
2
|
-
import { SelectRequest, VuuCreateVisualLink, VuuRemoveVisualLink, VuuRpcMenuRequest, VuuRpcServiceRequest } from "@vuu-ui/vuu-protocol-types";
|
|
1
|
+
import type { ConnectOptions, VuuUIMessageIn, VuuUIMessageOut, WithRequestId } from "@vuu-ui/vuu-data-types";
|
|
2
|
+
import type { SelectRequest, VuuLoginSuccessResponse, VuuCreateVisualLink, VuuRemoveVisualLink, VuuRpcMenuRequest, VuuRpcServiceRequest } from "@vuu-ui/vuu-protocol-types";
|
|
3
3
|
export declare class DedicatedWorker {
|
|
4
4
|
#private;
|
|
5
5
|
constructor(onMessage: (msg: VuuUIMessageIn) => void);
|
|
6
|
-
connect(options: ConnectOptions): Promise<
|
|
6
|
+
connect(options: ConnectOptions): Promise<{
|
|
7
|
+
loginResponse: VuuLoginSuccessResponse & {
|
|
8
|
+
sessionId: string;
|
|
9
|
+
};
|
|
10
|
+
status: "connected" | "reconnected";
|
|
11
|
+
} | {
|
|
12
|
+
status: "rejected";
|
|
13
|
+
}>;
|
|
7
14
|
send(message: VuuUIMessageOut | WithRequestId<VuuCreateVisualLink | VuuRemoveVisualLink | VuuRpcServiceRequest | VuuRpcMenuRequest | SelectRequest>): Promise<void>;
|
|
8
15
|
terminate(): Promise<void>;
|
|
9
16
|
}
|
package/types/VuuDataSource.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export declare class VuuDataSource extends BaseDataSource implements DataSourceB
|
|
|
10
10
|
rangeRequest: RangeRequest;
|
|
11
11
|
readonly isRemote = true;
|
|
12
12
|
table: VuuTable;
|
|
13
|
-
constructor({ session, sessionTableMessageColumn, ...props }: DataSourceConstructorProps);
|
|
13
|
+
constructor({ session, connectionId, sessionTableMessageColumn, ...props }: DataSourceConstructorProps);
|
|
14
14
|
subscribe(subscribeProps: DataSourceSubscribeProps, callback: DataSourceSubscribeCallback): Promise<void>;
|
|
15
15
|
handleMessageFromServer: (message: DataSourceCallbackMessage) => void;
|
|
16
16
|
handleSessionMessageFromServer: (msg: DataSourceCallbackMessage) => void;
|
|
@@ -61,9 +61,9 @@ export declare class VuuDataSource extends BaseDataSource implements DataSourceB
|
|
|
61
61
|
remoteProcedureCall<T extends VuuRpcResponse = VuuRpcResponse>(): Promise<T>;
|
|
62
62
|
createSessionDataSource(copyOption: CopyOption, sessionType?: SessionType, overrides?: SessionDataSourceOverrides): Promise<VuuDataSource | undefined>;
|
|
63
63
|
beginEditSession(): Promise<VuuDataSource>;
|
|
64
|
-
editCell(key: string, column: string, data: VuuRowDataItemType): Promise<
|
|
64
|
+
editCell(key: string, column: string, data: VuuRowDataItemType): Promise<RpcResultError | RpcResultSuccess>;
|
|
65
65
|
endEditSession(saveChanges?: boolean, force?: boolean): Promise<void>;
|
|
66
|
-
rpcRequest(rpcRequest: Omit<VuuRpcServiceRequest, "context">): Promise<
|
|
66
|
+
rpcRequest(rpcRequest: Omit<VuuRpcServiceRequest, "context">): Promise<RpcResultError | RpcResultSuccess>;
|
|
67
67
|
menuRpcCall(rpcRequest: Omit<VuuRpcMenuRequest, "vpId">): Promise<MenuRpcResponse<any> | undefined>;
|
|
68
68
|
insertRow(): Promise<string>;
|
|
69
69
|
deleteRow(key: string, mode?: DeleteRowMode): Promise<true | string>;
|