hasExecutableSql returns as soon as it meets a quote character, so the quote variable was assigned and never read: the `if (quote)` branch could never run. eslint reported it as a useless assignment and the error blocks the release gate on main. Removing the variable and the unreachable branch keeps behaviour identical, since encountering a quote already means the SQL is executable. Also drops an eslint-disable directive in csr's output error reporter that suppressed nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
75 lines
2.5 KiB
TypeScript
75 lines
2.5 KiB
TypeScript
export type OutputHandler<T = unknown> = (payload: T) => void | Promise<void>;
|
|
export interface OutputHost extends HTMLElement {
|
|
__wrnexusOutputHandlers?: Map<string, Set<OutputHandler>>;
|
|
}
|
|
|
|
export function registerOutputHandler<T>(
|
|
host: OutputHost,
|
|
name: string,
|
|
handler: OutputHandler<T>,
|
|
): () => void {
|
|
const registry = (host.__wrnexusOutputHandlers ??= new Map());
|
|
const handlers = registry.get(name) ?? new Set();
|
|
handlers.add(handler as OutputHandler);
|
|
registry.set(name, handlers);
|
|
return () => {
|
|
handlers.delete(handler as OutputHandler);
|
|
if (!handlers.size) registry.delete(name);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Surface a throwing output handler.
|
|
*
|
|
* Callers of `invokeOutput` do not await it — an output call is fire-and-forget
|
|
* from the component's point of view. A handler that throws therefore becomes an
|
|
* unhandled rejection that never reaches the console, and the symptom is a
|
|
* control that silently does nothing. Reporting here turns that into a named
|
|
* diagnostic instead of a dead button.
|
|
*/
|
|
function reportOutputError(host: OutputHost, name: string, error: unknown): void {
|
|
const code = "WRN-DEV-OUTPUT-HANDLER-ERROR";
|
|
const message = `The handler bound to output '${name}' threw. The control will appear to do nothing.`;
|
|
console.error(`[${code}] ${message}`, error);
|
|
try {
|
|
window.dispatchEvent(
|
|
new CustomEvent("wrnexus:diagnostic", {
|
|
detail: {
|
|
code,
|
|
message,
|
|
hydrationId: host.getAttribute ? host.getAttribute("data-wrn-hydration") : null,
|
|
detail: { output: name, error: String(error) },
|
|
},
|
|
}),
|
|
);
|
|
} catch {
|
|
// CustomEvent can be unavailable in minimal DOM test environments.
|
|
}
|
|
}
|
|
|
|
export async function invokeOutput<T>(host: OutputHost, name: string, payload?: T): Promise<void> {
|
|
const handlers = host.__wrnexusOutputHandlers?.get(name);
|
|
if (handlers?.size) {
|
|
for (const handler of handlers) {
|
|
try {
|
|
await handler(payload);
|
|
} catch (error) {
|
|
reportOutputError(host, name, error);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
// Compatibility path for legacy listeners outside a hydrated WRN parent.
|
|
host.dispatchEvent(new CustomEvent(name, { detail: payload }));
|
|
host.dispatchEvent(new CustomEvent(`wrnexus:${name}`, { detail: payload }));
|
|
}
|
|
|
|
export function createOutputProxy<T extends Record<string, (...args: any[]) => void>>(
|
|
host: OutputHost,
|
|
): T {
|
|
return new Proxy({} as T, {
|
|
get: (_target, property) => (payload?: unknown) =>
|
|
invokeOutput(host, String(property), payload),
|
|
});
|
|
}
|