tftsr-devops_investigation/node_modules/mdast-util-to-markdown/lib/util/container-phrasing.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

126 lines
3.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @import {Handle, Info, State} from 'mdast-util-to-markdown'
* @import {PhrasingParents} from '../types.js'
*/
import {encodeCharacterReference} from './encode-character-reference.js'
/**
* Serialize the children of a parent that contains phrasing children.
*
* These children will be joined flush together.
*
* @param {PhrasingParents} parent
* Parent of flow nodes.
* @param {State} state
* Info passed around about the current state.
* @param {Info} info
* Info on where we are in the document we are generating.
* @returns {string}
* Serialized children, joined together.
*/
export function containerPhrasing(parent, state, info) {
const indexStack = state.indexStack
const children = parent.children || []
/** @type {Array<string>} */
const results = []
let index = -1
let before = info.before
/** @type {string | undefined} */
let encodeAfter
indexStack.push(-1)
let tracker = state.createTracker(info)
while (++index < children.length) {
const child = children[index]
/** @type {string} */
let after
indexStack[indexStack.length - 1] = index
if (index + 1 < children.length) {
/** @type {Handle} */
// @ts-expect-error: hush, its actually a `zwitch`.
let handle = state.handle.handlers[children[index + 1].type]
/** @type {Handle} */
// @ts-expect-error: hush, its actually a `zwitch`.
if (handle && handle.peek) handle = handle.peek
after = handle
? handle(children[index + 1], parent, state, {
before: '',
after: '',
...tracker.current()
}).charAt(0)
: ''
} else {
after = info.after
}
// In some cases, html (text) can be found in phrasing right after an eol.
// When wed serialize that, in most cases that would be seen as html
// (flow).
// As we cant escape or so to prevent it from happening, we take a somewhat
// reasonable approach: replace that eol with a space.
// See: <https://github.com/syntax-tree/mdast-util-to-markdown/issues/15>
if (
results.length > 0 &&
(before === '\r' || before === '\n') &&
child.type === 'html'
) {
results[results.length - 1] = results[results.length - 1].replace(
/(\r?\n|\r)$/,
' '
)
before = ' '
// To do: does this work to reset tracker?
tracker = state.createTracker(info)
tracker.move(results.join(''))
}
let value = state.handle(child, parent, state, {
...tracker.current(),
after,
before
})
// If we had to encode the first character after the previous node and its
// still the same character,
// encode it.
if (encodeAfter && encodeAfter === value.slice(0, 1)) {
value =
encodeCharacterReference(encodeAfter.charCodeAt(0)) + value.slice(1)
}
const encodingInfo = state.attentionEncodeSurroundingInfo
state.attentionEncodeSurroundingInfo = undefined
encodeAfter = undefined
// If we have to encode the first character before the current node and
// its still the same character,
// encode it.
if (encodingInfo) {
if (
results.length > 0 &&
encodingInfo.before &&
before === results[results.length - 1].slice(-1)
) {
results[results.length - 1] =
results[results.length - 1].slice(0, -1) +
encodeCharacterReference(before.charCodeAt(0))
}
if (encodingInfo.after) encodeAfter = after
}
tracker.move(value)
results.push(value)
before = value.slice(-1)
}
indexStack.pop()
return results.join('')
}