tftsr-devops_investigation/node_modules/postcss-nested/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

362 lines
8.8 KiB
JavaScript

const { AtRule, Rule } = require('postcss')
let parser = require('postcss-selector-parser')
/**
* Run a selector string through postcss-selector-parser
*/
function parse(rawSelector, rule) {
let nodes
try {
parser(parsed => {
nodes = parsed
}).processSync(rawSelector)
} catch (e) {
if (rawSelector.includes(':')) {
throw rule ? rule.error('Missed semicolon') : e
} else {
throw rule ? rule.error(e.message) : e
}
}
return nodes.at(0)
}
/**
* Replaces the "&" token in a node's selector with the parent selector
* similar to what SCSS does.
*
* Mutates the nodes list
*/
function interpolateAmpInSelector(nodes, parent) {
let replaced = false
nodes.each(node => {
if (node.type === 'nesting') {
let clonedParent = parent.clone({})
if (node.value !== '&') {
node.replaceWith(
parse(node.value.replace('&', clonedParent.toString()))
)
} else {
node.replaceWith(clonedParent)
}
replaced = true
} else if ('nodes' in node && node.nodes) {
if (interpolateAmpInSelector(node, parent)) {
replaced = true
}
}
})
return replaced
}
/**
* Combines parent and child selectors, in a SCSS-like way
*/
function mergeSelectors(parent, child) {
let merged = []
parent.selectors.forEach(sel => {
let parentNode = parse(sel, parent)
child.selectors.forEach(selector => {
if (!selector) {
return
}
let node = parse(selector, child)
let replaced = interpolateAmpInSelector(node, parentNode)
if (!replaced) {
node.prepend(parser.combinator({ value: ' ' }))
node.prepend(parentNode.clone({}))
}
merged.push(node.toString())
})
})
return merged
}
/**
* Move a child and its preceeding comment(s) to after "after"
*/
function breakOut(child, after) {
let prev = child.prev()
after.after(child)
while (prev && prev.type === 'comment') {
let nextPrev = prev.prev()
after.after(prev)
prev = nextPrev
}
return child
}
function createFnAtruleChilds(bubble) {
return function atruleChilds(rule, atrule, bubbling, mergeSels = bubbling) {
let children = []
atrule.each(child => {
if (child.type === 'rule' && bubbling) {
if (mergeSels) {
child.selectors = mergeSelectors(rule, child)
}
} else if (child.type === 'atrule' && child.nodes) {
if (bubble[child.name]) {
atruleChilds(rule, child, mergeSels)
} else if (atrule[rootRuleMergeSel] !== false) {
children.push(child)
}
} else {
children.push(child)
}
})
if (bubbling) {
if (children.length) {
let clone = rule.clone({ nodes: [] })
for (let child of children) {
clone.append(child)
}
atrule.prepend(clone)
}
}
}
}
function pickDeclarations(selector, declarations, after) {
let parent = new Rule({
nodes: [],
selector
})
parent.append(declarations)
after.after(parent)
return parent
}
function atruleNames(defaults, custom) {
let list = {}
for (let name of defaults) {
list[name] = true
}
if (custom) {
for (let name of custom) {
list[name.replace(/^@/, '')] = true
}
}
return list
}
function parseRootRuleParams(params) {
params = params.trim()
let braceBlock = params.match(/^\((.*)\)$/)
if (!braceBlock) {
return { selector: params, type: 'basic' }
}
let bits = braceBlock[1].match(/^(with(?:out)?):(.+)$/)
if (bits) {
let allowlist = bits[1] === 'with'
let rules = Object.fromEntries(
bits[2]
.trim()
.split(/\s+/)
.map(name => [name, true])
)
if (allowlist && rules.all) {
return { type: 'noop' }
}
let escapes = rule => !!rules[rule]
if (rules.all) {
escapes = () => true
} else if (allowlist) {
escapes = rule => (rule === 'all' ? false : !rules[rule])
}
return {
escapes,
type: 'withrules'
}
}
// Unrecognized brace block
return { type: 'unknown' }
}
function getAncestorRules(leaf) {
let lineage = []
let parent = leaf.parent
while (parent && parent instanceof AtRule) {
lineage.push(parent)
parent = parent.parent
}
return lineage
}
function unwrapRootRule(rule) {
let escapes = rule[rootRuleEscapes]
if (!escapes) {
rule.after(rule.nodes)
} else {
let nodes = rule.nodes
let topEscaped
let topEscapedIdx = -1
let breakoutLeaf
let breakoutRoot
let clone
let lineage = getAncestorRules(rule)
lineage.forEach((parent, i) => {
if (escapes(parent.name)) {
topEscaped = parent
topEscapedIdx = i
breakoutRoot = clone
} else {
let oldClone = clone
clone = parent.clone({ nodes: [] })
oldClone && clone.append(oldClone)
breakoutLeaf = breakoutLeaf || clone
}
})
if (!topEscaped) {
rule.after(nodes)
} else if (!breakoutRoot) {
topEscaped.after(nodes)
} else {
let leaf = breakoutLeaf
leaf.append(nodes)
topEscaped.after(breakoutRoot)
}
if (rule.next() && topEscaped) {
let restRoot
lineage.slice(0, topEscapedIdx + 1).forEach((parent, i, arr) => {
let oldRoot = restRoot
restRoot = parent.clone({ nodes: [] })
oldRoot && restRoot.append(oldRoot)
let nextSibs = []
let _child = arr[i - 1] || rule
let next = _child.next()
while (next) {
nextSibs.push(next)
next = next.next()
}
restRoot.append(nextSibs)
})
restRoot && (breakoutRoot || nodes[nodes.length - 1]).after(restRoot)
}
}
rule.remove()
}
const rootRuleMergeSel = Symbol('rootRuleMergeSel')
const rootRuleEscapes = Symbol('rootRuleEscapes')
function normalizeRootRule(rule) {
let { params } = rule
let { escapes, selector, type } = parseRootRuleParams(params)
if (type === 'unknown') {
throw rule.error(
`Unknown @${rule.name} parameter ${JSON.stringify(params)}`
)
}
if (type === 'basic' && selector) {
let selectorBlock = new Rule({ nodes: rule.nodes, selector })
rule.removeAll()
rule.append(selectorBlock)
}
rule[rootRuleEscapes] = escapes
rule[rootRuleMergeSel] = escapes ? !escapes('all') : type === 'noop'
}
const hasRootRule = Symbol('hasRootRule')
module.exports = (opts = {}) => {
let bubble = atruleNames(
['media', 'supports', 'layer', 'container', 'starting-style'],
opts.bubble
)
let atruleChilds = createFnAtruleChilds(bubble)
let unwrap = atruleNames(
[
'document',
'font-face',
'keyframes',
'-webkit-keyframes',
'-moz-keyframes'
],
opts.unwrap
)
let rootRuleName = (opts.rootRuleName || 'at-root').replace(/^@/, '')
let preserveEmpty = opts.preserveEmpty
return {
Once(root) {
root.walkAtRules(rootRuleName, node => {
normalizeRootRule(node)
root[hasRootRule] = true
})
},
postcssPlugin: 'postcss-nested',
RootExit(root) {
if (root[hasRootRule]) {
root.walkAtRules(rootRuleName, unwrapRootRule)
root[hasRootRule] = false
}
},
Rule(rule) {
let unwrapped = false
let after = rule
let copyDeclarations = false
let declarations = []
rule.each(child => {
if (child.type === 'rule') {
if (declarations.length) {
after = pickDeclarations(rule.selector, declarations, after)
declarations = []
}
copyDeclarations = true
unwrapped = true
child.selectors = mergeSelectors(rule, child)
after = breakOut(child, after)
} else if (child.type === 'atrule') {
if (declarations.length) {
after = pickDeclarations(rule.selector, declarations, after)
declarations = []
}
if (child.name === rootRuleName) {
unwrapped = true
atruleChilds(rule, child, true, child[rootRuleMergeSel])
after = breakOut(child, after)
} else if (bubble[child.name]) {
copyDeclarations = true
unwrapped = true
atruleChilds(rule, child, true)
after = breakOut(child, after)
} else if (unwrap[child.name]) {
copyDeclarations = true
unwrapped = true
atruleChilds(rule, child, false)
after = breakOut(child, after)
} else if (copyDeclarations) {
declarations.push(child)
}
} else if (child.type === 'decl' && copyDeclarations) {
declarations.push(child)
}
})
if (declarations.length) {
after = pickDeclarations(rule.selector, declarations, after)
}
if (unwrapped && preserveEmpty !== true) {
rule.raws.semicolon = true
if (rule.nodes.length === 0) rule.remove()
}
}
}
}
module.exports.postcss = true