///
import type {
CounterfactualAnalysis,
LeavePlacement,
PlanOutcome,
PlanningExplanation,
PlanningOutcome,
PlanningReason,
PlanningSnapshot,
PlanningStateSnapshot,
} from './leave-planner.service';
import {
analyzeCounterfactualInternal,
calculatePlanningAsyncInternal,
explainIncompleteCheckpoint,
type PlanningArtifacts,
} from './leave-planner.core';
type WorkerPlanningRequest = {
calculationDate?: string;
declaredDays?: unknown;
horizon?: { start?: unknown; end?: unknown };
balances?: unknown;
rules?: unknown;
identities?: unknown;
labels?: unknown;
deadlineMs?: unknown;
progress?: unknown;
};
type WorkerRequest =
| {
type: 'plan';
request: WorkerPlanningRequest;
}
| { type: 'cancel' }
| {
type: 'counterfactual';
request: PlanningSnapshot;
plan: PlanOutcome;
placement: LeavePlacement;
state?: PlanningStateSnapshot;
}
| {
type: 'explain-checkpoint';
request: PlanningSnapshot;
plan: PlanOutcome;
state: PlanningStateSnapshot;
interruption: PlanningReason;
};
type WorkerResponse =
| { type: 'heartbeat' }
| { type: 'progress'; stage: string }
| { type: 'checkpoint'; plan: PlanOutcome; state?: PlanningStateSnapshot }
| { type: 'result'; outcome: PlanningOutcome }
| { type: 'artifacts'; artifacts: PlanningArtifacts }
| {
type: 'explanation';
explanation?: PlanningExplanation;
explanationError?: PlanningReason;
finalState?: PlanningStateSnapshot;
}
| { type: 'counterfactual-result'; analysis: CounterfactualAnalysis }
| { type: 'error'; code?: string; message: string };
const worker = self as DedicatedWorkerGlobalScope;
let activeCalculation: { cancelled: boolean } | undefined;
worker.onmessage = async (event: MessageEvent) => {
try {
if (event.data?.type === 'cancel') {
if (activeCalculation) activeCalculation.cancelled = true;
return;
}
if (event.data?.type === 'plan') {
const calculation = { cancelled: false };
activeCalculation = calculation;
let artifacts: PlanningArtifacts = {};
let outcomePosted = false;
let outcome: PlanningOutcome;
// The cooperative driver yields during preparation and search, even within one phase.
const heartbeat = () =>
worker.postMessage({ type: 'heartbeat' } satisfies WorkerResponse);
const heartbeatTimer = setInterval(heartbeat, 1000);
try {
heartbeat();
outcome = await calculatePlanningAsyncInternal(
{
...event.data.request,
progress: (stage: string) =>
worker.postMessage({
type: 'progress',
stage,
} satisfies WorkerResponse),
},
(plan, state) =>
worker.postMessage({
type: 'checkpoint',
plan,
state,
} satisfies WorkerResponse),
(value) => (artifacts = value),
(value) => {
outcomePosted = true;
worker.postMessage({
type: 'result',
outcome: value,
} satisfies WorkerResponse);
},
(explanation, explanationError, finalState) =>
worker.postMessage({
type: 'explanation',
explanation,
explanationError,
finalState,
} satisfies WorkerResponse),
() => calculation.cancelled,
);
} catch (error) {
worker.postMessage({
type: 'error',
code: 'planner-error',
message: error instanceof Error ? error.message : String(error),
} satisfies WorkerResponse);
return;
} finally {
clearInterval(heartbeatTimer);
if (activeCalculation === calculation) activeCalculation = undefined;
}
if (!outcomePosted)
worker.postMessage({ type: 'result', outcome } satisfies WorkerResponse);
worker.postMessage({ type: 'artifacts', artifacts } satisfies WorkerResponse);
return;
}
if (event.data?.type === 'explain-checkpoint') {
const result = explainIncompleteCheckpoint(
event.data.request,
event.data.plan,
event.data.state,
event.data.interruption,
);
worker.postMessage({ type: 'explanation', ...result } satisfies WorkerResponse);
return;
}
if (event.data?.type !== 'counterfactual') return;
try {
const analysis = analyzeCounterfactualInternal(
event.data.request,
event.data.plan,
event.data.placement,
event.data.state,
);
worker.postMessage({
type: 'counterfactual-result',
analysis,
} satisfies WorkerResponse);
} catch (error) {
worker.postMessage({
type: 'error',
code: 'counterfactual-error',
message: error instanceof Error ? error.message : String(error),
} satisfies WorkerResponse);
}
} catch (error) {
worker.postMessage({
type: 'error',
code: 'worker-error',
message: error instanceof Error ? error.message : String(error),
} satisfies WorkerResponse);
}
};