Fix up secure-chat using facets!; and factor out readline

This commit is contained in:
Tony Garnock-Jones 2021-04-16 20:58:22 +02:00
parent ff1feff82b
commit 8b460a2f1c
4 changed files with 63 additions and 51 deletions

19
src/readline.ts Normal file
View File

@ -0,0 +1,19 @@
import { Turn, Entity, Facet } from './actor.js';
import readline from 'readline';
export function attachReadline(t: Turn, entity: Partial<Entity>): Facet {
const ref = t.ref(entity);
return t.facet(t => {
let rl: readline.Interface | null =
readline.createInterface({ input: process.stdin, output: process.stdout });
function shutdown(_t: Turn) {
rl?.close();
rl = null;
}
t.assert(ref, true);
t.activeFacet.actor.atExit(shutdown);
t.activeFacet.onStop(shutdown);
rl.on('line', (line: string) => t.freshen(t => t.message(ref, line)));
rl.on('close', () => t.freshen(t => t.stop()));
});
}

View File

@ -1,7 +1,7 @@
import { $joinedUser, $says, $user, asJoin, asSays, asUserInfo, fromNickClaim, fromSays, NickClaim, Says, UserId } from "./gen/secure-chat-protocol.js";
import { during, observe } from "./dataspace.js";
import { Assertion, Ref, Turn } from "./actor.js";
import readline from 'readline';
import { attachReadline } from './readline.js';
export default function (t: Turn, ds: Ref) {
observe(t, ds, $joinedUser, {
@ -52,15 +52,14 @@ function runSession(t: Turn, uid: UserId, session: Ref) {
},
});
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
rl.on('line', (line: string) => t.freshen(t => {
if (line.toLowerCase().startsWith('/nick ')) {
updateUsername(t, line.slice(5).trimLeft());
} else {
t.message(session, fromSays(Says({ who: uid, what: line })));
}
}));
rl.on('close', () => t.freshen(t => t.stopActor()));
attachReadline(t, {
retract(t) { t.stop(); },
message(t, line: string) {
if (line.toLowerCase().startsWith('/nick ')) {
updateUsername(t, line.slice(5).trimLeft());
} else {
t.message(session, fromSays(Says({ who: uid, what: line })));
}
},
});
}

View File

@ -6,37 +6,33 @@ export default function (t: Turn, ds: Ref) {
let nextUserId: UserId = 0;
const nicks = new Map<string, UserId>();
const users = new Map<UserId, { infoHandle: Handle | undefined, nick: string | undefined }>();
observe(t, ds, $claimNick, {
assert(t: Turn, c0: Assertion): void {
const c = asNickClaim(c0);
if (nicks.has(c.name)) {
t.message(c.k, fromNickConflict(NickConflict()));
} else {
t.message(c.k, true);
let u = users.get(c.uid);
if (!u) {
u = { infoHandle: void 0, nick: void 0 };
users.set(c.uid, u);
}
if (u.nick !== void 0) nicks.delete(u.nick);
u.infoHandle = t.replace(ds, u.infoHandle, fromUserInfo(UserInfo(c)));
u.nick = c.name;
nicks.set(c.name, c.uid);
}
}
});
observe(t, ds, $Observe, during((t, o0) => {
const o = asObserve(o0);
if (o.label !== $joinedUser) return null;
const uid: UserId = nextUserId++;
const h = t.assert(o.observer, fromJoin(Join({
uid,
handle: ds,
})));
return t => t.retract(h);
const f = t.facet(t => {
t.assert(o.observer, fromJoin(Join({ uid, handle: ds })));
let infoHandle: Handle | undefined;
let nick: string | undefined;
observe(t, ds, $claimNick, {
assert(t: Turn, c0: Assertion): void {
const c = asNickClaim(c0);
if (c.uid !== uid) return;
if (nicks.has(c.name)) {
t.message(c.k, fromNickConflict(NickConflict()));
} else {
t.message(c.k, true);
if (nick !== void 0) nicks.delete(nick);
infoHandle = t.replace(ds, infoHandle, fromUserInfo(UserInfo(c)));
nick = c.name;
nicks.set(nick, uid);
}
}
});
});
return t => t.stop(f);
}));
}

View File

@ -1,7 +1,7 @@
import { $Present, $Says, asPresent, asSays, fromPresent, fromSays, Present, Says } from "./gen/simple-chat-protocol.js";
import { during, observe } from "./dataspace.js";
import { Assertion, Handle, Ref, Turn } from "./actor.js";
import readline from 'readline';
import { attachReadline } from './readline.js';
export default function (t: Turn, ds: Ref) {
let username = '';
@ -26,16 +26,14 @@ export default function (t: Turn, ds: Ref) {
},
});
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
t.activeFacet.actor.atExit(_t => rl.close());
rl.on('line', (line: string) => t.freshen(t => {
if (line.toLowerCase().startsWith('/nick ')) {
updateUsername(t, line.slice(5).trimLeft());
} else {
t.message(ds, fromSays(Says({ who: username, what: line })));
}
}));
rl.on('close', () => t.freshen(t => t.stopActor()));
attachReadline(t, {
retract(t) { t.stop(); },
message(t, line: string) {
if (line.toLowerCase().startsWith('/nick ')) {
updateUsername(t, line.slice(5).trimLeft());
} else {
t.message(ds, fromSays(Says({ who: username, what: line })));
}
},
});
}