91 lines
2.6 KiB
TypeScript
91 lines
2.6 KiB
TypeScript
import { readVoiceFields } from '../src/core/note';
|
|
import type {
|
|
Env,
|
|
ListedNote,
|
|
NoteStore,
|
|
RecordingStore,
|
|
TranscribeResult,
|
|
Transcriber,
|
|
} from '../src/core/ports';
|
|
|
|
export class MemoryRecordings implements RecordingStore {
|
|
files = new Map<string, ArrayBuffer>();
|
|
|
|
async write(path: string, data: ArrayBuffer): Promise<void> {
|
|
this.files.set(path, data.slice(0));
|
|
}
|
|
|
|
async read(path: string): Promise<ArrayBuffer> {
|
|
const data = this.files.get(path);
|
|
if (!data) throw new Error(`no such file: ${path}`);
|
|
return data.slice(0);
|
|
}
|
|
}
|
|
|
|
export class MemoryNotes implements NoteStore {
|
|
files = new Map<string, string>();
|
|
// Имитация отстающего metadataCache: если задано, list() возвращает это.
|
|
staleList: ListedNote[] | null = null;
|
|
listCalls = 0;
|
|
|
|
async exists(path: string): Promise<boolean> {
|
|
return this.files.has(path);
|
|
}
|
|
|
|
async create(path: string, content: string): Promise<void> {
|
|
if (this.files.has(path)) throw new Error(`already exists: ${path}`);
|
|
this.files.set(path, content);
|
|
}
|
|
|
|
async list(): Promise<ListedNote[]> {
|
|
this.listCalls++;
|
|
if (this.staleList) return this.staleList;
|
|
return [...this.files]
|
|
.map(([path, content]) => ({ path, frontmatter: readVoiceFields(content) as Record<string, unknown> }))
|
|
.filter((note) => 'voice-id' in note.frontmatter);
|
|
}
|
|
|
|
async process(path: string, fn: (content: string) => string): Promise<boolean> {
|
|
const content = this.files.get(path);
|
|
if (content === undefined) return false;
|
|
this.files.set(path, fn(content));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
export type Step =
|
|
| TranscribeResult
|
|
| Error
|
|
| ((audio: ArrayBuffer, mime: string) => Promise<TranscribeResult>);
|
|
|
|
export class ScriptedTranscriber implements Transcriber {
|
|
calls: { audio: ArrayBuffer; mime: string; signal: AbortSignal }[] = [];
|
|
|
|
constructor(public steps: Step[] = []) {}
|
|
|
|
async transcribe(audio: ArrayBuffer, mime: string, signal: AbortSignal): Promise<TranscribeResult> {
|
|
this.calls.push({ audio, mime, signal });
|
|
const step = this.steps.shift();
|
|
if (step === undefined) throw new Error('ScriptedTranscriber: no more steps');
|
|
if (step instanceof Error) throw step;
|
|
if (typeof step === 'function') return step(audio, mime);
|
|
return step;
|
|
}
|
|
}
|
|
|
|
export class TestEnv implements Env {
|
|
constructor(
|
|
public time = new Date('2026-09-27T11:15:03.000Z'),
|
|
public deviceId = 'test-device',
|
|
public random: () => number = () => 0,
|
|
) {}
|
|
|
|
now(): Date {
|
|
return new Date(this.time);
|
|
}
|
|
|
|
advance(ms: number): void {
|
|
this.time = new Date(this.time.getTime() + ms);
|
|
}
|
|
}
|