Files
openchamber/packages/web/server/lib/notifications/emitter-runtime.js
T
Bohdan Triapitsyn c7bc026b4b refactor: remove legacy Tauri desktop support
Electron updater now uses Electron release metadata only
Removed legacy Tauri package and migration workflow
Replaced Tauri shim usage with the desktop bridge
2026-06-03 02:42:00 +03:00

102 lines
2.8 KiB
JavaScript

export const createNotificationEmitterRuntime = (dependencies) => {
const {
process,
getDesktopNotifyEnabled,
desktopNotifyPrefix,
getUiNotificationClients,
getBroadcastGlobalUiEvent,
// Optional: in-process desktop shells (Electron main) inject a callback so
// notifications are delivered as a direct function call instead of a stdout
// stringly-typed IPC.
onDesktopNotification: initialOnDesktopNotification,
} = dependencies;
// Late-bindable: main() in server/index.js may call setOnDesktopNotification
// after runtime construction so the in-process shell can subscribe without
// restructuring the module-level wiring.
let onDesktopNotification = typeof initialOnDesktopNotification === 'function'
? initialOnDesktopNotification
: null;
const setOnDesktopNotification = (cb) => {
onDesktopNotification = typeof cb === 'function' ? cb : null;
};
const writeSseEvent = (res, payload) => {
res.write(`data: ${JSON.stringify(payload)}\n\n`);
};
const emitDesktopNotification = (payload) => {
const desktopNotifyEnabled = getDesktopNotifyEnabled();
if (!desktopNotifyEnabled) {
return;
}
if (!payload || typeof payload !== 'object') {
return;
}
if (onDesktopNotification) {
try {
onDesktopNotification(payload);
} catch {
// ignore host-side throw
}
return;
}
try {
// stdout fallback for runtimes that parse the one-line `${prefix}{json}` protocol.
process.stdout.write(`${desktopNotifyPrefix}${JSON.stringify(payload)}\n`);
} catch {
// ignore
}
};
const broadcastUiNotification = (payload) => {
const desktopNotifyEnabled = getDesktopNotifyEnabled();
if (!payload || typeof payload !== 'object') {
return;
}
const syntheticPayload = {
type: 'openchamber:notification',
properties: {
...payload,
// Tell the UI whether the stdout notification channel is active.
// When true, the desktop UI should skip this SSE notification to avoid duplicates.
// When false, the UI must handle this SSE notification itself.
desktopStdoutActive: desktopNotifyEnabled,
},
};
const broadcastGlobalUiEvent = typeof getBroadcastGlobalUiEvent === 'function'
? getBroadcastGlobalUiEvent()
: null;
if (broadcastGlobalUiEvent) {
broadcastGlobalUiEvent(syntheticPayload);
return;
}
const clients = getUiNotificationClients();
if (clients.size === 0) {
return;
}
for (const res of clients) {
try {
writeSseEvent(res, syntheticPayload);
} catch {
// ignore
}
}
};
return {
writeSseEvent,
emitDesktopNotification,
broadcastUiNotification,
setOnDesktopNotification,
};
};