# shared/selectors/document.selector.ts

> 81 lines of code and 28 definitions.

Tree: Governance tree
Language: typescript
Layer: application
Canonical: https://banes-lab.com/anatomy/governance#file-governance-shared-selectors-document-selector-ts
Source text: https://banes-lab.com/source/governance/shared/selectors/document.selector.ts.txt

Listed in [shared/selectors](https://banes-lab.com/api/source/governance/shared/selectors.md), before [shared/selectors/literal.selector.ts](https://banes-lab.com/source/governance/shared/selectors/literal.selector.ts.md).

## Definitions

- `lineTexts` (lexical_declaration, line 39)
- `markdownTexts` (lexical_declaration, line 54, exported)
- `withoutCode` (lexical_declaration, line 16, exported)
- `isRuleCell` (lexical_declaration, line 30)
- `jsonTexts` (lexical_declaration, line 71, exported)
- `LINE` (lexical_declaration, line 3)
- `FENCE` (lexical_declaration, line 4)
- `FRONTMATTER` (lexical_declaration, line 5)
- `CODE_MARK` (lexical_declaration, line 6)
- `CODE_WORD` (lexical_declaration, line 7)
- `CELL` (lexical_declaration, line 8)
- `RULE_CELL_CHARS` (lexical_declaration, line 9)
- `SPACE` (lexical_declaration, line 10)
- `CODE_KEY` (lexical_declaration, line 11)
- `POINTER` (lexical_declaration, line 12)
- `LOCATION` (lexical_declaration, line 13)
- `FRAGMENT` (lexical_declaration, line 14)
- `output` (lexical_declaration, line 17, exported)
- `inside` (lexical_declaration, line 18, exported)
- `prose` (lexical_declaration, line 40)
- `frontmatterEnd` (lexical_declaration, line 50)
- `lines` (lexical_declaration, line 55, exported)
- `start` (lexical_declaration, line 56, exported)
- `texts` (lexical_declaration, line 57, exported)
- `fenced` (lexical_declaration, line 58, exported)
- `body` (lexical_declaration, line 60, exported)
- `fence` (lexical_declaration, line 61, exported)
- `at` (lexical_declaration, line 73, exported)

## Used by

- [shared/loaders/document.loader.ts](https://banes-lab.com/source/governance/shared/loaders/document.loader.ts.md)

## Source

````typescript
import type { DocumentText } from "../../types/writing.types.ts";

const LINE = "\n";
const FENCE = "```";
const FRONTMATTER = "---";
const CODE_MARK = "`";
const CODE_WORD = "code";
const CELL = "|";
const RULE_CELL_CHARS: ReadonlySet<string> = new Set(["-", ":", " "]);
const SPACE = " ";
const CODE_KEY = "code";
const POINTER = "/";
const LOCATION = ":";
const FRAGMENT = "#";

export const withoutCode = function withoutCode(text: string): string {
    let output = "";
    let inside = false;
    for (const char of text) {
        if (char === CODE_MARK) {
            output += inside ? "" : CODE_WORD;
            inside = !inside;
            continue;
        }
        output += inside ? "" : char;
    }
    return output;
};

const isRuleCell = function isRuleCell(cell: string): boolean {
    for (let at = 0; at < cell.length; at += 1) {
        if (!RULE_CELL_CHARS.has(cell.charAt(at))) {
            return false;
        }
    }
    return true;
};

const lineTexts = function lineTexts(line: string): readonly string[] {
    const prose = withoutCode(line).trim();
    if (!prose.startsWith(CELL)) {
        return [prose];
    }
    return prose
        .split(CELL)
        .map((cell) => cell.trim())
        .filter((cell) => cell.length > 0 && !isRuleCell(cell));
};

const frontmatterEnd = function frontmatterEnd(lines: readonly string[]): number {
    return lines[0]?.trim() === FRONTMATTER ? lines.indexOf(FRONTMATTER, 1) + 1 : 0;
};

export const markdownTexts = function markdownTexts(label: string, content: string): DocumentText[] {
    const lines = content.split(LINE).map((line) => line.trimEnd());
    const start = frontmatterEnd(lines);
    const texts: DocumentText[] = [];
    let fenced = false;
    for (const [index, line] of lines.entries()) {
        const body = index >= start;
        const fence = body && line.trimStart().startsWith(FENCE);
        fenced = fence ? !fenced : fenced;
        if (body && !fence && !fenced && line.trim().length > 0) {
            const at = `${label}${LOCATION}${String(index + 1)}`;
            texts.push(...lineTexts(line).map((text) => ({ at, text })));
        }
    }
    return texts;
};

export const jsonTexts = function jsonTexts(label: string, value: unknown, pointer = ""): DocumentText[] {
    if (typeof value === "string") {
        const at = `${label}${FRAGMENT}${pointer}`;
        return value
            .split(LINE)
            .filter((line) => line.includes(SPACE))
            .map((line) => ({ at, text: withoutCode(line) }));
    }
    if (Array.isArray(value)) {
        return value.flatMap((item, index) => jsonTexts(label, item, `${pointer}${POINTER}${String(index)}`));
    }
    if (typeof value !== "object" || value === null) {
        return [];
    }
    return Object.entries(value).flatMap(([key, item]) =>
        key === CODE_KEY ? [] : jsonTexts(label, item, `${pointer}${POINTER}${key}`),
    );
};
````
