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>
160 lines
3.4 KiB
JavaScript
160 lines
3.4 KiB
JavaScript
// Manually “tree shaken” from:
|
||
// <https://github.com/nodejs/node/blob/7c3dce0/lib/internal/modules/esm/get_format.js>
|
||
// Last checked on: Apr 29, 2024.
|
||
|
||
import {fileURLToPath} from 'node:url'
|
||
import {getPackageType} from './package-json-reader.js'
|
||
import {codes} from './errors.js'
|
||
|
||
const {ERR_UNKNOWN_FILE_EXTENSION} = codes
|
||
|
||
const hasOwnProperty = {}.hasOwnProperty
|
||
|
||
/** @type {Record<string, string>} */
|
||
const extensionFormatMap = {
|
||
// @ts-expect-error: hush.
|
||
__proto__: null,
|
||
'.cjs': 'commonjs',
|
||
'.js': 'module',
|
||
'.json': 'json',
|
||
'.mjs': 'module'
|
||
}
|
||
|
||
/**
|
||
* @param {string | null} mime
|
||
* @returns {string | null}
|
||
*/
|
||
function mimeToFormat(mime) {
|
||
if (
|
||
mime &&
|
||
/\s*(text|application)\/javascript\s*(;\s*charset=utf-?8\s*)?/i.test(mime)
|
||
)
|
||
return 'module'
|
||
if (mime === 'application/json') return 'json'
|
||
return null
|
||
}
|
||
|
||
/**
|
||
* @callback ProtocolHandler
|
||
* @param {URL} parsed
|
||
* @param {{parentURL: string, source?: Buffer}} context
|
||
* @param {boolean} ignoreErrors
|
||
* @returns {string | null | void}
|
||
*/
|
||
|
||
/**
|
||
* @type {Record<string, ProtocolHandler>}
|
||
*/
|
||
const protocolHandlers = {
|
||
// @ts-expect-error: hush.
|
||
__proto__: null,
|
||
'data:': getDataProtocolModuleFormat,
|
||
'file:': getFileProtocolModuleFormat,
|
||
'http:': getHttpProtocolModuleFormat,
|
||
'https:': getHttpProtocolModuleFormat,
|
||
'node:'() {
|
||
return 'builtin'
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @param {URL} parsed
|
||
*/
|
||
function getDataProtocolModuleFormat(parsed) {
|
||
const {1: mime} = /^([^/]+\/[^;,]+)[^,]*?(;base64)?,/.exec(
|
||
parsed.pathname
|
||
) || [null, null, null]
|
||
return mimeToFormat(mime)
|
||
}
|
||
|
||
/**
|
||
* Returns the file extension from a URL.
|
||
*
|
||
* Should give similar result to
|
||
* `require('node:path').extname(require('node:url').fileURLToPath(url))`
|
||
* when used with a `file:` URL.
|
||
*
|
||
* @param {URL} url
|
||
* @returns {string}
|
||
*/
|
||
function extname(url) {
|
||
const pathname = url.pathname
|
||
let index = pathname.length
|
||
|
||
while (index--) {
|
||
const code = pathname.codePointAt(index)
|
||
|
||
if (code === 47 /* `/` */) {
|
||
return ''
|
||
}
|
||
|
||
if (code === 46 /* `.` */) {
|
||
return pathname.codePointAt(index - 1) === 47 /* `/` */
|
||
? ''
|
||
: pathname.slice(index)
|
||
}
|
||
}
|
||
|
||
return ''
|
||
}
|
||
|
||
/**
|
||
* @type {ProtocolHandler}
|
||
*/
|
||
function getFileProtocolModuleFormat(url, _context, ignoreErrors) {
|
||
const value = extname(url)
|
||
|
||
if (value === '.js') {
|
||
const packageType = getPackageType(url)
|
||
|
||
if (packageType !== 'none') {
|
||
return packageType
|
||
}
|
||
|
||
return 'commonjs'
|
||
}
|
||
|
||
if (value === '') {
|
||
const packageType = getPackageType(url)
|
||
|
||
// Legacy behavior
|
||
if (packageType === 'none' || packageType === 'commonjs') {
|
||
return 'commonjs'
|
||
}
|
||
|
||
// Note: we don’t implement WASM, so we don’t need
|
||
// `getFormatOfExtensionlessFile` from `formats`.
|
||
return 'module'
|
||
}
|
||
|
||
const format = extensionFormatMap[value]
|
||
if (format) return format
|
||
|
||
// Explicit undefined return indicates load hook should rerun format check
|
||
if (ignoreErrors) {
|
||
return undefined
|
||
}
|
||
|
||
const filepath = fileURLToPath(url)
|
||
throw new ERR_UNKNOWN_FILE_EXTENSION(value, filepath)
|
||
}
|
||
|
||
function getHttpProtocolModuleFormat() {
|
||
// To do: HTTPS imports.
|
||
}
|
||
|
||
/**
|
||
* @param {URL} url
|
||
* @param {{parentURL: string}} context
|
||
* @returns {string | null}
|
||
*/
|
||
export function defaultGetFormatWithoutErrors(url, context) {
|
||
const protocol = url.protocol
|
||
|
||
if (!hasOwnProperty.call(protocolHandlers, protocol)) {
|
||
return null
|
||
}
|
||
|
||
return protocolHandlers[protocol](url, context, true) || null
|
||
}
|