im_wower
·
2026-03-29
extract.ts
1import type { BaaExtractedBlock } from "./types.js";
2
3const CODE_FENCE_OPEN_PATTERN = /^[ \t]{0,3}```([^\n]*)$/u;
4const CODE_FENCE_CLOSE_PATTERN = /^[ \t]{0,3}```[ \t]*$/u;
5
6export class BaaInstructionExtractError extends Error {
7 readonly stage = "extract";
8
9 constructor(message: string) {
10 super(message);
11 }
12}
13
14export function extractBaaInstructionBlocks(text: string): BaaExtractedBlock[] {
15 const normalizedText = text.replace(/\r\n?/gu, "\n");
16 const lines = normalizedText.split("\n");
17 const blocks: BaaExtractedBlock[] = [];
18 let pending:
19 | {
20 contentLines: string[];
21 isBaa: boolean;
22 openingLine: string;
23 }
24 | null = null;
25
26 for (const line of lines) {
27 if (pending == null) {
28 const match = line.match(CODE_FENCE_OPEN_PATTERN);
29
30 if (!match) {
31 continue;
32 }
33
34 pending = {
35 contentLines: [],
36 isBaa: /^baa(?:\s|$)/u.test((match[1] ?? "").trim()),
37 openingLine: line
38 };
39 continue;
40 }
41
42 if (CODE_FENCE_CLOSE_PATTERN.test(line)) {
43 if (pending.isBaa) {
44 blocks.push({
45 blockIndex: blocks.length,
46 content: pending.contentLines.join("\n"),
47 rawBlock: `${pending.openingLine}\n${pending.contentLines.join("\n")}\n${line}`
48 });
49 }
50
51 pending = null;
52 continue;
53 }
54
55 pending.contentLines.push(line);
56 }
57
58 return blocks;
59}