Improve wsRelay
This commit is contained in:
parent
09ae5ddb5b
commit
3fe2af848b
22
TODO.md
22
TODO.md
|
@ -1,3 +1,25 @@
|
||||||
|
|
||||||
|
// // v Ideally, this would be of type "Sturdy.SturdyRef | null", but (1) the
|
||||||
|
// // current Dataflow implementation isn't bright enough to mark valuesas being
|
||||||
|
// // convertible to preserves values on demand, and (2) null isn't preservesable
|
||||||
|
// // in any case. If preserves were improved to be able to convert schema-parsed
|
||||||
|
// // values to preserves on demand, and to know it could do that at the type
|
||||||
|
// // level, then I could change Dataflow to support any value that could be
|
||||||
|
// // converted to preserves on demand, and I could special-case null and
|
||||||
|
// // undefined for the ergonomics.
|
||||||
|
// field servercap: AnyValue = false;
|
||||||
|
// on asserted InputValue('#servercap', $v: string) => {
|
||||||
|
// servercap.value = false;
|
||||||
|
// try {
|
||||||
|
// const a = new Reader<Ref>(v).next();
|
||||||
|
// if (Sturdy.toSturdyRef(a) !== void 0) servercap.value = a;
|
||||||
|
// } catch (e) {
|
||||||
|
// console.error(e);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// assert UIAttribute('#servercap', 'class', 'invalid') when (!servercap.value);
|
||||||
|
|
||||||
|
|
||||||
- [DONE] `during/spawn`
|
- [DONE] `during/spawn`
|
||||||
- [DONE] `during`
|
- [DONE] `during`
|
||||||
- [DONE] `let { TimeLaterThan } = activate require("@syndicate-lang/driver-timer");`
|
- [DONE] `let { TimeLaterThan } = activate require("@syndicate-lang/driver-timer");`
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepare": "yarn regenerate && yarn compile && yarn rollup",
|
"prepare": "yarn regenerate && yarn compile && yarn rollup",
|
||||||
"regenerate": "rm -rf ./src/gen && preserves-schema-ts --module EntityRef=@syndicate-lang/core --module transportAddress=@syndicate-lang/core:Schemas.transportAddress --output ./src/gen './protocols/schemas/**/*.prs'",
|
"regenerate": "rm -rf ./src/gen && preserves-schema-ts --module EntityRef=@syndicate-lang/core --module transportAddress=@syndicate-lang/core:Schemas.transportAddress --module sturdy=@syndicate-lang/core:Schemas.sturdy --xref 'node_modules/@syndicate-lang/core/protocols/schemas/**/*.prs' --output ./src/gen './protocols/schemas/**/*.prs'",
|
||||||
"regenerate:watch": "yarn regenerate --watch",
|
"regenerate:watch": "yarn regenerate --watch",
|
||||||
"compile": "syndicate-tsc",
|
"compile": "syndicate-tsc",
|
||||||
"compile:watch": "syndicate-tsc -w --verbose --intermediate-directory src.ts",
|
"compile:watch": "syndicate-tsc -w --verbose --intermediate-directory src.ts",
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
version 1 .
|
version 1 .
|
||||||
|
|
||||||
|
Resolved = <resolved @addr RelayAddress @sturdyref sturdy.SturdyRef @resolved #!any> .
|
||||||
|
|
||||||
ViaRelay = <via-relay @addr RelayAddress @assertion any> .
|
ViaRelay = <via-relay @addr RelayAddress @assertion any> .
|
||||||
ForceRelayDisconnect = <force-relay-disconnect @addr RelayAddress> .
|
ForceRelayDisconnect = <force-relay-disconnect @addr RelayAddress> .
|
||||||
|
|
||||||
|
|
|
@ -1,80 +1,63 @@
|
||||||
/// SPDX-License-Identifier: GPL-3.0-or-later
|
/// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
/// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
/// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
||||||
|
|
||||||
import { QuasiValue as Q, Dataspace, Ref, Sturdy, Observe, AnyValue, Reader, Embedded, Schemas } from "@syndicate-lang/core";
|
import { Dataspace, Ref, Sturdy, AnyValue, Reader, Schemas, isEmbedded } from "@syndicate-lang/core";
|
||||||
import { boot as bootHtml, Anchor, template as html, HtmlFragments, GlobalEvent, UIAttribute } from "@syndicate-lang/html";
|
import { boot as bootHtml, Anchor, template as html, HtmlFragments, GlobalEvent, UIAttribute, UIChangeableProperty } from "@syndicate-lang/html";
|
||||||
import { boot as bootWakeDetector, WakeEvent } from "./wake-detector";
|
import { boot as bootWakeDetector, WakeEvent } from "./wake-detector";
|
||||||
import { boot as bootWsRelay, ForceRelayDisconnect, ViaRelay, RelayAddress, fromForceRelayDisconnect, fromViaRelay } from "./wsRelay";
|
import { boot as bootWsRelay, ForceRelayDisconnect, fromRelayAddress, RelayAddress, fromForceRelayDisconnect, Resolved } from "./wsRelay";
|
||||||
import { fromSays, fromPresent, Present, Says } from './gen/simpleChatProtocol';
|
import { fromSays, fromPresent, Present, Says } from './gen/simpleChatProtocol';
|
||||||
|
|
||||||
const Transport = Schemas.transportAddress;
|
const Transport = Schemas.transportAddress;
|
||||||
|
|
||||||
function setWsurl() {
|
|
||||||
const wsurl = document.getElementById('wsurl')! as HTMLInputElement;
|
|
||||||
if (wsurl.value === '') {
|
|
||||||
wsurl.value = `ws://${document.location.host}:9001/`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setUsernameIfUnset() {
|
|
||||||
const nym = document.getElementById('nym')! as HTMLInputElement;
|
|
||||||
if (nym.value === '') {
|
|
||||||
nym.value = "nym" + Math.floor(Math.random() * 65536);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
document.getElementById('chat_form')!.onsubmit = e => { e.preventDefault(); return false; };
|
document.getElementById('chat_form')!.onsubmit = e => { e.preventDefault(); return false; };
|
||||||
document.getElementById('nym_form')!.onsubmit = e => { e.preventDefault(); return false; };
|
document.getElementById('nym_form')!.onsubmit = e => { e.preventDefault(); return false; };
|
||||||
setWsurl();
|
setWsurl();
|
||||||
setUsernameIfUnset();
|
setUsernameIfUnset();
|
||||||
|
document.getElementById('chat_input')!.focus();
|
||||||
|
|
||||||
Dataspace.boot(ds => {
|
Dataspace.boot(ds => {
|
||||||
bootHtml(ds);
|
bootHtml(ds);
|
||||||
bootWakeDetector(ds);
|
bootWakeDetector(ds);
|
||||||
bootWsRelay(ds);
|
bootWsRelay(ds, true);
|
||||||
spawnInputChangeMonitor(ds);
|
bootChat(ds);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function bootChat(ds: Ref) {
|
||||||
spawn named 'chat' {
|
spawn named 'chat' {
|
||||||
at ds {
|
at ds {
|
||||||
const ui = new Anchor();
|
|
||||||
|
|
||||||
field nym: string = '';
|
field nym: string = '';
|
||||||
on asserted InputValue('#nym', $v: string) => nym.value = v;
|
on asserted UIChangeableProperty('#nym', 'value', $v: string) => nym.value = v;
|
||||||
|
|
||||||
// v Ideally, this would be of type "Sturdy.SturdyRef | null", but (1) the
|
during UIChangeableProperty('#wsurl', 'value', $wsurl: string) =>
|
||||||
// current Dataflow implementation isn't bright enough to mark valuesas being
|
during UIChangeableProperty('#servercap', 'value', $servercapText: string) => {
|
||||||
// convertible to preserves values on demand, and (2) null isn't preservesable
|
let servercap: AnyValue | null = null;
|
||||||
// in any case. If preserves were improved to be able to convert schema-parsed
|
|
||||||
// values to preserves on demand, and to know it could do that at the type
|
|
||||||
// level, then I could change Dataflow to support any value that could be
|
|
||||||
// converted to preserves on demand, and I could special-case null and
|
|
||||||
// undefined for the ergonomics.
|
|
||||||
field servercap: AnyValue = false;
|
|
||||||
on asserted InputValue('#servercap', $v: string) => {
|
|
||||||
servercap.value = false;
|
|
||||||
try {
|
try {
|
||||||
const a = new Reader<Ref>(v).next();
|
const a = new Reader<Ref>(servercapText).next();
|
||||||
if (Sturdy.toSturdyRef(a) !== void 0) servercap.value = a;
|
Sturdy.asSturdyRef(a); // throws if invalid
|
||||||
|
servercap = a;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
|
assert UIAttribute('#servercap', 'class', 'invalid') when (!servercap);
|
||||||
|
if (wsurl && servercap) {
|
||||||
|
contactRemote(wsurl, servercap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
assert UIAttribute('#servercap', 'class', 'invalid') when (!servercap.value);
|
|
||||||
|
|
||||||
during InputValue('#wsurl', $wsurl: string) => {
|
function contactRemote(wsurl: string, servercap: AnyValue) {
|
||||||
const addr = RelayAddress(Transport.WebSocket(wsurl));
|
during Resolved({
|
||||||
assert fromViaRelay(ViaRelay({
|
"addr": RelayAddress(Transport.WebSocket(wsurl)),
|
||||||
"addr": addr,
|
"sturdyref": servercap,
|
||||||
"assertion": Schemas.gatekeeper.fromResolve(Schemas.gatekeeper.Resolve({
|
"resolved": $remoteDs_e,
|
||||||
"sturdyref": Sturdy.asSturdyRef(servercap.value),
|
}) => {
|
||||||
"observer": create ({
|
if (!isEmbedded(remoteDs_e)) return;
|
||||||
assert(remoteDs_e: Embedded<Ref>) {
|
|
||||||
const remoteDs = remoteDs_e.embeddedValue;
|
const remoteDs = remoteDs_e.embeddedValue;
|
||||||
console.log('Saw remoteDs', remoteDs);
|
|
||||||
|
|
||||||
on message WakeEvent() =>
|
on message WakeEvent() =>
|
||||||
send message fromForceRelayDisconnect(ForceRelayDisconnect(addr));
|
send message fromForceRelayDisconnect(ForceRelayDisconnect(
|
||||||
|
RelayAddress(Transport.WebSocket(wsurl))));
|
||||||
|
|
||||||
outputState('connected', 'connected to ' + wsurl);
|
outputState('connected', 'connected to ' + wsurl);
|
||||||
on stop outputState('disconnected', 'disconnected from ' + wsurl);
|
on stop outputState('disconnected', 'disconnected from ' + wsurl);
|
||||||
|
@ -93,6 +76,7 @@ export function main() {
|
||||||
at remoteDs {
|
at remoteDs {
|
||||||
assert fromPresent(Present(nym.value));
|
assert fromPresent(Present(nym.value));
|
||||||
|
|
||||||
|
const ui = new Anchor();
|
||||||
during Present($who: string) => at ds {
|
during Present($who: string) => at ds {
|
||||||
assert ui.context(who).html('#nymlist', html`<li>${who}</li>`);
|
assert ui.context(who).html('#nymlist', html`<li>${who}</li>`);
|
||||||
}
|
}
|
||||||
|
@ -102,39 +86,24 @@ export function main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
|
||||||
})),
|
|
||||||
})) when (wsurl && servercap.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Input control value monitoring
|
|
||||||
|
|
||||||
assertion type InputValue(selector, value);
|
|
||||||
|
|
||||||
function spawnInputChangeMonitor(ds: Ref) {
|
|
||||||
spawn {
|
|
||||||
at ds {
|
|
||||||
during Observe({
|
|
||||||
"pattern": :pattern InputValue(\Q.lit($selector: string), \_)
|
|
||||||
}) => spawn named `input(${selector})` {
|
|
||||||
const element = document.querySelector(selector) as HTMLInputElement;
|
|
||||||
if (element !== null) {
|
|
||||||
field value: string = element.value;
|
|
||||||
assert InputValue(selector, value.value);
|
|
||||||
on message GlobalEvent(selector, 'change', $_e) => value.value = element.value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
function setWsurl() {
|
||||||
// Adding items to the transcript panel
|
const wsurl = document.getElementById('wsurl')! as HTMLInputElement;
|
||||||
|
if (wsurl.value === '') {
|
||||||
|
wsurl.value = `ws://${document.location.host}:9001/`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUsernameIfUnset() {
|
||||||
|
const nym = document.getElementById('nym')! as HTMLInputElement;
|
||||||
|
if (nym.value === '') {
|
||||||
|
nym.value = "nym" + Math.floor(Math.random() * 65536);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function outputItem(cls: string, item0: HtmlFragments): ChildNode {
|
function outputItem(cls: string, item0: HtmlFragments): ChildNode {
|
||||||
const stamp = html`<span class="timestamp">${(new Date()).toUTCString()}</span>`;
|
const stamp = html`<span class="timestamp">${(new Date()).toUTCString()}</span>`;
|
||||||
|
|
|
@ -1,22 +1,48 @@
|
||||||
/// SPDX-License-Identifier: GPL-3.0-or-later
|
/// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
/// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
/// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
||||||
|
|
||||||
import { Ref, Relay, Turn, Supervisor, SupervisorRestartPolicy } from "@syndicate-lang/core";
|
import { QuasiValue as Q, Ref, Relay, Turn, Supervisor, SupervisorRestartPolicy, Observe, Schemas, assertionFacetObserver, isEmbedded } from "@syndicate-lang/core";
|
||||||
import * as G from "./gen/wsRelay";
|
import * as G from "./gen/wsRelay";
|
||||||
export * from "./gen/wsRelay";
|
export * from "./gen/wsRelay";
|
||||||
|
|
||||||
export function boot(ds: Ref) {
|
export function boot(ds: Ref, debug: boolean = false) {
|
||||||
spawn named 'wsRelay' {
|
spawn named 'wsRelay' {
|
||||||
at ds {
|
at ds {
|
||||||
|
during Observe({ "pattern": :pattern G.Resolved({
|
||||||
|
"addr": \Q.lit($addrValue),
|
||||||
|
"sturdyref": \Q.lit($sturdyRefValue),
|
||||||
|
"resolved": \Q.bind(),
|
||||||
|
}) }) => {
|
||||||
|
const addr = G.toRelayAddress(addrValue);
|
||||||
|
const sturdyref = Schemas.sturdy.toSturdyRef(sturdyRefValue);
|
||||||
|
if (addr && sturdyref) {
|
||||||
|
assert G.fromViaRelay(G.ViaRelay({
|
||||||
|
"addr": addr,
|
||||||
|
"assertion": Schemas.gatekeeper.fromResolve(Schemas.gatekeeper.Resolve({
|
||||||
|
"sturdyref": sturdyref,
|
||||||
|
"observer": create assertionFacetObserver(e => {
|
||||||
|
if (isEmbedded(e)) {
|
||||||
|
assert G.fromResolved(G.Resolved({
|
||||||
|
"addr": addr,
|
||||||
|
"sturdyref": sturdyref,
|
||||||
|
"resolved": e.embeddedValue,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
during G.ViaRelay({ "addr": $addrValue }) => spawn named ['wsRelay', addrValue] {
|
during G.ViaRelay({ "addr": $addrValue }) => spawn named ['wsRelay', addrValue] {
|
||||||
let counter = 0;
|
let counter = 0;
|
||||||
new Supervisor({
|
new Supervisor({
|
||||||
restartPolicy: SupervisorRestartPolicy.ALWAYS,
|
restartPolicy: SupervisorRestartPolicy.ALWAYS,
|
||||||
}, () => ['wsRelay', addrValue, counter++], () => {
|
}, () => ['wsRelay', addrValue, counter++], () => {
|
||||||
const facet = Turn.activeFacet;
|
|
||||||
facet.preventInertCheck();
|
|
||||||
const addr = G.toRelayAddress(addrValue);
|
const addr = G.toRelayAddress(addrValue);
|
||||||
if (addr !== void 0) {
|
if (addr !== void 0) {
|
||||||
|
stop on message G.ForceRelayDisconnect(addrValue);
|
||||||
|
const facet = Turn.activeFacet;
|
||||||
const ws = new WebSocket(addr.url);
|
const ws = new WebSocket(addr.url);
|
||||||
ws.binaryType = 'arraybuffer';
|
ws.binaryType = 'arraybuffer';
|
||||||
ws.onclose = () => facet.turn(() => { stop {} });
|
ws.onclose = () => facet.turn(() => { stop {} });
|
||||||
|
@ -24,7 +50,7 @@ export function boot(ds: Ref) {
|
||||||
Turn.active.crash(new Error("WebSocket error")));
|
Turn.active.crash(new Error("WebSocket error")));
|
||||||
ws.onopen = () => facet.turn(() => {
|
ws.onopen = () => facet.turn(() => {
|
||||||
const relay = new Relay.Relay({
|
const relay = new Relay.Relay({
|
||||||
debug: true,
|
debug,
|
||||||
trustPeer: true,
|
trustPeer: true,
|
||||||
packetWriter: bs => ws.send(bs),
|
packetWriter: bs => ws.send(bs),
|
||||||
setup(r: Relay.Relay) {
|
setup(r: Relay.Relay) {
|
||||||
|
|
Loading…
Reference in New Issue