fix(ipc-thread-handlers): drop redundant try/catch wrappers and duplicate emit
Every handler was wrapping its body in try/catch and returning fail(code,
error.message), leaking raw DB messages to the renderer and bypassing the
sanitizer in registerHandle. Removed the 14 inner try/catch shells so
errors surface through ipc-support's IPC_HANDLER_FAILED / IPC_INVALID_PAYLOAD
path. Also removed the duplicate emitEvent({ type: 'domain-changed' }) in
addThreadEvent — publishDomainChanged already emits the same event.
This commit is contained in:
1 parent
6c6cc96060
commit
deb980448c
1 file changed
+139
-200
+139
-200
@@ -21,249 +21,188 @@ import { fail, ok } from "./ipc-core-common";
|
||||
|
||||
export function registerThreadHandlers({ registerHandle, db, ensureCompanyExists, publishDomainChanged, emitEvent }: RegisterCoreHandlersDependencies) {
|
||||
registerHandle(IPC_CHANNELS.createThread, async (_event, payload) => {
|
||||
try {
|
||||
const input = createThreadSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
const thread = db.createThread({
|
||||
companyId: input.companyId,
|
||||
title: input.title,
|
||||
type: input.type,
|
||||
relatedEntityType: input.relatedEntityType ?? null,
|
||||
relatedEntityId: input.relatedEntityId ?? null,
|
||||
});
|
||||
publishDomainChanged();
|
||||
return ok(thread);
|
||||
} catch (error) {
|
||||
return fail("THREAD_CREATE_FAILED", error instanceof Error ? error.message : "Failed to create thread");
|
||||
}
|
||||
const input = createThreadSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
const thread = db.createThread({
|
||||
companyId: input.companyId,
|
||||
title: input.title,
|
||||
type: input.type,
|
||||
relatedEntityType: input.relatedEntityType ?? null,
|
||||
relatedEntityId: input.relatedEntityId ?? null,
|
||||
});
|
||||
publishDomainChanged();
|
||||
return ok(thread);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.listThreads, async (_event, payload) => {
|
||||
try {
|
||||
const input = listThreadsSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
const threads = db.listThreads({
|
||||
companyId: input.companyId,
|
||||
type: input.type,
|
||||
relatedEntityType: input.relatedEntityType,
|
||||
relatedEntityId: input.relatedEntityId,
|
||||
limit: input.limit,
|
||||
offset: input.offset,
|
||||
});
|
||||
return ok(threads);
|
||||
} catch (error) {
|
||||
return fail("THREAD_LIST_FAILED", error instanceof Error ? error.message : "Failed to list threads");
|
||||
}
|
||||
const input = listThreadsSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
const threads = db.listThreads({
|
||||
companyId: input.companyId,
|
||||
type: input.type,
|
||||
relatedEntityType: input.relatedEntityType,
|
||||
relatedEntityId: input.relatedEntityId,
|
||||
limit: input.limit,
|
||||
offset: input.offset,
|
||||
});
|
||||
return ok(threads);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.getThread, async (_event, payload) => {
|
||||
try {
|
||||
const input = threadGetterSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
const thread = db.getThread(input.threadId, input.companyId);
|
||||
if (!thread) return fail("THREAD_NOT_FOUND", "Thread not found");
|
||||
return ok(thread);
|
||||
} catch (error) {
|
||||
return fail("THREAD_GET_FAILED", error instanceof Error ? error.message : "Failed to get thread");
|
||||
}
|
||||
const input = threadGetterSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
const thread = db.getThread(input.threadId, input.companyId);
|
||||
if (!thread) return fail("THREAD_NOT_FOUND", "Thread not found");
|
||||
return ok(thread);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.addThreadEvent, async (_event, payload) => {
|
||||
try {
|
||||
const input = addThreadEventSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
const event = db.addThreadEvent({
|
||||
threadId: input.threadId,
|
||||
actorId: input.actorId,
|
||||
actorType: input.actorType,
|
||||
eventType: input.eventType,
|
||||
body: input.body,
|
||||
metadataJson: input.metadataJson,
|
||||
});
|
||||
publishDomainChanged();
|
||||
emitEvent({ type: "domain-changed" });
|
||||
return ok(event);
|
||||
} catch (error) {
|
||||
return fail("THREAD_EVENT_ADD_FAILED", error instanceof Error ? error.message : "Failed to add thread event");
|
||||
}
|
||||
const input = addThreadEventSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
const event = db.addThreadEvent({
|
||||
threadId: input.threadId,
|
||||
actorId: input.actorId,
|
||||
actorType: input.actorType,
|
||||
eventType: input.eventType,
|
||||
body: input.body,
|
||||
metadataJson: input.metadataJson,
|
||||
});
|
||||
publishDomainChanged();
|
||||
return ok(event);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.listThreadEvents, async (_event, payload) => {
|
||||
try {
|
||||
const input = listThreadEventsSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
const events = db.listThreadEvents(input.threadId, input.companyId, {
|
||||
limit: input.limit,
|
||||
offset: input.offset,
|
||||
});
|
||||
return ok(events);
|
||||
} catch (error) {
|
||||
return fail("THREAD_EVENTS_LIST_FAILED", error instanceof Error ? error.message : "Failed to list thread events");
|
||||
}
|
||||
const input = listThreadEventsSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
const events = db.listThreadEvents(input.threadId, input.companyId, {
|
||||
limit: input.limit,
|
||||
offset: input.offset,
|
||||
});
|
||||
return ok(events);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.setThreadWaitState, async (_event, payload) => {
|
||||
try {
|
||||
const input = setThreadWaitStateSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
const waitState = db.setThreadWaitState({
|
||||
threadId: input.threadId,
|
||||
companyId: input.companyId,
|
||||
waitingFor: input.waitingFor,
|
||||
relatedApprovalId: input.relatedApprovalId ?? null,
|
||||
reason: input.reason,
|
||||
});
|
||||
publishDomainChanged();
|
||||
emitEvent({
|
||||
type: "thread-wait-state-updated",
|
||||
threadId: input.threadId,
|
||||
companyId: input.companyId,
|
||||
waitStateId: waitState.id,
|
||||
action: "created",
|
||||
});
|
||||
return ok(waitState);
|
||||
} catch (error) {
|
||||
return fail("THREAD_WAIT_STATE_SET_FAILED", error instanceof Error ? error.message : "Failed to set thread wait state");
|
||||
}
|
||||
const input = setThreadWaitStateSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
const waitState = db.setThreadWaitState({
|
||||
threadId: input.threadId,
|
||||
companyId: input.companyId,
|
||||
waitingFor: input.waitingFor,
|
||||
relatedApprovalId: input.relatedApprovalId ?? null,
|
||||
reason: input.reason,
|
||||
});
|
||||
publishDomainChanged();
|
||||
emitEvent({
|
||||
type: "thread-wait-state-updated",
|
||||
threadId: input.threadId,
|
||||
companyId: input.companyId,
|
||||
waitStateId: waitState.id,
|
||||
action: "created",
|
||||
});
|
||||
return ok(waitState);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.resolveThreadWaitState, async (_event, payload) => {
|
||||
try {
|
||||
const input = resolveThreadWaitStateSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
const input = resolveThreadWaitStateSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
|
||||
// Query the wait state before resolving to capture its threadId for the event
|
||||
const waitStates = db.listThreadWaitStates(input.companyId);
|
||||
const target = waitStates.find((ws) => ws.id === input.waitStateId);
|
||||
if (!target) return fail("THREAD_WAIT_STATE_NOT_FOUND", "Wait state not found");
|
||||
// Query the wait state before resolving to capture its threadId for the event
|
||||
const waitStates = db.listThreadWaitStates(input.companyId);
|
||||
const target = waitStates.find((ws) => ws.id === input.waitStateId);
|
||||
if (!target) return fail("THREAD_WAIT_STATE_NOT_FOUND", "Wait state not found");
|
||||
|
||||
db.resolveThreadWaitState(input.waitStateId, input.companyId);
|
||||
publishDomainChanged();
|
||||
emitEvent({
|
||||
type: "thread-wait-state-updated",
|
||||
threadId: target.threadId,
|
||||
companyId: input.companyId,
|
||||
waitStateId: input.waitStateId,
|
||||
action: "resolved",
|
||||
});
|
||||
return ok(true);
|
||||
} catch (error) {
|
||||
return fail("THREAD_WAIT_STATE_RESOLVE_FAILED", error instanceof Error ? error.message : "Failed to resolve thread wait state");
|
||||
}
|
||||
db.resolveThreadWaitState(input.waitStateId, input.companyId);
|
||||
publishDomainChanged();
|
||||
emitEvent({
|
||||
type: "thread-wait-state-updated",
|
||||
threadId: target.threadId,
|
||||
companyId: input.companyId,
|
||||
waitStateId: input.waitStateId,
|
||||
action: "resolved",
|
||||
});
|
||||
return ok(true);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.listThreadWaitStates, async (_event, payload) => {
|
||||
try {
|
||||
const input = listThreadWaitStatesSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
const waitStates = db.listThreadWaitStates(input.companyId, { resolvedOnly: input.resolvedOnly });
|
||||
return ok(waitStates);
|
||||
} catch (error) {
|
||||
return fail("THREAD_WAIT_STATES_LIST_FAILED", error instanceof Error ? error.message : "Failed to list thread wait states");
|
||||
}
|
||||
const input = listThreadWaitStatesSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
const waitStates = db.listThreadWaitStates(input.companyId, { resolvedOnly: input.resolvedOnly });
|
||||
return ok(waitStates);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.listParticipants, async (_event, payload) => {
|
||||
try {
|
||||
const input = listParticipantsSchema.parse(payload);
|
||||
const thread = db.getThread(input.threadId, input.companyId);
|
||||
if (!thread) return fail("THREAD_NOT_FOUND", "Thread not found in this company");
|
||||
const participants = db.listParticipants(input.threadId);
|
||||
return ok(participants);
|
||||
} catch (error) {
|
||||
return fail("PARTICIPANTS_LIST_FAILED", error instanceof Error ? error.message : "Failed to list participants");
|
||||
}
|
||||
const input = listParticipantsSchema.parse(payload);
|
||||
const thread = db.getThread(input.threadId, input.companyId);
|
||||
if (!thread) return fail("THREAD_NOT_FOUND", "Thread not found in this company");
|
||||
const participants = db.listParticipants(input.threadId);
|
||||
return ok(participants);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.addParticipant, async (_event, payload) => {
|
||||
try {
|
||||
const input = addParticipantSchema.parse(payload);
|
||||
const thread = db.getThread(input.threadId, input.companyId);
|
||||
if (!thread) return fail("THREAD_NOT_FOUND", "Thread not found in this company");
|
||||
const participant = db.addParticipant({
|
||||
threadId: input.threadId,
|
||||
participantId: input.participantId,
|
||||
participantType: input.participantType,
|
||||
role: input.role,
|
||||
});
|
||||
publishDomainChanged();
|
||||
return ok(participant);
|
||||
} catch (error) {
|
||||
return fail("PARTICIPANT_ADD_FAILED", error instanceof Error ? error.message : "Failed to add participant");
|
||||
}
|
||||
const input = addParticipantSchema.parse(payload);
|
||||
const thread = db.getThread(input.threadId, input.companyId);
|
||||
if (!thread) return fail("THREAD_NOT_FOUND", "Thread not found in this company");
|
||||
const participant = db.addParticipant({
|
||||
threadId: input.threadId,
|
||||
participantId: input.participantId,
|
||||
participantType: input.participantType,
|
||||
role: input.role,
|
||||
});
|
||||
publishDomainChanged();
|
||||
return ok(participant);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.removeParticipant, async (_event, payload) => {
|
||||
try {
|
||||
const input = removeParticipantSchema.parse(payload);
|
||||
const thread = db.getThread(input.threadId, input.companyId);
|
||||
if (!thread) return fail("THREAD_NOT_FOUND", "Thread not found in this company");
|
||||
db.removeParticipant(input.threadId, input.participantId);
|
||||
publishDomainChanged();
|
||||
return ok(true);
|
||||
} catch (error) {
|
||||
return fail("PARTICIPANT_REMOVE_FAILED", error instanceof Error ? error.message : "Failed to remove participant");
|
||||
}
|
||||
const input = removeParticipantSchema.parse(payload);
|
||||
const thread = db.getThread(input.threadId, input.companyId);
|
||||
if (!thread) return fail("THREAD_NOT_FOUND", "Thread not found in this company");
|
||||
db.removeParticipant(input.threadId, input.participantId);
|
||||
publishDomainChanged();
|
||||
return ok(true);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.subscribeThread, async (_event, payload) => {
|
||||
try {
|
||||
const input = subscribeThreadSchema.parse(payload);
|
||||
const thread = db.getThread(input.threadId, input.companyId);
|
||||
if (!thread) return fail("THREAD_NOT_FOUND", "Thread not found in this company");
|
||||
const subscription = db.subscribe({
|
||||
threadId: input.threadId,
|
||||
subscriberId: input.subscriberId,
|
||||
subscriberType: input.subscriberType,
|
||||
notifyOn: input.notifyOn,
|
||||
});
|
||||
publishDomainChanged();
|
||||
return ok(subscription);
|
||||
} catch (error) {
|
||||
return fail("THREAD_SUBSCRIBE_FAILED", error instanceof Error ? error.message : "Failed to subscribe to thread");
|
||||
}
|
||||
const input = subscribeThreadSchema.parse(payload);
|
||||
const thread = db.getThread(input.threadId, input.companyId);
|
||||
if (!thread) return fail("THREAD_NOT_FOUND", "Thread not found in this company");
|
||||
const subscription = db.subscribe({
|
||||
threadId: input.threadId,
|
||||
subscriberId: input.subscriberId,
|
||||
subscriberType: input.subscriberType,
|
||||
notifyOn: input.notifyOn,
|
||||
});
|
||||
publishDomainChanged();
|
||||
return ok(subscription);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.unsubscribeThread, async (_event, payload) => {
|
||||
try {
|
||||
const input = unsubscribeThreadSchema.parse(payload);
|
||||
const thread = db.getThread(input.threadId, input.companyId);
|
||||
if (!thread) return fail("THREAD_NOT_FOUND", "Thread not found in this company");
|
||||
db.unsubscribe(input.threadId, input.subscriberId);
|
||||
publishDomainChanged();
|
||||
return ok(true);
|
||||
} catch (error) {
|
||||
return fail("THREAD_UNSUBSCRIBE_FAILED", error instanceof Error ? error.message : "Failed to unsubscribe from thread");
|
||||
}
|
||||
const input = unsubscribeThreadSchema.parse(payload);
|
||||
const thread = db.getThread(input.threadId, input.companyId);
|
||||
if (!thread) return fail("THREAD_NOT_FOUND", "Thread not found in this company");
|
||||
db.unsubscribe(input.threadId, input.subscriberId);
|
||||
publishDomainChanged();
|
||||
return ok(true);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.listSubscriptions, async (_event, payload) => {
|
||||
try {
|
||||
const input = listSubscriptionsSchema.parse(payload);
|
||||
const thread = db.getThread(input.threadId, input.companyId);
|
||||
if (!thread) return fail("THREAD_NOT_FOUND", "Thread not found in this company");
|
||||
const subscriptions = db.listSubscriptions(input.threadId);
|
||||
return ok(subscriptions);
|
||||
} catch (error) {
|
||||
return fail("SUBSCRIPTIONS_LIST_FAILED", error instanceof Error ? error.message : "Failed to list subscriptions");
|
||||
}
|
||||
const input = listSubscriptionsSchema.parse(payload);
|
||||
const thread = db.getThread(input.threadId, input.companyId);
|
||||
if (!thread) return fail("THREAD_NOT_FOUND", "Thread not found in this company");
|
||||
const subscriptions = db.listSubscriptions(input.threadId);
|
||||
return ok(subscriptions);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.listSubscribedThreads, async (_event, payload) => {
|
||||
try {
|
||||
const input = listSubscribedThreadsSchema.parse(payload);
|
||||
const threads = db.listSubscribedThreads(input.subscriberId, input.companyId);
|
||||
return ok(threads);
|
||||
} catch (error) {
|
||||
return fail("SUBSCRIBED_THREADS_LIST_FAILED", error instanceof Error ? error.message : "Failed to list subscribed threads");
|
||||
}
|
||||
const input = listSubscribedThreadsSchema.parse(payload);
|
||||
const threads = db.listSubscribedThreads(input.subscriberId, input.companyId);
|
||||
return ok(threads);
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user