feat(wasm): validate client event and state ABI
req: client_local/005\nreq: client_local/006\nreq: client_local/007\nreq: client_local/008\nreq: client_local/009\nreq: client_local/010\nreq: client_local/014
This commit is contained in:
Vendored
+11
-1
@@ -45,6 +45,16 @@ export interface AtomSnapshot {
|
||||
bytes: Uint8Array;
|
||||
}
|
||||
|
||||
export type ClientHandler = (
|
||||
eventVersion: number,
|
||||
eventKind: string,
|
||||
eventValue: string | undefined,
|
||||
eventChecked: boolean | undefined,
|
||||
eventKey: string | undefined,
|
||||
stateVersion: number,
|
||||
encodedState: string,
|
||||
) => Uint8Array | Promise<Uint8Array>;
|
||||
|
||||
export interface HemxRuntime {
|
||||
readonly runtimeAbiVersion: number;
|
||||
roots(): Element[];
|
||||
@@ -54,7 +64,7 @@ export interface HemxRuntime {
|
||||
decodeBatch(buffer: ArrayBuffer): EffectBatch;
|
||||
atomValue(root: Element | ParentNode | null | undefined, id: number): Uint8Array | undefined;
|
||||
decodeAtomState(encoded: string): AtomSnapshot[];
|
||||
registerClientHandler(name: string, handler: () => Uint8Array | Promise<Uint8Array>): void;
|
||||
registerClientHandler(name: string, handler: ClientHandler): void;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
+31
-6
@@ -238,13 +238,34 @@
|
||||
});
|
||||
}
|
||||
|
||||
async function runClient(el) {
|
||||
async function runClient(el, event) {
|
||||
const name = el.getAttribute("data-hemx-client");
|
||||
const root = rootOf(el);
|
||||
const handler = clientHandlers.get(name);
|
||||
if (!handler) throw new Error(`unknown client-local hemx handler: ${name}`);
|
||||
const wire = await handler();
|
||||
if (!(wire instanceof Uint8Array)) throw new Error(`client-local hemx handler ${name} returned an invalid effect batch`);
|
||||
applyBatch(wire, rootOf(el));
|
||||
showError(el, null);
|
||||
showPending(el, true);
|
||||
try {
|
||||
if (!handler) throw new Error(`unknown client-local hemx handler: ${name}`);
|
||||
const stateVersion = Number(root.getAttribute("data-hemx-client-state-version") || "1");
|
||||
const wire = await handler(
|
||||
1,
|
||||
event.type,
|
||||
"value" in el ? String(el.value) : undefined,
|
||||
"checked" in el ? Boolean(el.checked) : undefined,
|
||||
event.key || undefined,
|
||||
stateVersion,
|
||||
root.getAttribute(STATE) || "",
|
||||
);
|
||||
if (!(wire instanceof Uint8Array)) throw new Error(`client-local hemx handler ${name} returned an invalid effect batch`);
|
||||
applyBatch(wire, root);
|
||||
} catch (error) {
|
||||
const fallback = el.hasAttribute("data-hemx-client-fallback");
|
||||
showError(el, error);
|
||||
emit(root, "hemx:client-error", { handler: name, message: String(error), fallback });
|
||||
if (fallback) await send(el, event.type, el);
|
||||
} finally {
|
||||
showPending(el, false);
|
||||
}
|
||||
}
|
||||
|
||||
async function send(el, eventName, source = el) {
|
||||
@@ -682,7 +703,11 @@
|
||||
if (direct && defaultEvent(direct) === "click") {
|
||||
event.preventDefault();
|
||||
if (direct.hasAttribute("data-hemx-client")) {
|
||||
runClient(direct).catch((error) => emit(root, "hemx:client-error", String(error)));
|
||||
runClient(direct, event).catch((error) => emit(root, "hemx:client-error", {
|
||||
handler: direct.getAttribute("data-hemx-client"),
|
||||
message: String(error),
|
||||
fallback: false,
|
||||
}));
|
||||
} else {
|
||||
schedule(direct, name);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user