Complete generic Eve command wire contract

This commit is contained in:
Meta Vens 2026-07-10 13:10:19 +01:00
commit 8f658932b5
5 changed files with 115 additions and 25 deletions

View file

@ -11,6 +11,10 @@
"types": "./src/index.d.ts",
"import": "./src/index.mjs"
},
"./provider-client": {
"types": "./src/cultmesh-provider-client.d.ts",
"import": "./src/cultmesh-provider-client.mjs"
},
"./preload": "./src/eve-provider-preload.cjs"
},
"files": ["src", "capture", "scripts", "eve-runtime-capability.json", "README.md", "LICENSE"],

31
src/cultmesh-provider-client.d.ts vendored Normal file
View file

@ -0,0 +1,31 @@
export interface EveCultMeshProviderTarget {
providerId?: string;
advertisementRecordRef: string;
peerId?: string;
verseId?: string;
role?: string;
endpoints?: string[];
}
export interface EveCommandSubmission extends Record<string, unknown> {
commandId: string;
providerId: string;
surfaceId: string;
state: "submitted";
receiptSchema: string;
receiptRecordRef: string;
}
export declare class EveCultMeshProviderClient {
constructor(target: EveCultMeshProviderTarget, dependencies: { CultMesh: object; encode: (value: unknown) => Uint8Array }, options?: { runtimeId?: string; timeoutMs?: number });
close(): Promise<void>;
providerAdvertisement(): Promise<Record<string, unknown>>;
surface(surfaceId?: string): Promise<Record<string, unknown>>;
document(request: { schemaId: string; recordRef: string }): Promise<unknown>;
submitCommand(request: Record<string, unknown>): Promise<EveCommandSubmission>;
receipt(submission: Record<string, unknown>): Promise<unknown>;
}
export declare function normalizeProviderAdvertisement(value: unknown): Record<string, unknown>;
export declare function normalizeSurfaceDocument(value: unknown): Record<string, unknown>;
export declare function normalizeCommandReceipt(value: unknown): Record<string, unknown>;

View file

@ -1,6 +1,6 @@
const providerSchema = "gamecult.eve.provider_advertisement.v1";
const commandSchema = "gamecult.eve.command.v1";
const connectionId = 0x45564545;
const commandSchema = "gamecult.eve.command_invocation.v1";
const connectionId = 0x43554c54;
export class EveCultMeshProviderClient {
#peer = null;
@ -59,7 +59,17 @@ export class EveCultMeshProviderClient {
if (!command) throw new Error("Command is required.");
const issuedAtUtc = request.issuedAtUtc || new Date().toISOString();
const commandId = request.commandId || `${this.runtimeId}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
const payload = [commandSchema, commandId, advertisement.providerId, surface.surfaceId, command, issuedAtUtc, request.clientId || this.runtimeId, objectValue(request.payload)];
const payload = [
commandSchema,
advertisement.providerId,
surface.surfaceId,
[command, "", "Network", interaction.commandBoundary, commandId],
stringRecord(request.payload),
[new Date(issuedAtUtc), 0],
request.clientId || this.runtimeId,
interaction.commandBoundary,
interaction.receiptSchema,
];
const peer = await this.#getPeer();
peer.send({
schemaVersion: "cultnet.document_put_raw.v0",
@ -80,6 +90,7 @@ export class EveCultMeshProviderClient {
providerId: advertisement.providerId,
surfaceId: surface.surfaceId,
state: "submitted",
route: { kind: "network", description: interaction.commandBoundary },
receiptSchema: interaction.receiptSchema,
receiptRecordRef: interaction.receiptRecordRef
? childRecordRef(interaction.receiptRecordRef, commandId)
@ -91,7 +102,7 @@ export class EveCultMeshProviderClient {
if (!submission?.receiptSchema || !submission?.receiptRecordRef) {
throw new Error("The command submission has no advertised receipt location.");
}
return this.#document(submission.receiptSchema, submission.receiptRecordRef, "receipt");
return normalizeCommandReceipt(await this.#document(submission.receiptSchema, submission.receiptRecordRef, "receipt"));
}
async #document(schemaId, recordRef, kind) {
@ -154,8 +165,49 @@ export function normalizeSurfaceDocument(value) {
title: stringField(value, "title", portable ? 4 : 2),
version: numberField(value, "version", portable ? 5 : 3),
updatedAtUtc: stringField(value, "updatedAtUtc", portable ? 6 : 4),
surface: { id, root: field(surface, "root", 1), styles: arrayField(surface, "styles", 2) },
commands: arrayField(value, "commands", portable ? 8 : 6),
surface: {
id,
root: normalizeComponent(field(surface, "root", 1), `${id}.root`),
styles: arrayField(surface, "styles", 2).map(token => ({ name: stringField(token, "name", 0), value: stringField(token, "value", 1) })),
},
commands: arrayField(value, "commands", portable ? 8 : 6).map(normalizeCommandDescriptor).filter(command => command.command),
};
}
export function normalizeCommandReceipt(value) {
return {
schema: stringField(value, "schema", 0), receiptId: stringField(value, "receiptId", 1),
commandId: stringField(value, "commandId", 2), command: stringField(value, "command", 3),
state: stringField(value, "state", 4), ownerRepo: stringField(value, "ownerRepo", 5),
authority: stringField(value, "authority", 6), providerId: stringField(value, "providerId", 7),
surfaceId: stringField(value, "surfaceId", 8), message: stringField(value, "message", 9),
issuedAtUtc: stringField(value, "issuedAtUtc", 10), sourceVersion: numberField(value, "sourceVersion", 11),
};
}
function normalizeComponent(value, fallbackId) {
return {
id: stringField(value, "id", 0) || fallbackId,
kind: stringField(value, "kind", 1) || "surface",
props: recordField(value, "props", 2),
children: arrayField(value, "children", 3).map((child, index) => normalizeComponent(child, `${fallbackId}.${index}`)),
stateBindings: arrayField(value, "stateBindings", 4),
embeddedDocuments: arrayField(value, "embeddedDocuments", 5).map(slot => ({
slotId: stringField(slot, "slotId", 0), documentId: stringField(slot, "documentId", 1),
schemaId: stringField(slot, "schemaId", 2), presentationKind: stringField(slot, "presentationKind", 3),
})),
layout: recordField(value, "layout", 6),
style: recordField(value, "style", 7),
};
}
function normalizeCommandDescriptor(value) {
const operation = field(value, "operation", 0);
const routeHint = field(operation, "routeHint", 3);
return {
command: stringField(operation, "operationId", 0),
label: stringField(operation, "label", 1),
transport: stringField(routeHint, "description", 1) || "cultmesh",
};
}
@ -195,5 +247,7 @@ function field(value, name, slot) { return value && typeof value === "object" ?
function stringField(value, name, slot) { const result = field(value, name, slot); return typeof result === "string" ? result : ""; }
function numberField(value, name, slot) { const result = field(value, name, slot); return typeof result === "number" && Number.isFinite(result) ? result : 0; }
function arrayField(value, name, slot) { const result = field(value, name, slot); return Array.isArray(result) ? result : []; }
function recordField(value, name, slot) { const result = field(value, name, slot); return result && typeof result === "object" && !Array.isArray(result) ? result : {}; }
function objectValue(value) { return value && typeof value === "object" && !Array.isArray(value) ? value : {}; }
function stringRecord(value) { return Object.fromEntries(Object.entries(objectValue(value)).map(([key, entry]) => [key, entry == null ? "" : String(entry)])); }
function requireFunctionSet(value, names, label) { if (!value) throw new Error(`${label} is required.`); for (const name of names) if (typeof value[name] !== "function") throw new Error(`${label}.${name} is required.`); return value; }

13
src/index.d.ts vendored
View file

@ -1,17 +1,6 @@
import type { BrowserWindow, BrowserWindowConstructorOptions, IpcMain, Shell } from "electron";
export declare class EveCultMeshProviderClient {
constructor(target: object, dependencies: { CultMesh: object; encode: (value: unknown) => Uint8Array }, options?: { runtimeId?: string; timeoutMs?: number });
close(): Promise<void>;
providerAdvertisement(): Promise<object>;
surface(surfaceId?: string): Promise<object>;
document(request: { schemaId: string; recordRef: string }): Promise<unknown>;
submitCommand(request: object): Promise<object>;
receipt(submission: object): Promise<unknown>;
}
export declare function normalizeProviderAdvertisement(value: unknown): object;
export declare function normalizeSurfaceDocument(value: unknown): object;
export * from "./cultmesh-provider-client.js";
export interface EveElectronWindowOptions extends BrowserWindowConstructorOptions {
browserWindow?: BrowserWindowConstructorOptions;

View file

@ -10,15 +10,17 @@ const advertisement = [
["provider-world", [], "commands", "records/commands", "gamecult.eve.command_receipt.v1", "records/receipts", "records/assets", ["electron-shell"], "provider owns truth"],
]], [],
];
const surface = ["surface-state", "gamecult.eve.surface.v1", "test-provider", "daemon", "World", 3, "now", ["world", { id: "root", kind: "world.scene", children: [] }, []], []];
const surface = ["surface-state", "gamecult.eve.surface.v1", "test-provider", "daemon", "World", 3, "now", ["world", ["root", "world.scene", { title: "World" }, [["child", "text", { value: "Ready" }, [], [], [], {}, {}]], [], [], {}, {}], []], []];
function harness() {
const sent = [];
const records = new Map([["records/provider", advertisement], ["records/world", surface], ["records/receipts:cmd-1", { state: "accepted" }]]);
const connections = [];
const encoded = [];
const records = new Map([["records/provider", advertisement], ["records/world", surface], ["records/receipts:cmd-1", ["gamecult.eve.command_receipt.v1", "receipt-1", "cmd-1", "move", "accepted", "Provider", "daemon", "test-provider", "world", "moved", "now", 4]]]);
const peer = { send: message => sent.push(message), close() {}, on() {} };
const mesh = {
createPeerCatalog: () => ({ upsert() {} }), createAuthorityLeaseCatalog: () => ({ upsert() {} }),
createRudpPeerForAuthorizedPeer: async () => peer,
createRudpPeerForAuthorizedPeer: async (...args) => { connections.push(args); return peer; },
documentFromPublication: (_source, _schema, recordRef) => ({ latest: async () => {
if (!records.has(recordRef)) throw new Error(`missing ${recordRef}`);
return records.get(recordRef);
@ -28,8 +30,8 @@ function harness() {
const client = new EveCultMeshProviderClient({
providerId: "test-provider", advertisementRecordRef: "records/provider", peerId: "test-peer",
verseId: "test.verse", role: "provider", endpoints: ["rudp://127.0.0.1:7777"],
}, { CultMesh: mesh, encode: value => Uint8Array.from([value.length]) }, { runtimeId: "test-runtime" });
return { client, sent };
}, { CultMesh: mesh, encode: value => { encoded.push(value); return Uint8Array.from([value.length]); } }, { runtimeId: "test-runtime" });
return { client, connections, encoded, sent };
}
test("follows provider-advertised record references", async () => {
@ -37,15 +39,25 @@ test("follows provider-advertised record references", async () => {
const provider = await client.providerAdvertisement();
assert.equal(provider.surfaces[0].recordRef, "records/world");
assert.equal((await client.surface("world")).surface.id, "world");
assert.equal((await client.surface("world")).surface.root.children[0].props.value, "Ready");
});
test("submits at the advertised command boundary and reads the advertised receipt", async () => {
const { client, sent } = harness();
const { client, connections, encoded, sent } = harness();
const submission = await client.submitCommand({ surfaceId: "world", command: "move", commandId: "cmd-1", payload: { x: 1 } });
assert.equal(sent[0].document.recordKey, "records/commands:cmd-1");
assert.equal(sent[0].document.schemaId, "gamecult.eve.command_invocation.v1");
assert.deepEqual(encoded[0][3], ["move", "", "Network", "commands", "cmd-1"]);
assert.equal(encoded[0][5][1], 0);
assert.deepEqual(encoded[0][4], { x: "1" });
assert.equal(sent[0].document.sourceRole, "eve-runtime");
assert.equal(connections[0][1], 0x43554c54);
assert.equal(submission.receiptRecordRef, "records/receipts:cmd-1");
assert.deepEqual(await client.receipt(submission), { state: "accepted" });
assert.deepEqual(await client.receipt(submission), {
schema: "gamecult.eve.command_receipt.v1", receiptId: "receipt-1", commandId: "cmd-1", command: "move",
state: "accepted", ownerRepo: "Provider", authority: "daemon", providerId: "test-provider", surfaceId: "world",
message: "moved", issuedAtUtc: "now", sourceVersion: 4,
});
});
test("refuses unadvertised surfaces", async () => {