@vuu-ui/vuu-data-remote 4.0.0-alpha.9 → 4.0.0-beta.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 +12 -12
- package/src/ConnectionManager.js +65 -158
- package/src/DedicatedWorker.js +1 -4
- package/src/LostConnectionHandler.js +89 -0
- package/src/VuuAuthProvider.js +78 -0
- package/src/VuuAuthenticator.js +36 -0
- package/src/VuuDataSource.js +7 -10
- package/src/WebSocketConnection.js +1 -8
- package/src/authenticate.js +50 -0
- package/src/index.js +4 -0
- package/src/inlined-worker.js +1 -2805
- package/src/server-proxy/array-backed-moving-window.js +12 -17
- package/src/server-proxy/server-proxy.js +4 -7
- package/src/worker.js +2 -3
- package/types/ConnectionManager.d.ts +17 -45
- package/types/DedicatedWorker.d.ts +3 -10
- package/types/VuuDataSource.d.ts +1 -1
- package/types/WebSocketConnection.d.ts +3 -3
- package/types/index.d.ts +4 -0
- package/types/inlined-worker.d.ts +1 -1
- package/types/server-proxy/server-proxy.d.ts +2 -4
- package/types/src/ConnectionManager.d.ts +0 -36
- package/types/src/DedicatedWorker.d.ts +0 -9
- package/types/src/LostConnectionHandler.d.ts +0 -28
- package/types/src/VuuAuthProvider.d.ts +0 -39
- package/types/src/VuuAuthenticator.d.ts +0 -21
- package/types/src/VuuDataSource.d.ts +0 -74
- package/types/src/WebSocketConnection.d.ts +0 -54
- package/types/src/authenticate.d.ts +0 -15
- package/types/src/constants.d.ts +0 -41
- package/types/src/data-source.d.ts +0 -6
- package/types/src/index.d.ts +0 -11
- package/types/src/inlined-worker.d.ts +0 -1
- package/types/src/message-utils.d.ts +0 -20
- package/types/src/server-proxy/array-backed-moving-window.d.ts +0 -27
- package/types/src/server-proxy/messages.d.ts +0 -33
- package/types/src/server-proxy/server-proxy.d.ts +0 -68
- package/types/src/server-proxy/viewport.d.ts +0 -132
- package/types/src/worker.d.ts +0 -1
package/package.json
CHANGED
|
@@ -1,26 +1,24 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "4.0.0-
|
|
2
|
+
"version": "4.0.0-beta.1",
|
|
3
3
|
"main": "./src/index.js",
|
|
4
4
|
"author": "heswell",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
|
-
"type": "module",
|
|
7
6
|
"scripts": {
|
|
8
|
-
"build": "node ../../scripts/
|
|
9
|
-
"build:dev": "node ../../scripts/
|
|
10
|
-
"build:worker": "node ../../scripts/
|
|
7
|
+
"build": "node ../../scripts/build-worker.ts && node ../../scripts/run-build-rslib.ts",
|
|
8
|
+
"build:dev": "node ../../scripts/build-worker.ts && node ../../scripts/run-build.mjs -- --dev",
|
|
9
|
+
"build:worker": "node ../../scripts/build-worker.ts",
|
|
11
10
|
"test": "vitest run",
|
|
12
11
|
"type-defs": "node ../../scripts/build-type-defs.mjs"
|
|
13
12
|
},
|
|
14
13
|
"devDependencies": {
|
|
15
|
-
"@vuu-ui/vuu-data-types": "4.0.0-
|
|
16
|
-
"@vuu-ui/vuu-table-types": "4.0.0-
|
|
17
|
-
"@vuu-ui/vuu-filter-types": "4.0.0-
|
|
18
|
-
"@vuu-ui/vuu-protocol-types": "4.0.0-
|
|
14
|
+
"@vuu-ui/vuu-data-types": "4.0.0-beta.1",
|
|
15
|
+
"@vuu-ui/vuu-table-types": "4.0.0-beta.1",
|
|
16
|
+
"@vuu-ui/vuu-filter-types": "4.0.0-beta.1",
|
|
17
|
+
"@vuu-ui/vuu-protocol-types": "4.0.0-beta.1"
|
|
19
18
|
},
|
|
20
19
|
"dependencies": {
|
|
21
|
-
"@vuu-ui/vuu-
|
|
22
|
-
"@vuu-ui/vuu-
|
|
23
|
-
"@vuu-ui/vuu-utils": "4.0.0-alpha.9"
|
|
20
|
+
"@vuu-ui/vuu-filter-parser": "4.0.0-beta.1",
|
|
21
|
+
"@vuu-ui/vuu-utils": "4.0.0-beta.1"
|
|
24
22
|
},
|
|
25
23
|
"files": [
|
|
26
24
|
"README.md",
|
|
@@ -30,10 +28,12 @@
|
|
|
30
28
|
"exports": {
|
|
31
29
|
".": {
|
|
32
30
|
"import": "./src/index.js",
|
|
31
|
+
"default": "./src/index.js",
|
|
33
32
|
"types": "./types/index.d.ts"
|
|
34
33
|
}
|
|
35
34
|
},
|
|
36
35
|
"module": "./src/index.js",
|
|
37
36
|
"name": "@vuu-ui/vuu-data-remote",
|
|
37
|
+
"type": "module",
|
|
38
38
|
"types": "types/index.d.ts"
|
|
39
39
|
}
|
package/src/ConnectionManager.js
CHANGED
|
@@ -2,55 +2,20 @@ 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
|
-
|
|
7
|
-
onConnectionStatus;
|
|
8
|
-
onConnectionMetrics;
|
|
5
|
+
class ConnectionManager extends EventEmitter {
|
|
6
|
+
static #instance;
|
|
9
7
|
#connectionStatus = "closed";
|
|
10
8
|
#deferredServerAPI = new DeferredPromise();
|
|
11
9
|
#pendingRequests = new Map();
|
|
12
10
|
#viewports = new Map();
|
|
13
11
|
#worker;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
this.onConnectionStatus = onConnectionStatus;
|
|
17
|
-
this.onConnectionMetrics = onConnectionMetrics;
|
|
12
|
+
constructor(){
|
|
13
|
+
super();
|
|
18
14
|
this.#worker = new DedicatedWorker(this.handleMessageFromWorker);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
};
|
|
15
|
+
}
|
|
16
|
+
static get instance() {
|
|
17
|
+
if (!ConnectionManager.#instance) ConnectionManager.#instance = new ConnectionManager();
|
|
18
|
+
return ConnectionManager.#instance;
|
|
54
19
|
}
|
|
55
20
|
get connectionStatus() {
|
|
56
21
|
return this.#connectionStatus;
|
|
@@ -58,45 +23,28 @@ class ConnectionChannel {
|
|
|
58
23
|
get connected() {
|
|
59
24
|
return "connected" === this.#connectionStatus || "reconnected" === this.#connectionStatus;
|
|
60
25
|
}
|
|
61
|
-
get serverAPI() {
|
|
62
|
-
return this.#deferredServerAPI.promise;
|
|
63
|
-
}
|
|
64
26
|
async connect(options, throwOnRejected = false) {
|
|
65
27
|
try {
|
|
66
28
|
const result = await this.#worker.connect(options);
|
|
67
|
-
if ("connected" === result
|
|
29
|
+
if ("connected" === result) this.#deferredServerAPI.resolve(this.connectedServerAPI);
|
|
68
30
|
return result;
|
|
69
31
|
} catch (err) {
|
|
70
|
-
if (throwOnRejected)
|
|
71
|
-
|
|
72
|
-
status: "rejected"
|
|
73
|
-
};
|
|
32
|
+
if (!throwOnRejected) return "connection-failed";
|
|
33
|
+
throw err;
|
|
74
34
|
}
|
|
75
35
|
}
|
|
76
36
|
disableActiveSubscriptions() {
|
|
37
|
+
console.log("[ConnectionManager] disableActiveSubscriptions");
|
|
77
38
|
this.#worker.send({
|
|
78
39
|
type: "disable-all-active"
|
|
79
40
|
});
|
|
80
41
|
}
|
|
81
42
|
enableActiveSubscriptions() {
|
|
43
|
+
console.log("[ConnectionManager] enableActiveSubscriptions");
|
|
82
44
|
this.#worker.send({
|
|
83
45
|
type: "enable-all-active"
|
|
84
46
|
});
|
|
85
47
|
}
|
|
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
|
-
}
|
|
100
48
|
handleMessageFromWorker = (message)=>{
|
|
101
49
|
if (shouldMessageBeRoutedToDataSource(message)) {
|
|
102
50
|
const viewport = this.#viewports.get(message.clientViewportId);
|
|
@@ -104,8 +52,8 @@ class ConnectionChannel {
|
|
|
104
52
|
else console.error(`[ConnectionManager] ${message.type} message received, viewport not found`);
|
|
105
53
|
} else if (isWebSocketConnectionStatus(message)) {
|
|
106
54
|
this.#connectionStatus = message;
|
|
107
|
-
this.
|
|
108
|
-
} else if (isConnectionQualityMetrics(message)) this.
|
|
55
|
+
this.emit("connection-status", message);
|
|
56
|
+
} else if (isConnectionQualityMetrics(message)) this.emit("connection-metrics", message);
|
|
109
57
|
else if (isRequestResponse(message)) {
|
|
110
58
|
const { requestId } = message;
|
|
111
59
|
if (this.#pendingRequests.has(requestId)) {
|
|
@@ -116,112 +64,71 @@ class ConnectionChannel {
|
|
|
116
64
|
} else console.warn("%cConnectionManager Unexpected message from the worker", "color:red;font-weight:bold;");
|
|
117
65
|
}
|
|
118
66
|
};
|
|
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
|
+
};
|
|
119
105
|
asyncRequest = (msg)=>{
|
|
120
106
|
const requestId = uuid();
|
|
121
107
|
this.#worker.send({
|
|
122
108
|
requestId,
|
|
123
109
|
...msg
|
|
124
110
|
});
|
|
125
|
-
return new Promise((resolve)=>{
|
|
111
|
+
return new Promise((resolve, reject)=>{
|
|
126
112
|
this.#pendingRequests.set(requestId, {
|
|
127
|
-
resolve
|
|
113
|
+
resolve,
|
|
114
|
+
reject
|
|
128
115
|
});
|
|
129
116
|
});
|
|
130
117
|
};
|
|
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
|
-
}
|
|
204
118
|
async disconnect() {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
await connection.disconnect();
|
|
214
|
-
connection.destroy();
|
|
215
|
-
this.#connections.delete(connectionId);
|
|
119
|
+
try {
|
|
120
|
+
this.#worker.send({
|
|
121
|
+
type: "disconnect"
|
|
122
|
+
});
|
|
123
|
+
this.#deferredServerAPI = new DeferredPromise();
|
|
124
|
+
return "disconnected";
|
|
125
|
+
} catch (err) {
|
|
126
|
+
return "rejected";
|
|
216
127
|
}
|
|
217
|
-
this.#connectionStatusListeners.delete(connectionId);
|
|
218
128
|
}
|
|
219
129
|
destroy() {
|
|
220
|
-
|
|
221
|
-
this.#connections.clear();
|
|
222
|
-
this.#connectionStatusListeners.clear();
|
|
130
|
+
this.#worker.terminate();
|
|
223
131
|
}
|
|
224
132
|
}
|
|
225
133
|
var src_ConnectionManager = ConnectionManager.instance;
|
|
226
134
|
export default src_ConnectionManager;
|
|
227
|
-
export { DEFAULT_CONNECTION_ID };
|
package/src/DedicatedWorker.js
CHANGED
|
@@ -21,10 +21,7 @@ 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(
|
|
25
|
-
loginResponse: message.loginResponse,
|
|
26
|
-
status: "connected"
|
|
27
|
-
});
|
|
24
|
+
} else if ("connected" === message.type) this.#deferredConnection?.resolve("connected");
|
|
28
25
|
else if ("connection-failed" === message.type) this.#deferredConnection?.reject(message.reason);
|
|
29
26
|
else onMessage(message);
|
|
30
27
|
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
function _to_primitive(input, hint) {
|
|
2
|
+
if ("object" !== _type_of(input) || null === input) return input;
|
|
3
|
+
var prim = input[Symbol.toPrimitive];
|
|
4
|
+
if (void 0 !== prim) {
|
|
5
|
+
var res = prim.call(input, hint || "default");
|
|
6
|
+
if ("object" !== _type_of(res)) return res;
|
|
7
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
8
|
+
}
|
|
9
|
+
return ("string" === hint ? String : Number)(input);
|
|
10
|
+
}
|
|
11
|
+
function _to_property_key(arg) {
|
|
12
|
+
var key = _to_primitive(arg, "string");
|
|
13
|
+
return "symbol" === _type_of(key) ? key : String(key);
|
|
14
|
+
}
|
|
15
|
+
function _type_of(obj) {
|
|
16
|
+
return obj && "u" > typeof Symbol && obj.constructor === Symbol ? "symbol" : typeof obj;
|
|
17
|
+
}
|
|
18
|
+
let _computedKey;
|
|
19
|
+
class RetryOptionsImpl {
|
|
20
|
+
retryIntervals;
|
|
21
|
+
ind = 0;
|
|
22
|
+
constructor(retryIntervals){
|
|
23
|
+
this.retryIntervals = retryIntervals;
|
|
24
|
+
console.log("[RetryOptionsImpl] constructor");
|
|
25
|
+
}
|
|
26
|
+
next() {
|
|
27
|
+
this.ind = Math.min(this.retryIntervals.length, this.ind + 1);
|
|
28
|
+
}
|
|
29
|
+
get remaining() {
|
|
30
|
+
return this.retryIntervals.length - this.ind;
|
|
31
|
+
}
|
|
32
|
+
get interval() {
|
|
33
|
+
if (this.ind === this.retryIntervals.length) return -1;
|
|
34
|
+
return 1000 * this.retryIntervals[this.ind];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function RetryOptions(retryIntervals) {
|
|
38
|
+
if (Array.isArray(retryIntervals)) return new RetryOptionsImpl(retryIntervals);
|
|
39
|
+
throw Error("[lostConnectionHandler] RetryOptions, invalid retryIntervals");
|
|
40
|
+
}
|
|
41
|
+
const defaultRetryIntervals = [
|
|
42
|
+
1,
|
|
43
|
+
2,
|
|
44
|
+
3,
|
|
45
|
+
5,
|
|
46
|
+
10,
|
|
47
|
+
30,
|
|
48
|
+
60,
|
|
49
|
+
120
|
|
50
|
+
];
|
|
51
|
+
_computedKey = _to_property_key(Symbol.asyncIterator);
|
|
52
|
+
class RetryGenerator {
|
|
53
|
+
vuuAuth;
|
|
54
|
+
options;
|
|
55
|
+
constructor(vuuAuth, options){
|
|
56
|
+
this.vuuAuth = vuuAuth;
|
|
57
|
+
this.options = options;
|
|
58
|
+
console.log("[RetryGenerator] constructor");
|
|
59
|
+
}
|
|
60
|
+
async *[_computedKey]() {
|
|
61
|
+
let connected = false;
|
|
62
|
+
do {
|
|
63
|
+
await new Promise((resolve)=>setTimeout(resolve, this.options.interval));
|
|
64
|
+
try {
|
|
65
|
+
await this.vuuAuth.login();
|
|
66
|
+
connected = true;
|
|
67
|
+
yield "connected";
|
|
68
|
+
} catch (err) {}
|
|
69
|
+
this.options.next();
|
|
70
|
+
}while (!connected && -1 !== this.options.interval)
|
|
71
|
+
if (!connected) yield "connection-failed";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
class LostConnectionHandler {
|
|
75
|
+
vuuAuth;
|
|
76
|
+
retryIntervals;
|
|
77
|
+
constructor(vuuAuth, retryIntervals = defaultRetryIntervals){
|
|
78
|
+
this.vuuAuth = vuuAuth;
|
|
79
|
+
this.retryIntervals = retryIntervals;
|
|
80
|
+
}
|
|
81
|
+
async reconnect() {
|
|
82
|
+
for await (const result of new RetryGenerator(this.vuuAuth, RetryOptions(this.retryIntervals))){
|
|
83
|
+
console.log(` ... async iterator result = ${result}`);
|
|
84
|
+
if ("connected" === result) return result;
|
|
85
|
+
}
|
|
86
|
+
return "connection-failed";
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export { LostConnectionHandler, RetryGenerator, RetryOptions };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { getCookieValue } from "@vuu-ui/vuu-utils";
|
|
2
|
+
import { parseVuuUserFromToken } from "./authenticate.js";
|
|
3
|
+
class VuuAuthProvider {
|
|
4
|
+
authEndpoint;
|
|
5
|
+
constructor(authEndpoint){
|
|
6
|
+
this.authEndpoint = authEndpoint;
|
|
7
|
+
}
|
|
8
|
+
login = async (username, password)=>{
|
|
9
|
+
const date = new Date();
|
|
10
|
+
const days = 1;
|
|
11
|
+
date.setTime(date.getTime() + 24 * days * 3600000);
|
|
12
|
+
if (username && password) {
|
|
13
|
+
const { token } = await this.getVuuTokenWithUsernameAndPassword(username, password);
|
|
14
|
+
document.cookie = `vuu-auth-user=${username};expires=${date.toUTCString()};path=/`;
|
|
15
|
+
document.cookie = `vuu-auth-password=${password};expires=${date.toUTCString()};path=/`;
|
|
16
|
+
document.cookie = `vuu-auth-token=${token};expires=${date.toUTCString()};path=/`;
|
|
17
|
+
return this.redirectToApplication();
|
|
18
|
+
}
|
|
19
|
+
{
|
|
20
|
+
const userName = getCookieValue("vuu-auth-user");
|
|
21
|
+
const password = getCookieValue("vuu-auth-password");
|
|
22
|
+
if (!userName || !password) return this.redirectToLoginPage();
|
|
23
|
+
{
|
|
24
|
+
const { authorizations, token } = await this.getVuuTokenWithUsernameAndPassword(userName, password);
|
|
25
|
+
document.cookie = `vuu-auth-token=${token};expires=${date.toUTCString()};path=/`;
|
|
26
|
+
return {
|
|
27
|
+
authorizations,
|
|
28
|
+
user: {
|
|
29
|
+
userName
|
|
30
|
+
},
|
|
31
|
+
token
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
async getVuuTokenWithUsernameAndPassword(username, password) {
|
|
37
|
+
const response = await fetch(this.authEndpoint, {
|
|
38
|
+
method: "POST",
|
|
39
|
+
credentials: "include",
|
|
40
|
+
headers: {
|
|
41
|
+
"Content-Type": "application/json",
|
|
42
|
+
"access-control-allow-origin": location.host
|
|
43
|
+
},
|
|
44
|
+
body: JSON.stringify({
|
|
45
|
+
username,
|
|
46
|
+
password
|
|
47
|
+
})
|
|
48
|
+
});
|
|
49
|
+
if (response.ok) {
|
|
50
|
+
const vuuAuthToken = response.headers.get("vuu-auth-token");
|
|
51
|
+
if ("string" == typeof vuuAuthToken && vuuAuthToken.length > 0) {
|
|
52
|
+
const { authorizations } = parseVuuUserFromToken(vuuAuthToken);
|
|
53
|
+
return {
|
|
54
|
+
authorizations,
|
|
55
|
+
token: vuuAuthToken
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
throw Error("Authentication failed auth token not returned by server");
|
|
59
|
+
}
|
|
60
|
+
throw Error(`Authentication failed, ${response.status}`);
|
|
61
|
+
}
|
|
62
|
+
clear() {
|
|
63
|
+
document.cookie = "vuu-auth-user= ; expires = Thu, 01 Jan 1970 00:00:00 GMT";
|
|
64
|
+
document.cookie = "vuu-auth-password= ; expires = Thu, 01 Jan 1970 00:00:00 GMT";
|
|
65
|
+
document.cookie = "vuu-auth-token= ; expires = Thu, 01 Jan 1970 00:00:00 GMT";
|
|
66
|
+
}
|
|
67
|
+
redirectToLoginPage() {
|
|
68
|
+
window.location.href = "login.html";
|
|
69
|
+
}
|
|
70
|
+
redirectToApplication() {
|
|
71
|
+
window.location.href = "index.html";
|
|
72
|
+
}
|
|
73
|
+
logout() {
|
|
74
|
+
this.clear();
|
|
75
|
+
this.redirectToLoginPage();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export { VuuAuthProvider };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import ConnectionManager from "./ConnectionManager.js";
|
|
2
|
+
const VuuAuthTokenIssuePolicy = {
|
|
3
|
+
BearerToken: "bearer-token",
|
|
4
|
+
UsernamePassword: "username-password"
|
|
5
|
+
};
|
|
6
|
+
class VuuAuthenticator {
|
|
7
|
+
authProvider;
|
|
8
|
+
authTokenIssuePolicy;
|
|
9
|
+
websocketUrl;
|
|
10
|
+
constructor({ authProvider, authTokenIssuePolicy = VuuAuthTokenIssuePolicy.BearerToken, websocketUrl }){
|
|
11
|
+
this.authProvider = authProvider;
|
|
12
|
+
this.authTokenIssuePolicy = authTokenIssuePolicy;
|
|
13
|
+
this.websocketUrl = websocketUrl;
|
|
14
|
+
}
|
|
15
|
+
openWebsocketConnection = async (vuuToken)=>{
|
|
16
|
+
await ConnectionManager.connect({
|
|
17
|
+
url: this.websocketUrl,
|
|
18
|
+
token: vuuToken
|
|
19
|
+
}, true);
|
|
20
|
+
};
|
|
21
|
+
login = async ()=>{
|
|
22
|
+
const { authorizations, user, token, websocket = true } = await this.authProvider.login();
|
|
23
|
+
if (token && user) {
|
|
24
|
+
if (websocket) await this.openWebsocketConnection(token);
|
|
25
|
+
return [
|
|
26
|
+
user,
|
|
27
|
+
authorizations
|
|
28
|
+
];
|
|
29
|
+
}
|
|
30
|
+
throw Error("[VuuAuthenticator] login failed");
|
|
31
|
+
};
|
|
32
|
+
logout = ()=>{
|
|
33
|
+
this.authProvider.logout();
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export { VuuAuthTokenIssuePolicy, VuuAuthenticator };
|
package/src/VuuDataSource.js
CHANGED
|
@@ -16,7 +16,6 @@ class VuuDataSource extends BaseDataSource {
|
|
|
16
16
|
bufferSize;
|
|
17
17
|
server = null;
|
|
18
18
|
rangeRequest;
|
|
19
|
-
#connectionId;
|
|
20
19
|
#allColumns;
|
|
21
20
|
#autosubscribeColumns = [];
|
|
22
21
|
#pendingVisualLink;
|
|
@@ -33,13 +32,12 @@ class VuuDataSource extends BaseDataSource {
|
|
|
33
32
|
#tableSchema;
|
|
34
33
|
isRemote = true;
|
|
35
34
|
table;
|
|
36
|
-
constructor({ session,
|
|
35
|
+
constructor({ session, sessionTableMessageColumn, ...props }){
|
|
37
36
|
super(props);
|
|
38
37
|
const { bufferSize = 100, table, visualLink } = props;
|
|
39
38
|
if (!table) throw Error("RemoteDataSource constructor called without table");
|
|
40
39
|
this.bufferSize = bufferSize;
|
|
41
40
|
this.table = table;
|
|
42
|
-
this.#connectionId = connectionId;
|
|
43
41
|
this.#pendingVisualLink = visualLink;
|
|
44
42
|
this.#session = session;
|
|
45
43
|
this.#sessionTableMessageColumn = sessionTableMessageColumn;
|
|
@@ -51,15 +49,11 @@ class VuuDataSource extends BaseDataSource {
|
|
|
51
49
|
}
|
|
52
50
|
async subscribe(subscribeProps, callback) {
|
|
53
51
|
super.subscribe(subscribeProps, callback);
|
|
54
|
-
|
|
55
|
-
if (!viewport) {
|
|
56
|
-
viewport = uuid();
|
|
57
|
-
this.viewport = viewport;
|
|
58
|
-
}
|
|
52
|
+
const { viewport = this.viewport || (this.viewport = uuid()) } = subscribeProps;
|
|
59
53
|
if ("disabled" === this.#status || "disabling" === this.#status || "enabling" === this.#status) return void this.enable(callback);
|
|
60
54
|
if ("initialising" !== this.#status && "unsubscribed" !== this.#status) throw Error(`[VuuDataSource] invalid status ${this.#status} for subscribe`);
|
|
61
55
|
this.#status = "subscribing";
|
|
62
|
-
this.server = await ConnectionManager.
|
|
56
|
+
this.server = await ConnectionManager.serverAPI;
|
|
63
57
|
const { bufferSize } = this;
|
|
64
58
|
const { columns, ...dataSourceConfig } = combineFilters(this.config);
|
|
65
59
|
this.server?.subscribe({
|
|
@@ -440,7 +434,6 @@ class VuuDataSource extends BaseDataSource {
|
|
|
440
434
|
assertExpectedSessionTable(sessionTable, effectiveOverrides?.table);
|
|
441
435
|
const sessionDataSource = new VuuDataSource({
|
|
442
436
|
...sessionDataSourceConfig(this.config, effectiveOverrides?.columns),
|
|
443
|
-
connectionId: this.#connectionId,
|
|
444
437
|
table: sessionTable,
|
|
445
438
|
viewport: sessionTable.table
|
|
446
439
|
});
|
|
@@ -483,6 +476,10 @@ class VuuDataSource extends BaseDataSource {
|
|
|
483
476
|
});
|
|
484
477
|
}
|
|
485
478
|
async endEditSession(saveChanges = false, force = false) {
|
|
479
|
+
if ("unsubscribed" === this.#status || !this.server || !this.viewport) {
|
|
480
|
+
if (!saveChanges) return;
|
|
481
|
+
throw Error("Cannot save changes: server or viewport are undefined");
|
|
482
|
+
}
|
|
486
483
|
const type = "RPC_REQUEST";
|
|
487
484
|
const rpcName = "endEditSession";
|
|
488
485
|
const rpcResponse = await this.rpcRequest(saveChanges ? {
|
|
@@ -81,14 +81,7 @@ 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
|
-
|
|
85
|
-
try {
|
|
86
|
-
this.#ws = new WebSocket(url, protocols);
|
|
87
|
-
} catch (e) {
|
|
88
|
-
console.error(e);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
const ws = this.#ws;
|
|
84
|
+
const ws = this.#ws = new WebSocket(url, protocols);
|
|
92
85
|
ws.onopen = ()=>{
|
|
93
86
|
this.connectionStatus = "websocket-open";
|
|
94
87
|
clearTimeout(timer);
|