Comparing version 0.6.3 to 0.6.4
{ | ||
"name": "aurumjs", | ||
"version": "0.6.3", | ||
"version": "0.6.4", | ||
"description": "Stream based declarative DOM rendering library for javascript", | ||
@@ -5,0 +5,0 @@ "main": "prebuilt/cjs/aurumjs.js", |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.syncDuplexDataSource = exports.syncArrayDataSource = exports.syncDataSource = exports.RemoteProtocol = void 0; | ||
const aurumjs_1 = require("../aurumjs"); | ||
var RemoteProtocol; | ||
@@ -22,12 +23,13 @@ (function (RemoteProtocol) { | ||
async function syncDataSource(source, aurumServerInfo, cancellation) { | ||
var _a; | ||
const key = `${aurumServerInfo.protocol}${(_a = aurumServerInfo.host) !== null && _a !== void 0 ? _a : location.host}`; | ||
await ensureConnection(key, aurumServerInfo); | ||
const key = makeKey(aurumServerInfo.protocol, aurumServerInfo.host); | ||
await ensureConnection(key, aurumServerInfo.protocol, aurumServerInfo.host); | ||
connections.get(key).syncDataSource(source, aurumServerInfo.id, aurumServerInfo.authenticationToken, cancellation); | ||
} | ||
exports.syncDataSource = syncDataSource; | ||
function makeKey(protocol, host) { | ||
return `${protocol !== null && protocol !== void 0 ? protocol : ''}${host !== null && host !== void 0 ? host : location.host}`; | ||
} | ||
async function syncArrayDataSource(source, aurumServerInfo, cancellation) { | ||
var _a; | ||
const key = `${aurumServerInfo.protocol}${(_a = aurumServerInfo.host) !== null && _a !== void 0 ? _a : location.host}`; | ||
await ensureConnection(key, aurumServerInfo); | ||
const key = makeKey(aurumServerInfo.protocol, aurumServerInfo.host); | ||
await ensureConnection(key, aurumServerInfo.protocol, aurumServerInfo.host); | ||
connections.get(key).syncArrayDataSource(source, aurumServerInfo.id, aurumServerInfo.authenticationToken, cancellation); | ||
@@ -37,5 +39,4 @@ } | ||
async function syncDuplexDataSource(source, aurumServerInfo, cancellation) { | ||
var _a; | ||
const key = `${aurumServerInfo.protocol}${(_a = aurumServerInfo.host) !== null && _a !== void 0 ? _a : location.host}`; | ||
await ensureConnection(key, aurumServerInfo); | ||
const key = makeKey(aurumServerInfo.protocol, aurumServerInfo.host); | ||
await ensureConnection(key, aurumServerInfo.protocol, aurumServerInfo.host); | ||
connections.get(key).syncDuplexDataSource(source, aurumServerInfo.id, aurumServerInfo.authenticationToken, cancellation); | ||
@@ -45,4 +46,6 @@ } | ||
const connections = new Map(); | ||
const pendingConnections = new Map(); | ||
class AurumServerClient { | ||
constructor(connection) { | ||
this.masterToken = new aurumjs_1.CancellationToken(); | ||
this.connection = connection; | ||
@@ -55,4 +58,5 @@ this.synchedDataSources = new Map(); | ||
cancellation.addCancelable(() => { | ||
const listeners = this.synchedDataSources.get(id); | ||
listeners.splice(listeners.indexOf(dataSource)); | ||
const listenersByAuth = this.synchedDataSources.get(id); | ||
const listeners = listenersByAuth.get(authenticationToken); | ||
listeners.splice(listeners.findIndex((s) => s.source === dataSource)); | ||
if (listeners.length === 0) { | ||
@@ -67,2 +71,5 @@ this.connection.send(JSON.stringify({ | ||
if (!this.synchedDataSources.has(id)) { | ||
this.synchedDataSources.set(id, new Map()); | ||
} | ||
if (!this.synchedDataSources.get(id).has(authenticationToken)) { | ||
this.connection.send(JSON.stringify({ | ||
@@ -73,6 +80,6 @@ type: RemoteProtocol.LISTEN_DATASOURCE, | ||
})); | ||
this.synchedDataSources.set(id, [dataSource]); | ||
this.synchedDataSources.get(id).set(id, [{ source: dataSource, token: cancellation }]); | ||
} | ||
else { | ||
this.synchedDataSources.get(id).push(dataSource); | ||
this.synchedDataSources.get(id).get(authenticationToken).push({ source: dataSource, token: cancellation }); | ||
} | ||
@@ -82,4 +89,5 @@ } | ||
cancellation.addCancelable(() => { | ||
const listeners = this.synchedArrayDataSources.get(id); | ||
listeners.splice(listeners.indexOf(dataSource)); | ||
const listenersByAuth = this.synchedArrayDataSources.get(id); | ||
const listeners = listenersByAuth.get(authenticationToken); | ||
listeners.splice(listeners.findIndex((s) => s.source === dataSource)); | ||
if (listeners.length === 0) { | ||
@@ -94,2 +102,5 @@ this.connection.send(JSON.stringify({ | ||
if (!this.synchedArrayDataSources.has(id)) { | ||
this.synchedArrayDataSources.set(id, new Map()); | ||
} | ||
if (!this.synchedArrayDataSources.get(id).has(authenticationToken)) { | ||
this.connection.send(JSON.stringify({ | ||
@@ -100,6 +111,6 @@ type: RemoteProtocol.LISTEN_ARRAY_DATASOURCE, | ||
})); | ||
this.synchedArrayDataSources.set(id, [dataSource]); | ||
this.synchedArrayDataSources.get(id).set(id, [{ source: dataSource, token: cancellation }]); | ||
} | ||
else { | ||
this.synchedArrayDataSources.get(id).push(dataSource); | ||
this.synchedArrayDataSources.get(id).get(authenticationToken).push({ source: dataSource, token: cancellation }); | ||
} | ||
@@ -109,4 +120,5 @@ } | ||
cancellation.addCancelable(() => { | ||
const listeners = this.synchedDuplexDataSources.get(id); | ||
listeners.splice(listeners.indexOf(dataSource)); | ||
const listenersByAuth = this.synchedDuplexDataSources.get(id); | ||
const listeners = listenersByAuth.get(authenticationToken); | ||
listeners.splice(listeners.findIndex((s) => s.source === dataSource)); | ||
if (listeners.length === 0) { | ||
@@ -127,4 +139,7 @@ this.connection.send(JSON.stringify({ | ||
})); | ||
}); | ||
if (!this.synchedDataSources.has(id)) { | ||
}, aurumjs_1.CancellationToken.fromMultiple([cancellation, this.masterToken])); | ||
if (!this.synchedDuplexDataSources.has(id)) { | ||
this.synchedDuplexDataSources.set(id, new Map()); | ||
} | ||
if (!this.synchedDuplexDataSources.get(id).has(authenticationToken)) { | ||
this.connection.send(JSON.stringify({ | ||
@@ -135,9 +150,14 @@ type: RemoteProtocol.LISTEN_DUPLEX_DATASOURCE, | ||
})); | ||
this.synchedDuplexDataSources.set(id, [dataSource]); | ||
this.synchedDuplexDataSources.get(id).set(id, [{ source: dataSource, token: cancellation }]); | ||
} | ||
else { | ||
this.synchedDuplexDataSources.get(id).push(dataSource); | ||
this.synchedDuplexDataSources.get(id).get(authenticationToken).push({ source: dataSource, token: cancellation }); | ||
} | ||
} | ||
static connect(host, protocol) { | ||
let pendingToken = new aurumjs_1.CancellationToken(); | ||
let started = false; | ||
let latency = 0; | ||
let latencyTs; | ||
let lastBeat; | ||
return new Promise((resolve, reject) => { | ||
@@ -157,11 +177,26 @@ if (!protocol) { | ||
const client = new AurumServerClient(connection); | ||
client.masterToken.addCancelable(() => { | ||
connections.delete(makeKey(host, protocol)); | ||
}); | ||
pendingToken.setTimeout(() => { | ||
connection.close(4001, 'no response'); | ||
reject(); | ||
client.masterToken.cancel(); | ||
}, 5000); | ||
connection.addEventListener('message', (m) => { | ||
lastBeat = Date.now(); | ||
try { | ||
const msg = JSON.parse(m.data); | ||
switch (msg.type) { | ||
case RemoteProtocol.HEARTBEAT: | ||
latency = Date.now() - latencyTs; | ||
console.log(`AurumServer latency: ${latency}ms`); | ||
break; | ||
case RemoteProtocol.UPDATE_DATASOURCE: | ||
if (client.synchedDataSources.has(msg.id)) { | ||
const dss = client.synchedDataSources.get(msg.id); | ||
for (const ds of dss) { | ||
ds.update(msg.value); | ||
const byAuth = client.synchedDataSources.get(msg.id); | ||
for (const dss of byAuth.values()) { | ||
for (const ds of dss) { | ||
ds.source.update(msg.value); | ||
} | ||
} | ||
@@ -172,36 +207,7 @@ } | ||
if (client.synchedArrayDataSources.has(msg.id)) { | ||
const dss = client.synchedArrayDataSources.get(msg.id); | ||
const change = msg.change; | ||
for (const ds of dss) { | ||
switch (change.operationDetailed) { | ||
case 'append': | ||
ds.appendArray(change.items); | ||
break; | ||
case 'clear': | ||
ds.clear(); | ||
break; | ||
case 'insert': | ||
ds.insertAt(change.index, ...change.items); | ||
break; | ||
case 'merge': | ||
ds.merge(change.items); | ||
break; | ||
case 'prepend': | ||
ds.unshift(change.items); | ||
break; | ||
case 'remove': | ||
ds.removeRange(change.index, change.index + change.count); | ||
break; | ||
case 'removeLeft': | ||
ds.removeLeft(change.count); | ||
break; | ||
case 'removeRight': | ||
ds.removeRight(change.count); | ||
break; | ||
case 'replace': | ||
ds.set(change.index, change.items[0]); | ||
break; | ||
case 'swap': | ||
ds.swap(change.index, change.index2); | ||
break; | ||
const byAuth = client.synchedArrayDataSources.get(msg.id); | ||
for (const dss of byAuth.values()) { | ||
const change = msg.change; | ||
for (const ds of dss) { | ||
ds.source.applyCollectionChange(change); | ||
} | ||
@@ -213,5 +219,7 @@ } | ||
if (client.synchedDuplexDataSources.has(msg.id)) { | ||
const dss = client.synchedDuplexDataSources.get(msg.id); | ||
for (const ds of dss) { | ||
ds.updateDownstream(msg.value); | ||
const byAuth = client.synchedDuplexDataSources.get(msg.id); | ||
for (const dss of byAuth.values()) { | ||
for (const ds of dss) { | ||
ds.source.updateDownstream(msg.value); | ||
} | ||
} | ||
@@ -227,9 +235,65 @@ } | ||
}); | ||
connection.addEventListener('error', (e) => reject(e)); | ||
connection.addEventListener('open', () => resolve(client)); | ||
connection.addEventListener('error', (e) => { | ||
client.masterToken.cancel(); | ||
reject(e); | ||
}); | ||
connection.addEventListener('open', () => { | ||
pendingToken.cancel(); | ||
pendingToken = undefined; | ||
started = true; | ||
lastBeat = Date.now(); | ||
client.masterToken.setInterval(() => { | ||
if (Date.now() - lastBeat > 10000) { | ||
connection.close(4000, 'timeout'); | ||
return; | ||
} | ||
latencyTs = Date.now(); | ||
connection.send(JSON.stringify({ | ||
type: RemoteProtocol.HEARTBEAT | ||
})); | ||
}, 2500); | ||
resolve(client); | ||
}); | ||
connection.addEventListener('close', () => { | ||
client.masterToken.cancel(); | ||
if (started) { | ||
ensureConnection(makeKey(host, protocol), protocol, host).then((newClient) => { | ||
newClient.migrate(client); | ||
}); | ||
} | ||
else { | ||
reject(); | ||
} | ||
}); | ||
}); | ||
} | ||
migrate(client) { | ||
for (const id of client.synchedDataSources.keys()) { | ||
for (const auth of client.synchedDataSources.get(id).keys()) { | ||
for (const { source, token } of client.synchedDataSources.get(id).get(auth)) { | ||
this.syncDataSource(source, id, auth, token); | ||
} | ||
} | ||
} | ||
for (const id of client.synchedArrayDataSources.keys()) { | ||
for (const auth of client.synchedArrayDataSources.get(id).keys()) { | ||
for (const { source, token } of client.synchedArrayDataSources.get(id).get(auth)) { | ||
this.syncArrayDataSource(source, id, auth, token); | ||
} | ||
} | ||
} | ||
for (const id of client.synchedDuplexDataSources.keys()) { | ||
for (const auth of client.synchedDuplexDataSources.get(id).keys()) { | ||
for (const { source, token } of client.synchedDuplexDataSources.get(id).get(auth)) { | ||
this.syncDuplexDataSource(source, id, auth, token); | ||
} | ||
} | ||
} | ||
client.synchedDataSources = undefined; | ||
client.synchedArrayDataSources = undefined; | ||
client.synchedDuplexDataSources = undefined; | ||
} | ||
} | ||
const pendingConnections = new Map(); | ||
async function ensureConnection(key, aurumServerInfo) { | ||
async function ensureConnection(key, protocol, host) { | ||
let backoff = 1000; | ||
if (pendingConnections.has(key)) { | ||
@@ -239,8 +303,25 @@ return pendingConnections.get(key); | ||
if (!connections.has(key)) { | ||
const p = AurumServerClient.connect(aurumServerInfo.host, aurumServerInfo.protocol); | ||
pendingConnections.set(key, p); | ||
connections.set(key, await p); | ||
pendingConnections.delete(key); | ||
const pendingConnection = new Promise((resolve) => { | ||
async function tryConnect() { | ||
const p = AurumServerClient.connect(host, protocol); | ||
try { | ||
const client = await p; | ||
connections.set(key, client); | ||
pendingConnections.delete(key); | ||
resolve(client); | ||
backoff = 1000; | ||
} | ||
catch (e) { | ||
setTimeout(() => { | ||
backoff += 1000; | ||
tryConnect(); | ||
}, backoff); | ||
} | ||
} | ||
tryConnect(); | ||
}); | ||
pendingConnections.set(key, pendingConnection); | ||
return pendingConnection; | ||
} | ||
} | ||
//# sourceMappingURL=aurum_server_client.js.map |
@@ -115,2 +115,3 @@ import { HTMLNodeProps } from '../builtin_compoents/dom_adapter'; | ||
sandbox?: AttributeValue; | ||
frameborder?: AttributeValue; | ||
} | ||
@@ -117,0 +118,0 @@ /** |
@@ -279,2 +279,5 @@ import { AurumServerInfo } from '../aurum_server/aurum_server_client'; | ||
listenAndRepeat(callback: Callback<CollectionChange<T>>, cancellationToken?: CancellationToken): Callback<void>; | ||
/** | ||
* Sends a reset signal followed by an append with all items signal. This will force all the views of this source the synchronize can be useful in case your views rely on non pure transformation functions. | ||
*/ | ||
repeatCurrentState(): void; | ||
@@ -284,2 +287,7 @@ listen(callback: Callback<CollectionChange<T>>, cancellationToken?: CancellationToken): Callback<void>; | ||
/** | ||
* Applies the changes described in the colleciton change to the array. Useful for synchronizing array data sources over the network or workers by serializing the changes and sending them over | ||
* @param collectionChange | ||
*/ | ||
applyCollectionChange(collectionChange: CollectionChange<T>): void; | ||
/** | ||
* Returns a promise that resolves when the next update occurs | ||
@@ -286,0 +294,0 @@ * @param cancellationToken |
@@ -322,2 +322,5 @@ "use strict"; | ||
} | ||
/** | ||
* Sends a reset signal followed by an append with all items signal. This will force all the views of this source the synchronize can be useful in case your views rely on non pure transformation functions. | ||
*/ | ||
repeatCurrentState() { | ||
@@ -348,2 +351,40 @@ this.update({ | ||
/** | ||
* Applies the changes described in the colleciton change to the array. Useful for synchronizing array data sources over the network or workers by serializing the changes and sending them over | ||
* @param collectionChange | ||
*/ | ||
applyCollectionChange(collectionChange) { | ||
switch (collectionChange.operationDetailed) { | ||
case 'append': | ||
this.appendArray(collectionChange.items); | ||
break; | ||
case 'clear': | ||
this.clear(); | ||
break; | ||
case 'insert': | ||
this.insertAt(collectionChange.index, ...collectionChange.items); | ||
break; | ||
case 'merge': | ||
this.merge(collectionChange.items); | ||
break; | ||
case 'prepend': | ||
this.unshift(...collectionChange.items); | ||
break; | ||
case 'remove': | ||
this.removeRange(collectionChange.index, collectionChange.index + collectionChange.count); | ||
break; | ||
case 'removeLeft': | ||
this.removeLeft(collectionChange.count); | ||
break; | ||
case 'removeRight': | ||
this.removeRight(collectionChange.count); | ||
break; | ||
case 'replace': | ||
this.set(collectionChange.index, collectionChange.items[0]); | ||
break; | ||
case 'swap': | ||
this.swap(collectionChange.index, collectionChange.index2); | ||
break; | ||
} | ||
} | ||
/** | ||
* Returns a promise that resolves when the next update occurs | ||
@@ -350,0 +391,0 @@ * @param cancellationToken |
@@ -7,2 +7,3 @@ import { Delegate, Callback } from './common'; | ||
constructor(...cancellables: Delegate[]); | ||
static fromMultiple(tokens: CancellationToken[]): CancellationToken; | ||
hasCancellables(): boolean; | ||
@@ -9,0 +10,0 @@ /** |
@@ -12,2 +12,9 @@ "use strict"; | ||
} | ||
static fromMultiple(tokens) { | ||
const result = new CancellationToken(); | ||
for (const token of tokens) { | ||
token.chain(result); | ||
} | ||
return result; | ||
} | ||
hasCancellables() { | ||
@@ -14,0 +21,0 @@ return this.cancelables.length > 0; |
@@ -0,1 +1,2 @@ | ||
import { CancellationToken } from '../aurumjs'; | ||
export var RemoteProtocol; | ||
@@ -19,19 +20,24 @@ (function (RemoteProtocol) { | ||
export async function syncDataSource(source, aurumServerInfo, cancellation) { | ||
const key = `${aurumServerInfo.protocol}${aurumServerInfo.host ?? location.host}`; | ||
await ensureConnection(key, aurumServerInfo); | ||
const key = makeKey(aurumServerInfo.protocol, aurumServerInfo.host); | ||
await ensureConnection(key, aurumServerInfo.protocol, aurumServerInfo.host); | ||
connections.get(key).syncDataSource(source, aurumServerInfo.id, aurumServerInfo.authenticationToken, cancellation); | ||
} | ||
function makeKey(protocol, host) { | ||
return `${protocol ?? ''}${host ?? location.host}`; | ||
} | ||
export async function syncArrayDataSource(source, aurumServerInfo, cancellation) { | ||
const key = `${aurumServerInfo.protocol}${aurumServerInfo.host ?? location.host}`; | ||
await ensureConnection(key, aurumServerInfo); | ||
const key = makeKey(aurumServerInfo.protocol, aurumServerInfo.host); | ||
await ensureConnection(key, aurumServerInfo.protocol, aurumServerInfo.host); | ||
connections.get(key).syncArrayDataSource(source, aurumServerInfo.id, aurumServerInfo.authenticationToken, cancellation); | ||
} | ||
export async function syncDuplexDataSource(source, aurumServerInfo, cancellation) { | ||
const key = `${aurumServerInfo.protocol}${aurumServerInfo.host ?? location.host}`; | ||
await ensureConnection(key, aurumServerInfo); | ||
const key = makeKey(aurumServerInfo.protocol, aurumServerInfo.host); | ||
await ensureConnection(key, aurumServerInfo.protocol, aurumServerInfo.host); | ||
connections.get(key).syncDuplexDataSource(source, aurumServerInfo.id, aurumServerInfo.authenticationToken, cancellation); | ||
} | ||
const connections = new Map(); | ||
const pendingConnections = new Map(); | ||
class AurumServerClient { | ||
constructor(connection) { | ||
this.masterToken = new CancellationToken(); | ||
this.connection = connection; | ||
@@ -44,4 +50,5 @@ this.synchedDataSources = new Map(); | ||
cancellation.addCancelable(() => { | ||
const listeners = this.synchedDataSources.get(id); | ||
listeners.splice(listeners.indexOf(dataSource)); | ||
const listenersByAuth = this.synchedDataSources.get(id); | ||
const listeners = listenersByAuth.get(authenticationToken); | ||
listeners.splice(listeners.findIndex((s) => s.source === dataSource)); | ||
if (listeners.length === 0) { | ||
@@ -56,2 +63,5 @@ this.connection.send(JSON.stringify({ | ||
if (!this.synchedDataSources.has(id)) { | ||
this.synchedDataSources.set(id, new Map()); | ||
} | ||
if (!this.synchedDataSources.get(id).has(authenticationToken)) { | ||
this.connection.send(JSON.stringify({ | ||
@@ -62,6 +72,6 @@ type: RemoteProtocol.LISTEN_DATASOURCE, | ||
})); | ||
this.synchedDataSources.set(id, [dataSource]); | ||
this.synchedDataSources.get(id).set(id, [{ source: dataSource, token: cancellation }]); | ||
} | ||
else { | ||
this.synchedDataSources.get(id).push(dataSource); | ||
this.synchedDataSources.get(id).get(authenticationToken).push({ source: dataSource, token: cancellation }); | ||
} | ||
@@ -71,4 +81,5 @@ } | ||
cancellation.addCancelable(() => { | ||
const listeners = this.synchedArrayDataSources.get(id); | ||
listeners.splice(listeners.indexOf(dataSource)); | ||
const listenersByAuth = this.synchedArrayDataSources.get(id); | ||
const listeners = listenersByAuth.get(authenticationToken); | ||
listeners.splice(listeners.findIndex((s) => s.source === dataSource)); | ||
if (listeners.length === 0) { | ||
@@ -83,2 +94,5 @@ this.connection.send(JSON.stringify({ | ||
if (!this.synchedArrayDataSources.has(id)) { | ||
this.synchedArrayDataSources.set(id, new Map()); | ||
} | ||
if (!this.synchedArrayDataSources.get(id).has(authenticationToken)) { | ||
this.connection.send(JSON.stringify({ | ||
@@ -89,6 +103,6 @@ type: RemoteProtocol.LISTEN_ARRAY_DATASOURCE, | ||
})); | ||
this.synchedArrayDataSources.set(id, [dataSource]); | ||
this.synchedArrayDataSources.get(id).set(id, [{ source: dataSource, token: cancellation }]); | ||
} | ||
else { | ||
this.synchedArrayDataSources.get(id).push(dataSource); | ||
this.synchedArrayDataSources.get(id).get(authenticationToken).push({ source: dataSource, token: cancellation }); | ||
} | ||
@@ -98,4 +112,5 @@ } | ||
cancellation.addCancelable(() => { | ||
const listeners = this.synchedDuplexDataSources.get(id); | ||
listeners.splice(listeners.indexOf(dataSource)); | ||
const listenersByAuth = this.synchedDuplexDataSources.get(id); | ||
const listeners = listenersByAuth.get(authenticationToken); | ||
listeners.splice(listeners.findIndex((s) => s.source === dataSource)); | ||
if (listeners.length === 0) { | ||
@@ -116,4 +131,7 @@ this.connection.send(JSON.stringify({ | ||
})); | ||
}); | ||
if (!this.synchedDataSources.has(id)) { | ||
}, CancellationToken.fromMultiple([cancellation, this.masterToken])); | ||
if (!this.synchedDuplexDataSources.has(id)) { | ||
this.synchedDuplexDataSources.set(id, new Map()); | ||
} | ||
if (!this.synchedDuplexDataSources.get(id).has(authenticationToken)) { | ||
this.connection.send(JSON.stringify({ | ||
@@ -124,9 +142,14 @@ type: RemoteProtocol.LISTEN_DUPLEX_DATASOURCE, | ||
})); | ||
this.synchedDuplexDataSources.set(id, [dataSource]); | ||
this.synchedDuplexDataSources.get(id).set(id, [{ source: dataSource, token: cancellation }]); | ||
} | ||
else { | ||
this.synchedDuplexDataSources.get(id).push(dataSource); | ||
this.synchedDuplexDataSources.get(id).get(authenticationToken).push({ source: dataSource, token: cancellation }); | ||
} | ||
} | ||
static connect(host, protocol) { | ||
let pendingToken = new CancellationToken(); | ||
let started = false; | ||
let latency = 0; | ||
let latencyTs; | ||
let lastBeat; | ||
return new Promise((resolve, reject) => { | ||
@@ -146,11 +169,26 @@ if (!protocol) { | ||
const client = new AurumServerClient(connection); | ||
client.masterToken.addCancelable(() => { | ||
connections.delete(makeKey(host, protocol)); | ||
}); | ||
pendingToken.setTimeout(() => { | ||
connection.close(4001, 'no response'); | ||
reject(); | ||
client.masterToken.cancel(); | ||
}, 5000); | ||
connection.addEventListener('message', (m) => { | ||
lastBeat = Date.now(); | ||
try { | ||
const msg = JSON.parse(m.data); | ||
switch (msg.type) { | ||
case RemoteProtocol.HEARTBEAT: | ||
latency = Date.now() - latencyTs; | ||
console.log(`AurumServer latency: ${latency}ms`); | ||
break; | ||
case RemoteProtocol.UPDATE_DATASOURCE: | ||
if (client.synchedDataSources.has(msg.id)) { | ||
const dss = client.synchedDataSources.get(msg.id); | ||
for (const ds of dss) { | ||
ds.update(msg.value); | ||
const byAuth = client.synchedDataSources.get(msg.id); | ||
for (const dss of byAuth.values()) { | ||
for (const ds of dss) { | ||
ds.source.update(msg.value); | ||
} | ||
} | ||
@@ -161,36 +199,7 @@ } | ||
if (client.synchedArrayDataSources.has(msg.id)) { | ||
const dss = client.synchedArrayDataSources.get(msg.id); | ||
const change = msg.change; | ||
for (const ds of dss) { | ||
switch (change.operationDetailed) { | ||
case 'append': | ||
ds.appendArray(change.items); | ||
break; | ||
case 'clear': | ||
ds.clear(); | ||
break; | ||
case 'insert': | ||
ds.insertAt(change.index, ...change.items); | ||
break; | ||
case 'merge': | ||
ds.merge(change.items); | ||
break; | ||
case 'prepend': | ||
ds.unshift(change.items); | ||
break; | ||
case 'remove': | ||
ds.removeRange(change.index, change.index + change.count); | ||
break; | ||
case 'removeLeft': | ||
ds.removeLeft(change.count); | ||
break; | ||
case 'removeRight': | ||
ds.removeRight(change.count); | ||
break; | ||
case 'replace': | ||
ds.set(change.index, change.items[0]); | ||
break; | ||
case 'swap': | ||
ds.swap(change.index, change.index2); | ||
break; | ||
const byAuth = client.synchedArrayDataSources.get(msg.id); | ||
for (const dss of byAuth.values()) { | ||
const change = msg.change; | ||
for (const ds of dss) { | ||
ds.source.applyCollectionChange(change); | ||
} | ||
@@ -202,5 +211,7 @@ } | ||
if (client.synchedDuplexDataSources.has(msg.id)) { | ||
const dss = client.synchedDuplexDataSources.get(msg.id); | ||
for (const ds of dss) { | ||
ds.updateDownstream(msg.value); | ||
const byAuth = client.synchedDuplexDataSources.get(msg.id); | ||
for (const dss of byAuth.values()) { | ||
for (const ds of dss) { | ||
ds.source.updateDownstream(msg.value); | ||
} | ||
} | ||
@@ -216,9 +227,65 @@ } | ||
}); | ||
connection.addEventListener('error', (e) => reject(e)); | ||
connection.addEventListener('open', () => resolve(client)); | ||
connection.addEventListener('error', (e) => { | ||
client.masterToken.cancel(); | ||
reject(e); | ||
}); | ||
connection.addEventListener('open', () => { | ||
pendingToken.cancel(); | ||
pendingToken = undefined; | ||
started = true; | ||
lastBeat = Date.now(); | ||
client.masterToken.setInterval(() => { | ||
if (Date.now() - lastBeat > 10000) { | ||
connection.close(4000, 'timeout'); | ||
return; | ||
} | ||
latencyTs = Date.now(); | ||
connection.send(JSON.stringify({ | ||
type: RemoteProtocol.HEARTBEAT | ||
})); | ||
}, 2500); | ||
resolve(client); | ||
}); | ||
connection.addEventListener('close', () => { | ||
client.masterToken.cancel(); | ||
if (started) { | ||
ensureConnection(makeKey(host, protocol), protocol, host).then((newClient) => { | ||
newClient.migrate(client); | ||
}); | ||
} | ||
else { | ||
reject(); | ||
} | ||
}); | ||
}); | ||
} | ||
migrate(client) { | ||
for (const id of client.synchedDataSources.keys()) { | ||
for (const auth of client.synchedDataSources.get(id).keys()) { | ||
for (const { source, token } of client.synchedDataSources.get(id).get(auth)) { | ||
this.syncDataSource(source, id, auth, token); | ||
} | ||
} | ||
} | ||
for (const id of client.synchedArrayDataSources.keys()) { | ||
for (const auth of client.synchedArrayDataSources.get(id).keys()) { | ||
for (const { source, token } of client.synchedArrayDataSources.get(id).get(auth)) { | ||
this.syncArrayDataSource(source, id, auth, token); | ||
} | ||
} | ||
} | ||
for (const id of client.synchedDuplexDataSources.keys()) { | ||
for (const auth of client.synchedDuplexDataSources.get(id).keys()) { | ||
for (const { source, token } of client.synchedDuplexDataSources.get(id).get(auth)) { | ||
this.syncDuplexDataSource(source, id, auth, token); | ||
} | ||
} | ||
} | ||
client.synchedDataSources = undefined; | ||
client.synchedArrayDataSources = undefined; | ||
client.synchedDuplexDataSources = undefined; | ||
} | ||
} | ||
const pendingConnections = new Map(); | ||
async function ensureConnection(key, aurumServerInfo) { | ||
async function ensureConnection(key, protocol, host) { | ||
let backoff = 1000; | ||
if (pendingConnections.has(key)) { | ||
@@ -228,8 +295,25 @@ return pendingConnections.get(key); | ||
if (!connections.has(key)) { | ||
const p = AurumServerClient.connect(aurumServerInfo.host, aurumServerInfo.protocol); | ||
pendingConnections.set(key, p); | ||
connections.set(key, await p); | ||
pendingConnections.delete(key); | ||
const pendingConnection = new Promise((resolve) => { | ||
async function tryConnect() { | ||
const p = AurumServerClient.connect(host, protocol); | ||
try { | ||
const client = await p; | ||
connections.set(key, client); | ||
pendingConnections.delete(key); | ||
resolve(client); | ||
backoff = 1000; | ||
} | ||
catch (e) { | ||
setTimeout(() => { | ||
backoff += 1000; | ||
tryConnect(); | ||
}, backoff); | ||
} | ||
} | ||
tryConnect(); | ||
}); | ||
pendingConnections.set(key, pendingConnection); | ||
return pendingConnection; | ||
} | ||
} | ||
//# sourceMappingURL=aurum_server_client.js.map |
@@ -115,2 +115,3 @@ import { HTMLNodeProps } from '../builtin_compoents/dom_adapter'; | ||
sandbox?: AttributeValue; | ||
frameborder?: AttributeValue; | ||
} | ||
@@ -117,0 +118,0 @@ /** |
@@ -279,2 +279,5 @@ import { AurumServerInfo } from '../aurum_server/aurum_server_client'; | ||
listenAndRepeat(callback: Callback<CollectionChange<T>>, cancellationToken?: CancellationToken): Callback<void>; | ||
/** | ||
* Sends a reset signal followed by an append with all items signal. This will force all the views of this source the synchronize can be useful in case your views rely on non pure transformation functions. | ||
*/ | ||
repeatCurrentState(): void; | ||
@@ -284,2 +287,7 @@ listen(callback: Callback<CollectionChange<T>>, cancellationToken?: CancellationToken): Callback<void>; | ||
/** | ||
* Applies the changes described in the colleciton change to the array. Useful for synchronizing array data sources over the network or workers by serializing the changes and sending them over | ||
* @param collectionChange | ||
*/ | ||
applyCollectionChange(collectionChange: CollectionChange<T>): void; | ||
/** | ||
* Returns a promise that resolves when the next update occurs | ||
@@ -286,0 +294,0 @@ * @param cancellationToken |
@@ -317,2 +317,5 @@ import { syncArrayDataSource, syncDataSource } from '../aurum_server/aurum_server_client'; | ||
} | ||
/** | ||
* Sends a reset signal followed by an append with all items signal. This will force all the views of this source the synchronize can be useful in case your views rely on non pure transformation functions. | ||
*/ | ||
repeatCurrentState() { | ||
@@ -343,2 +346,40 @@ this.update({ | ||
/** | ||
* Applies the changes described in the colleciton change to the array. Useful for synchronizing array data sources over the network or workers by serializing the changes and sending them over | ||
* @param collectionChange | ||
*/ | ||
applyCollectionChange(collectionChange) { | ||
switch (collectionChange.operationDetailed) { | ||
case 'append': | ||
this.appendArray(collectionChange.items); | ||
break; | ||
case 'clear': | ||
this.clear(); | ||
break; | ||
case 'insert': | ||
this.insertAt(collectionChange.index, ...collectionChange.items); | ||
break; | ||
case 'merge': | ||
this.merge(collectionChange.items); | ||
break; | ||
case 'prepend': | ||
this.unshift(...collectionChange.items); | ||
break; | ||
case 'remove': | ||
this.removeRange(collectionChange.index, collectionChange.index + collectionChange.count); | ||
break; | ||
case 'removeLeft': | ||
this.removeLeft(collectionChange.count); | ||
break; | ||
case 'removeRight': | ||
this.removeRight(collectionChange.count); | ||
break; | ||
case 'replace': | ||
this.set(collectionChange.index, collectionChange.items[0]); | ||
break; | ||
case 'swap': | ||
this.swap(collectionChange.index, collectionChange.index2); | ||
break; | ||
} | ||
} | ||
/** | ||
* Returns a promise that resolves when the next update occurs | ||
@@ -345,0 +386,0 @@ * @param cancellationToken |
@@ -7,2 +7,3 @@ import { Delegate, Callback } from './common'; | ||
constructor(...cancellables: Delegate[]); | ||
static fromMultiple(tokens: CancellationToken[]): CancellationToken; | ||
hasCancellables(): boolean; | ||
@@ -9,0 +10,0 @@ /** |
@@ -9,2 +9,9 @@ export class CancellationToken { | ||
} | ||
static fromMultiple(tokens) { | ||
const result = new CancellationToken(); | ||
for (const token of tokens) { | ||
token.chain(result); | ||
} | ||
return result; | ||
} | ||
hasCancellables() { | ||
@@ -11,0 +18,0 @@ return this.cancelables.length > 0; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
3420365
33316