tftsr-devops_investigation/node_modules/mdast-util-to-markdown/lib/index.js
Shaun Arman 8839075805 feat: initial implementation of TFTSR IT Triage & RCA application
Implements Phases 1-8 of the TFTSR implementation plan.

Rust backend (Tauri 2.x, src-tauri/):
- Multi-provider AI: OpenAI-compatible, Anthropic, Gemini, Mistral, Ollama
- PII detection engine: 11 regex patterns with overlap resolution
- SQLCipher AES-256 encrypted database with 10 versioned migrations
- 28 Tauri IPC commands for triage, analysis, document, and system ops
- Ollama: hardware probe, model recommendations, pull/delete with events
- RCA and blameless post-mortem Markdown document generators
- PDF export via printpdf
- Audit log: SHA-256 hash of every external data send
- Integration stubs for Confluence, ServiceNow, Azure DevOps (v0.2)

Frontend (React 18 + TypeScript + Vite, src/):
- 9 pages: full triage workflow NewIssue→LogUpload→Triage→Resolution→RCA→Postmortem→History+Settings
- 7 components: ChatWindow, TriageProgress, PiiDiffViewer, DocEditor, HardwareReport, ModelSelector, UI primitives
- 3 Zustand stores: session, settings (persisted), history
- Type-safe tauriCommands.ts matching Rust backend types exactly
- 8 IT domain system prompts (Linux, Windows, Network, K8s, DB, Virt, HW, Obs)

DevOps:
- .woodpecker/test.yml: rustfmt, clippy, cargo test, tsc, vitest on every push
- .woodpecker/release.yml: linux/amd64 + linux/arm64 builds, Gogs release upload

Verified:
- cargo check: zero errors
- tsc --noEmit: zero errors
- vitest run: 13/13 unit tests passing

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 22:36:25 -05:00

188 lines
4.7 KiB
JavaScript

/**
* @import {Info, Join, Options, SafeConfig, State} from 'mdast-util-to-markdown'
* @import {Nodes} from 'mdast'
* @import {Enter, FlowParents, PhrasingParents, TrackFields} from './types.js'
*/
import {zwitch} from 'zwitch'
import {configure} from './configure.js'
import {handle as handlers} from './handle/index.js'
import {join} from './join.js'
import {unsafe} from './unsafe.js'
import {association} from './util/association.js'
import {compilePattern} from './util/compile-pattern.js'
import {containerPhrasing} from './util/container-phrasing.js'
import {containerFlow} from './util/container-flow.js'
import {indentLines} from './util/indent-lines.js'
import {safe} from './util/safe.js'
import {track} from './util/track.js'
/**
* Turn an mdast syntax tree into markdown.
*
* @param {Nodes} tree
* Tree to serialize.
* @param {Options | null | undefined} [options]
* Configuration (optional).
* @returns {string}
* Serialized markdown representing `tree`.
*/
export function toMarkdown(tree, options) {
const settings = options || {}
/** @type {State} */
const state = {
associationId: association,
containerPhrasing: containerPhrasingBound,
containerFlow: containerFlowBound,
createTracker: track,
compilePattern,
enter,
// @ts-expect-error: GFM / frontmatter are typed in `mdast` but not defined
// here.
handlers: {...handlers},
// @ts-expect-error: add `handle` in a second.
handle: undefined,
indentLines,
indexStack: [],
join: [...join],
options: {},
safe: safeBound,
stack: [],
unsafe: [...unsafe]
}
configure(state, settings)
if (state.options.tightDefinitions) {
state.join.push(joinDefinition)
}
state.handle = zwitch('type', {
invalid,
unknown,
handlers: state.handlers
})
let result = state.handle(tree, undefined, state, {
before: '\n',
after: '\n',
now: {line: 1, column: 1},
lineShift: 0
})
if (
result &&
result.charCodeAt(result.length - 1) !== 10 &&
result.charCodeAt(result.length - 1) !== 13
) {
result += '\n'
}
return result
/** @type {Enter} */
function enter(name) {
state.stack.push(name)
return exit
/**
* @returns {undefined}
*/
function exit() {
state.stack.pop()
}
}
}
/**
* @param {unknown} value
* @returns {never}
*/
function invalid(value) {
throw new Error('Cannot handle value `' + value + '`, expected node')
}
/**
* @param {unknown} value
* @returns {never}
*/
function unknown(value) {
// Always a node.
const node = /** @type {Nodes} */ (value)
throw new Error('Cannot handle unknown node `' + node.type + '`')
}
/** @type {Join} */
function joinDefinition(left, right) {
// No blank line between adjacent definitions.
if (left.type === 'definition' && left.type === right.type) {
return 0
}
}
/**
* Serialize the children of a parent that contains phrasing children.
*
* These children will be joined flush together.
*
* @this {State}
* Info passed around about the current state.
* @param {PhrasingParents} parent
* Parent of flow nodes.
* @param {Info} info
* Info on where we are in the document we are generating.
* @returns {string}
* Serialized children, joined together.
*/
function containerPhrasingBound(parent, info) {
return containerPhrasing(parent, this, info)
}
/**
* Serialize the children of a parent that contains flow children.
*
* These children will typically be joined by blank lines.
* What they are joined by exactly is defined by `Join` functions.
*
* @this {State}
* Info passed around about the current state.
* @param {FlowParents} parent
* Parent of flow nodes.
* @param {TrackFields} info
* Info on where we are in the document we are generating.
* @returns {string}
* Serialized children, joined by (blank) lines.
*/
function containerFlowBound(parent, info) {
return containerFlow(parent, this, info)
}
/**
* Make a string safe for embedding in markdown constructs.
*
* In markdown, almost all punctuation characters can, in certain cases,
* result in something.
* Whether they do is highly subjective to where they happen and in what
* they happen.
*
* To solve this, `mdast-util-to-markdown` tracks:
*
* * Characters before and after something;
* * What “constructs” we are in.
*
* This information is then used by this function to escape or encode
* special characters.
*
* @this {State}
* Info passed around about the current state.
* @param {string | null | undefined} value
* Raw value to make safe.
* @param {SafeConfig} config
* Configuration.
* @returns {string}
* Serialized markdown safe for embedding.
*/
function safeBound(value, config) {
return safe(this, value, config)
}