Files
shd.build/src/lib/server/build-mechanic-runtime.ts
T

115 lines
3.6 KiB
TypeScript

import type { Database } from '@tursodatabase/database';
import {
loadSelectedMechanicRuntimesForOwnerReferences,
mechanicRuntimeOwnerReferenceKey,
type MechanicRuntimeSelection,
} from './mechanic-runtime-selection';
export type BuildMechanicRuntimeOwnerIds = {
talentIds: readonly string[];
protocolTalentIds: readonly string[];
skillChipTalentIds: readonly string[];
specializationAttachmentIds: readonly string[];
};
export type BuildMechanicRuntimes = {
talents: Record<string, MechanicRuntimeSelection | null>;
protocolTalents: Record<string, MechanicRuntimeSelection | null>;
skillChipTalents: Record<string, MechanicRuntimeSelection | null>;
specializationAttachments: Record<string, MechanicRuntimeSelection | null>;
};
function uniqueNonEmptyIds(ids: readonly string[]) {
return [...new Set(ids.filter((id) => id.trim().length > 0))];
}
function emptyRuntimeMap(ids: readonly string[]) {
return Object.fromEntries(ids.map((id) => [id, null])) as Record<
string,
MechanicRuntimeSelection | null
>;
}
/**
* Hydrates the only executable capability exposed to Build surfaces. One
* bounded bulk read resolves source metadata, selected revisions, graph rows,
* and active attributes. Every owner still receives an independent null when
* its source cannot be validated.
*/
export async function loadBuildMechanicRuntimes(
database: Database,
input: BuildMechanicRuntimeOwnerIds,
): Promise<BuildMechanicRuntimes> {
const talentIds = uniqueNonEmptyIds(input.talentIds);
const protocolTalentIds = uniqueNonEmptyIds(input.protocolTalentIds);
const skillChipTalentIds = uniqueNonEmptyIds(input.skillChipTalentIds);
const specializationAttachmentIds = uniqueNonEmptyIds(
input.specializationAttachmentIds,
);
const runtimes: BuildMechanicRuntimes = {
talents: emptyRuntimeMap(talentIds),
protocolTalents: emptyRuntimeMap(protocolTalentIds),
skillChipTalents: emptyRuntimeMap(skillChipTalentIds),
specializationAttachments: emptyRuntimeMap(specializationAttachmentIds),
};
const owners = [
...talentIds.map((id) => ({ type: 'talent' as const, id })),
...protocolTalentIds.map((id) => ({
type: 'os_protocol_talent' as const,
id,
})),
...skillChipTalentIds.map((id) => ({
type: 'skill_chip_talent' as const,
id,
})),
...specializationAttachmentIds.map((id) => ({
type: 'specialization_tree_rank' as const,
id,
})),
];
if (!owners.length) return runtimes;
try {
const selections = await loadSelectedMechanicRuntimesForOwnerReferences(
database,
owners,
);
for (const id of talentIds) {
runtimes.talents[id] =
selections.get(mechanicRuntimeOwnerReferenceKey({ type: 'talent', id })) ??
null;
}
for (const id of protocolTalentIds) {
runtimes.protocolTalents[id] =
selections.get(
mechanicRuntimeOwnerReferenceKey({
type: 'os_protocol_talent',
id,
}),
) ?? null;
}
for (const id of skillChipTalentIds) {
runtimes.skillChipTalents[id] =
selections.get(
mechanicRuntimeOwnerReferenceKey({
type: 'skill_chip_talent',
id,
}),
) ?? null;
}
for (const id of specializationAttachmentIds) {
runtimes.specializationAttachments[id] =
selections.get(
mechanicRuntimeOwnerReferenceKey({
type: 'specialization_tree_rank',
id,
}),
) ?? null;
}
} catch {
// An unavailable metadata read has no executable fallback at this boundary.
}
return runtimes;
}