syndicate-js/packages/server/src/server.js

101 lines
3.4 KiB
JavaScript

"use strict";
const Http = activate require("@syndicate-lang/driver-http-node");
const S = activate require("@syndicate-lang/driver-streams-node");
import {
Set, Bytes,
Encoder, Observe,
Dataspace, Skeleton, currentFacet, genUuid, RandomID
} from "@syndicate-lang/core";
const P = activate require("./internal_protocol");
const W = activate require("./protocol");
export function websocketServerFacet(reqId) {
assert P.POA(reqId);
on message Http.DataIn(reqId, $data) {
if (data instanceof Bytes) send P.FromPOA(reqId, W.makeDecoder(data).next());
}
on message P.ToPOA(reqId, $resp) send Http.DataOut(reqId, new Encoder().push(resp).contents());
stop on message P.Disconnect(reqId);
stop on retracted P.POAScope(reqId, _);
}
export function streamServerFacet(id) {
assert P.POA(id);
const decoder = W.makeDecoder(null);
on message S.Data(id, $data) {
decoder.write(data);
let v;
while ((v = decoder.try_next())) send P.FromPOA(id, v);
}
on message P.ToPOA(id, $resp) send S.Push(id, new Encoder().push(resp).contents(), null);
stop on message P.Disconnect(id);
stop on retracted P.POAScope(id, _);
}
export function streamServerActor(id, debugLabel) {
spawn named [debugLabel || 'stream-poa', id] {
stop on retracted S.Duplex(id);
streamServerFacet(id);
}
}
spawn named '@syndicate-lang/server/server/POAHandler' {
during P.Proposal($scope, $assertion) assert P.Envelope(scope, assertion);
on message P.Proposal($scope, $assertion) send P.Envelope(scope, assertion);
during Observe(P.Envelope($scope, $spec)) assert P.Proposal(scope, Observe(spec));
during P.POA($connId) spawn named P.POA(connId) {
field this.scope = null;
assert P.POAScope(connId, this.scope) when (this.scope !== null);
assert P.ServerActive(this.scope) when (this.scope !== null);
let endpoints = Set();
on message P.FromPOA(connId, W.Connect($scope)) {
// TODO: Enforce requirement that Connect appear exactly once, before anything else
this.scope = scope;
}
on message P.FromPOA(connId, W.Assert($ep, $a)) {
if (!endpoints.includes(ep)) {
endpoints = endpoints.add(ep);
react {
on stop { endpoints = endpoints.remove(ep); }
field this.assertion = a;
assert P.Proposal(this.scope, this.assertion);
currentFacet().addEndpoint(() => {
if (Observe.isClassOf(this.assertion)) {
const spec = P.Envelope(this.scope, this.assertion.get(0));
const analysis = Skeleton.analyzeAssertion(spec);
analysis.callback = Dataspace.wrap((evt, vs) => {
currentFacet().actor.scheduleScript(() => {
switch (evt) {
case Skeleton.EVENT_ADDED: send P.ToPOA(connId, W.Add(ep, vs)); break;
case Skeleton.EVENT_REMOVED: send P.ToPOA(connId, W.Del(ep, vs)); break;
case Skeleton.EVENT_MESSAGE: send P.ToPOA(connId, W.Msg(ep, vs)); break;
}
});
});
return [Observe(spec), analysis];
} else {
return [void 0, null];
}
}, true);
on message P.FromPOA(connId, W.Assert(ep, $newAssertion)) this.assertion = newAssertion;
stop on message P.FromPOA(connId, W.Clear(ep));
}
}
}
on message P.FromPOA(connId, W.Message($body)) {
send P.Proposal(this.scope, body);
}
}
}