mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-29 18:12:10 +08:00
Adds a zero-dependency `frontend-contract` bin that serializes the real NoticeStatus variants and FrontendEvent payloads, scripts/gen-contract.mjs which emits the committed src/services/contract.ts, and a CI job that regenerates it and fails on a non-empty diff. events.ts now derives its event map from the generated contract instead of hand-typed shapes.
64 lines
1.7 KiB
TypeScript
64 lines
1.7 KiB
TypeScript
import { listen, type UnlistenFn } from '@tauri-apps/api/event'
|
|
|
|
import type { VergeEventPayloads } from './contract'
|
|
|
|
/** Generated wire contract plus the frontend-internal test-all event. */
|
|
interface VergeEvents extends VergeEventPayloads {
|
|
'verge://test-all': null
|
|
}
|
|
|
|
type VergeEventName = keyof VergeEvents
|
|
|
|
type VergeEventHandlers = {
|
|
[Name in VergeEventName]?: (payload: VergeEvents[Name]) => void
|
|
}
|
|
|
|
/**
|
|
* Returns synchronous teardown for asynchronous registrations. `onSubscribed` lets callers
|
|
* reread event-only state after all listeners are live, closing the initial race window.
|
|
*/
|
|
export const subscribeVergeEvents = (
|
|
handlers: VergeEventHandlers,
|
|
onSubscribed?: () => void,
|
|
): (() => void) => {
|
|
let disposed = false
|
|
const unlisteners: UnlistenFn[] = []
|
|
|
|
const registrations = Object.entries(handlers).map(([name, handler]) => {
|
|
if (!handler) return Promise.resolve()
|
|
|
|
return listen(name, ({ payload }) => {
|
|
;(handler as (payload: unknown) => void)(payload)
|
|
})
|
|
.then((unlisten) => {
|
|
// Resolved after teardown: unsubscribe immediately rather than leak.
|
|
if (disposed) {
|
|
unlisten()
|
|
return
|
|
}
|
|
unlisteners.push(unlisten)
|
|
})
|
|
.catch((error) => {
|
|
console.error(`[events] failed to subscribe to ${name}:`, error)
|
|
})
|
|
})
|
|
|
|
if (onSubscribed) {
|
|
void Promise.all(registrations).then(() => {
|
|
if (disposed) return
|
|
onSubscribed()
|
|
})
|
|
}
|
|
|
|
return () => {
|
|
disposed = true
|
|
for (const unlisten of unlisteners.splice(0)) {
|
|
try {
|
|
unlisten()
|
|
} catch (error) {
|
|
console.error('[events] teardown failed:', error)
|
|
}
|
|
}
|
|
}
|
|
}
|