SOURCE / PINNED RELEASE
Made of little things.
Body Journal
- Release
- e8049f8da4e2…
- Author-recorded commit
- 61c04bf0e8aa…
- License
- LICENSE
- Author’s source reference
- nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-6c4554844c7
Archive hash verified: 4369d778da046a8b…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
import { boundedText, object } from './ai-contract.js';
import type { Concern } from './case.js';
export interface ConcernGroup { id: string; name: string; explanation: string; concernIds: number[] }
export const MAX_GROUPS = 20;
export const GROUP_NAME_LIMIT = 80;
export const GROUP_EXPLANATION_LIMIT = 2000;
export const GROUP_MEANING = 'Patient-suspected relationships, not established causes or clinician conclusions. Grouped concerns may be unrelated; ungrouped concerns are not necessarily unrelated.';
export function parseGroups(value: unknown, concerns: Pick<Concern, 'id'>[]): ConcernGroup[] {
if (!Array.isArray(value) || value.length > MAX_GROUPS) throw new Error('Invalid observation groups.');
const seen = new Set<string>(), validIds = new Set(concerns.map(c => c.id));
return value.map(item => {
const g = object(item), id = boundedText(g.id, 64);
if (!/^[a-zA-Z0-9-]+$/.test(id) || seen.has(id)) throw new Error('Invalid group identity.');
seen.add(id);
if (!Array.isArray(g.concernIds) || g.concernIds.length > 50 || new Set(g.concernIds).size !== g.concernIds.length || g.concernIds.some(id => typeof id !== 'number' || !validIds.has(id))) throw new Error('Invalid group membership.');
return { id, name: boundedText(g.name, GROUP_NAME_LIMIT, true), explanation: boundedText(g.explanation, GROUP_EXPLANATION_LIMIT, true), concernIds: [...g.concernIds] as number[] };
});
}
/** Deleting a concern only removes its links; groups and other observations stay. */
export function unlinkConcern(groups: ConcernGroup[], concernId: number): void {
for (const group of groups) group.concernIds = group.concernIds.filter(id => id !== concernId);
}
export function groupName(group: ConcernGroup): string { return group.name.trim() || 'Unnamed group'; }
