export interface RawAnkiNote {
noteId?: number;
id?: number;
modelName: string;
fields?: Record<string, { value?: string }>;
tags?: string[];
}
export interface AnkiCard {
noteId: number;
fields: {
Front: { value: string };
Back: { value: string };
};
tags: string[];
}
export const DEFAULT_CHUNK_SIZE = 5;
export function mapNoteToAnkiCard(note: RawAnkiNote): AnkiCard {
const noteId = note.noteId ?? note.id;
if (typeof noteId !== "number") {
throw new Error("Note is missing a numeric identifier");
}
const tags = Array.isArray(note.tags) ? note.tags : [];
const fields = note.fields ?? {};
if (note.modelName === "Cloze") {
const textValue = fields.Text?.value ?? "[Missing cloze text]";
const backExtraValue = fields["Back Extra"]?.value ?? "[Cloze deletion]";
return {
noteId,
fields: {
Front: { value: textValue },
Back: { value: backExtraValue || "[Cloze deletion]" },
},
tags,
};
}
if (fields.Front?.value !== undefined || fields.Back?.value !== undefined) {
return {
noteId,
fields: {
Front: { value: fields.Front?.value ?? "[Missing Front field]" },
Back: { value: fields.Back?.value ?? "[Missing Back field]" },
},
tags,
};
}
return {
noteId,
fields: {
Front: { value: "[Unknown note type]" },
Back: { value: "[Unknown note type]" },
},
tags,
};
}
export async function fetchCardsFromNotes(
noteIds: number[],
fetchChunk: (chunk: number[]) => Promise<RawAnkiNote[]>,
chunkSize: number = DEFAULT_CHUNK_SIZE
): Promise<AnkiCard[]> {
if (noteIds.length === 0) {
return [];
}
const allNotes: RawAnkiNote[] = [];
for (let index = 0; index < noteIds.length; index += chunkSize) {
const chunk = noteIds.slice(index, index + chunkSize);
const chunkNotes = await fetchChunk(chunk);
allNotes.push(...chunkNotes);
}
return allNotes.map((note) => mapNoteToAnkiCard(note));
}
export function formatCardList(cards: AnkiCard[]): string {
return cards
.map((card) => {
return `Note ID: ${card.noteId}\nFront: ${card.fields.Front.value}\nBack: ${card.fields.Back.value}\nTags: ${card.tags.join(", ")}\n---`;
})
.join("\n");
}