56 lines
1.3 KiB
TypeScript
56 lines
1.3 KiB
TypeScript
export type Note = {
|
|
id: string;
|
|
text: string;
|
|
x: number;
|
|
y: number;
|
|
author: string;
|
|
color: string;
|
|
};
|
|
|
|
/** Shape accepted when writing a note; positional and color fields fall back to defaults. */
|
|
export type NoteInput = {
|
|
id: string;
|
|
text: string;
|
|
author: string;
|
|
x?: number;
|
|
y?: number;
|
|
color?: string;
|
|
sourceMeta?: Record<string, unknown>;
|
|
};
|
|
|
|
export type Cluster = {
|
|
label: string;
|
|
noteIds: string[];
|
|
};
|
|
|
|
export type ClusterResponse = {
|
|
clusters: Cluster[];
|
|
score: number;
|
|
};
|
|
|
|
export type ValidationResult = {
|
|
valid: boolean;
|
|
reasons: string[];
|
|
};
|
|
|
|
/** noteId to embedding vector. */
|
|
export type EmbeddingMap = Map<string, number[]>;
|
|
|
|
const isStringArray = (value: unknown): value is string[] =>
|
|
Array.isArray(value) && value.every((entry) => typeof entry === 'string');
|
|
|
|
/**
|
|
* Runtime guard for LLM output, which arrives as parsed JSON of unknown shape.
|
|
* Structural correctness beyond this (complete coverage, no duplicates) is the
|
|
* job of validateStructure.
|
|
*/
|
|
export const isClusterArray = (value: unknown): value is Cluster[] => {
|
|
if (!Array.isArray(value)) return false;
|
|
|
|
return value.every((entry) => {
|
|
if (typeof entry !== 'object' || entry === null) return false;
|
|
const candidate = entry as Record<string, unknown>;
|
|
return typeof candidate.label === 'string' && isStringArray(candidate.noteIds);
|
|
});
|
|
};
|