A press on the server
A press is an event sent up. The trigger runs in the shell on the server, and the next frame comes back.
pnpm add @niscorp/moss @niscorp/charter @niscorp/nova @niscorp/prism @niscorp/strata @niscorp/tide @niscorp/vex zod @electric-sql/pgliteThe app, booted
The same theatre. The seats screen holds a count, and a trigger that adds one to it when the button with the ref hold is pressed.
import { PGlite } from '@electric-sql/pglite';
import { createServer, defineApp, mintDevToken } from '@niscorp/moss';
import { canvasTreeOf, shellSettled } from '@niscorp/nova';
import type { ActionDefinition, RenderNode } from '@niscorp/nova';
import { createPglitePool } from '@niscorp/vex/pglite';
const db = new PGlite();
const runtime = { db, pool: createPglitePool(db), session: 'dev-open' as const };
const charter = {
public: ['programme'],
member: ['programme', 'seats'],
manager: { extends: ['member'], allow: ['takings'] },
};
const assignments = { grace: ['member'], ruth: ['manager'] };
const text = (says: string) => ({ component: 'Text', children: says });
const actions: Record<string, ActionDefinition> = {
programme: { id: 'programme', layout: text('This week: The Tempest') },
seats: {
id: 'seats',
data: { held: 0 },
triggers: [{ event: 'ui:click', ref: 'hold', do: [{ increment: 'held' }] }],
layout: {
component: 'Stack',
children: [
text('Seats held: {{$.held}}'),
{ component: 'Button', ref: 'hold', props: { label: 'Hold a seat' } },
],
},
},
takings: { id: 'takings', layout: text('Saturday took 4,180') },
};
const app = defineApp({
charter,
assignments,
actions,
shell: { canvases: [{ id: 'main', initial: ['takings', 'seats', 'programme'] }] },
});
const server = await createServer(app, runtime);
const words = (nodes: readonly RenderNode[]): string[] =>
nodes.flatMap((node) => (node.type === 'text' ? [node.value] : 'children' in node ? words(node.children) : []));Grace’s session
shells.session gives the living shell of one person, the one a socket would attach to. It is built the first time it is asked for and kept.
const session = await server.shells!.session(mintDevToken('grace'), 'grace');
await shellSettled(session.shell);
console.log('before:', words(canvasTreeOf(session.shell, 'main')));Send the press
This is all a browser sends when a button is pressed: the kind of event and the ref of what was pressed. It does not say what should happen.
What happens is decided by the trigger, which is on the server. The count goes up in the shell, and the screen is drawn again.
session.dispatch('main', { type: 'ui:click', ref: 'hold' });
await shellSettled(session.shell);
console.log('after:', words(canvasTreeOf(session.shell, 'main')));A press for something that is not there
An event for a ref the screen does not have changes nothing. A browser cannot make up a press that does more than the screen allows.
session.dispatch('main', { type: 'ui:click', ref: 'refund-everyone' });
await shellSettled(session.shell);
console.log('still:', words(canvasTreeOf(session.shell, 'main')));
server.close();Run it.
The count before the press, after it, and unchanged by an event the screen has no trigger for.
npx tsx press.mtsbefore: [ 'Seats held: 0' ] after: [ 'Seats held: 1' ] still: [ 'Seats held: 1' ]