feat(housekeeping): orchestrate partial providers

This commit is contained in:
Simo committed 2026-08-27 18:02:23 +02:00
1 parent 60968409aa
commit 4c399873d1
4 files changed
+306

No files matched your search

@@ -0,0 +1,55 @@
import type {
HousekeepingCapabilityContext,
HousekeepingErrorCode,
} from "../contracts";
import { createCorrelationId } from "../correlation";
import { type HousekeepingProvider, runProvider } from "./run-provider";
export interface ProviderPolicy<T> {
timeoutMs: number;
perProviderLimit: number;
combinedLimit: number;
sort(items: readonly T[]): readonly T[];
dedupeKey(item: T): string;
}
export interface ProviderBatchResult<T> {
items: readonly T[];
errors: readonly { providerId: string; code: HousekeepingErrorCode }[];
correlationId: string;
}
export async function orchestrateProviders<T>(
providers: readonly HousekeepingProvider<T>[],
context: HousekeepingCapabilityContext,
policy: ProviderPolicy<T>,
): Promise<ProviderBatchResult<T>> {
const providerResults = await Promise.all(
providers.map((provider) =>
runProvider(provider, context, policy.timeoutMs),
),
);
const items: T[] = [];
const seen = new Set<string>();
const errors: { providerId: string; code: HousekeepingErrorCode }[] = [];
for (const result of providerResults) {
if (result.error !== undefined) {
errors.push({ providerId: result.providerId, code: result.error });
}
for (const item of result.items.slice(0, policy.perProviderLimit)) {
if (items.length >= policy.combinedLimit) break;
const key = policy.dedupeKey(item);
if (seen.has(key)) continue;
seen.add(key);
items.push(item);
}
}
return {
items: policy.sort(items),
errors,
correlationId: createCorrelationId(),
};
}