feat: publish HoloLake model-native living system source
This commit is contained in:
parent
6ad10edde1
commit
c395dd3a99
2467 changed files with 615073 additions and 0 deletions
157
product-source/hololake-platform/src/lib/feedbackDiagnostics.ts
Normal file
157
product-source/hololake-platform/src/lib/feedbackDiagnostics.ts
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
import { isTauri } from '../mock-tauri'
|
||||
import { TOKEN_REDACTION, isSensitiveDiagnosticKey, sanitizeDiagnosticText } from './sensitiveTextRedaction'
|
||||
|
||||
type DiagnosticLevel = 'error' | 'warn'
|
||||
|
||||
interface DiagnosticEntry {
|
||||
level: DiagnosticLevel
|
||||
message: string
|
||||
}
|
||||
|
||||
interface DiagnosticBundleContext {
|
||||
buildNumber?: string
|
||||
releaseChannel?: string | null
|
||||
}
|
||||
|
||||
const MAX_DIAGNOSTICS = 8
|
||||
|
||||
let recentDiagnostics: DiagnosticEntry[] = []
|
||||
let stopCapture: (() => void) | null = null
|
||||
|
||||
function truncate(input: string, maxLength = 240): string {
|
||||
if (input.length <= maxLength) return input
|
||||
return `${input.slice(0, maxLength - 1)}…`
|
||||
}
|
||||
|
||||
function sanitizeText(input: string): string {
|
||||
return truncate(sanitizeDiagnosticText({ text: input }))
|
||||
}
|
||||
|
||||
function safeSerialize(value: unknown): string {
|
||||
if (value instanceof Error) {
|
||||
return `${value.name}: ${value.message}`
|
||||
}
|
||||
|
||||
if (typeof value === 'string') {
|
||||
return value
|
||||
}
|
||||
|
||||
if (typeof value === 'number' || typeof value === 'boolean') {
|
||||
return String(value)
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
return String(value)
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.stringify(value, (key, nestedValue) => {
|
||||
if (typeof nestedValue === 'string') {
|
||||
return isSensitiveDiagnosticKey({ text: key }) ? TOKEN_REDACTION : truncate(nestedValue, 120)
|
||||
}
|
||||
return nestedValue
|
||||
})
|
||||
} catch {
|
||||
return Object.prototype.toString.call(value)
|
||||
}
|
||||
}
|
||||
|
||||
function recordDiagnostic(level: DiagnosticLevel, values: unknown[]): void {
|
||||
const message = sanitizeText(values.map((value) => safeSerialize(value)).join(' '))
|
||||
if (!message) return
|
||||
|
||||
recentDiagnostics = [
|
||||
...recentDiagnostics.slice(-(MAX_DIAGNOSTICS - 1)),
|
||||
{ level, message },
|
||||
]
|
||||
}
|
||||
|
||||
function resolveRuntime(): string {
|
||||
if (typeof window === 'undefined') return 'unknown'
|
||||
return isTauri() ? 'tauri' : 'browser'
|
||||
}
|
||||
|
||||
function resolvePlatform(): string {
|
||||
if (typeof navigator === 'undefined') return 'unknown'
|
||||
return sanitizeText(navigator.platform || 'unknown')
|
||||
}
|
||||
|
||||
function resolveUserAgent(): string {
|
||||
if (typeof navigator === 'undefined') return 'unknown'
|
||||
return sanitizeText(navigator.userAgent || 'unknown')
|
||||
}
|
||||
|
||||
export function startFeedbackDiagnosticsCapture(): () => void {
|
||||
if (stopCapture || typeof window === 'undefined') {
|
||||
return stopCapture ?? (() => {})
|
||||
}
|
||||
|
||||
const originalError = console.error
|
||||
const originalWarn = console.warn
|
||||
|
||||
console.error = (...args: Parameters<typeof console.error>) => {
|
||||
recordDiagnostic('error', args)
|
||||
originalError(...args)
|
||||
}
|
||||
|
||||
console.warn = (...args: Parameters<typeof console.warn>) => {
|
||||
recordDiagnostic('warn', args)
|
||||
originalWarn(...args)
|
||||
}
|
||||
|
||||
const handleError = (event: ErrorEvent) => {
|
||||
recordDiagnostic('error', [event.error ?? event.message ?? 'Unhandled window error'])
|
||||
}
|
||||
|
||||
const handleUnhandledRejection = (event: PromiseRejectionEvent) => {
|
||||
recordDiagnostic('error', ['Unhandled rejection:', event.reason])
|
||||
}
|
||||
|
||||
window.addEventListener('error', handleError)
|
||||
window.addEventListener('unhandledrejection', handleUnhandledRejection)
|
||||
|
||||
stopCapture = () => {
|
||||
console.error = originalError
|
||||
console.warn = originalWarn
|
||||
window.removeEventListener('error', handleError)
|
||||
window.removeEventListener('unhandledrejection', handleUnhandledRejection)
|
||||
stopCapture = null
|
||||
}
|
||||
|
||||
return stopCapture
|
||||
}
|
||||
|
||||
export function buildSanitizedDiagnosticBundle({
|
||||
buildNumber,
|
||||
releaseChannel,
|
||||
}: DiagnosticBundleContext): string {
|
||||
const lines = [
|
||||
'HoloLake Era sanitized diagnostics',
|
||||
`Generated: ${new Date().toISOString()}`,
|
||||
`Build: ${buildNumber ?? 'unknown'}`,
|
||||
`Release channel: ${releaseChannel ?? 'stable'}`,
|
||||
`Runtime: ${resolveRuntime()}`,
|
||||
`Platform: ${resolvePlatform()}`,
|
||||
`User agent: ${resolveUserAgent()}`,
|
||||
'',
|
||||
'Recent diagnostics:',
|
||||
]
|
||||
|
||||
if (recentDiagnostics.length === 0) {
|
||||
lines.push('No safe recent diagnostics were available.')
|
||||
} else {
|
||||
for (const entry of recentDiagnostics) {
|
||||
lines.push(`- [${entry.level}] ${entry.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
lines.push('')
|
||||
lines.push('Notes: paths and token-like strings are redacted. This bundle is sanitized and optional.')
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function __resetFeedbackDiagnosticsForTest(): void {
|
||||
stopCapture?.()
|
||||
recentDiagnostics = []
|
||||
}
|
||||
Loading…
Reference in a new issue