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>
117 lines
3.4 KiB
JavaScript
117 lines
3.4 KiB
JavaScript
export { M as ModuleMocker, c as createCompilerHints } from './chunk-mocker.js';
|
|
import { M as MockerRegistry } from './chunk-registry.js';
|
|
import { c as cleanUrl } from './chunk-utils.js';
|
|
export { M as ModuleMockerServerInterceptor } from './chunk-interceptor-native.js';
|
|
import './index.js';
|
|
import './chunk-pathe.ff20891b.js';
|
|
|
|
class ModuleMockerMSWInterceptor {
|
|
constructor(options = {}) {
|
|
this.options = options;
|
|
if (!options.globalThisAccessor) {
|
|
options.globalThisAccessor = '"__vitest_mocker__"';
|
|
}
|
|
}
|
|
mocks = new MockerRegistry();
|
|
startPromise;
|
|
worker;
|
|
async register(module) {
|
|
await this.init();
|
|
this.mocks.add(module);
|
|
}
|
|
async delete(url) {
|
|
await this.init();
|
|
this.mocks.delete(url);
|
|
}
|
|
invalidate() {
|
|
this.mocks.clear();
|
|
}
|
|
async resolveManualMock(mock) {
|
|
const exports = Object.keys(await mock.resolve());
|
|
const module = `const module = globalThis[${this.options.globalThisAccessor}].getFactoryModule("${mock.url}");`;
|
|
const keys = exports.map((name) => {
|
|
if (name === "default") {
|
|
return `export default module["default"];`;
|
|
}
|
|
return `export const ${name} = module["${name}"];`;
|
|
}).join("\n");
|
|
const text = `${module}
|
|
${keys}`;
|
|
return new Response(text, {
|
|
headers: {
|
|
"Content-Type": "application/javascript"
|
|
}
|
|
});
|
|
}
|
|
async init() {
|
|
if (this.worker) {
|
|
return this.worker;
|
|
}
|
|
if (this.startPromise) {
|
|
return this.startPromise;
|
|
}
|
|
const worker = this.options.mswWorker;
|
|
this.startPromise = Promise.all([
|
|
worker ? {
|
|
setupWorker(handler) {
|
|
worker.use(handler);
|
|
return worker;
|
|
}
|
|
} : import('msw/browser'),
|
|
import('msw/core/http')
|
|
]).then(([{ setupWorker }, { http }]) => {
|
|
const worker2 = setupWorker(
|
|
http.get(/.+/, async ({ request }) => {
|
|
const path = cleanQuery(request.url.slice(location.origin.length));
|
|
if (!this.mocks.has(path)) {
|
|
return passthrough();
|
|
}
|
|
const mock = this.mocks.get(path);
|
|
switch (mock.type) {
|
|
case "manual":
|
|
return this.resolveManualMock(mock);
|
|
case "automock":
|
|
case "autospy":
|
|
return Response.redirect(injectQuery(path, `mock=${mock.type}`));
|
|
case "redirect":
|
|
return Response.redirect(mock.redirect);
|
|
default:
|
|
throw new Error(`Unknown mock type: ${mock.type}`);
|
|
}
|
|
})
|
|
);
|
|
return worker2.start(this.options.mswOptions).then(() => worker2);
|
|
}).finally(() => {
|
|
this.worker = worker;
|
|
this.startPromise = void 0;
|
|
});
|
|
return await this.startPromise;
|
|
}
|
|
}
|
|
const timestampRegexp = /(\?|&)t=\d{13}/;
|
|
const versionRegexp = /(\?|&)v=\w{8}/;
|
|
function cleanQuery(url) {
|
|
return url.replace(timestampRegexp, "").replace(versionRegexp, "");
|
|
}
|
|
function passthrough() {
|
|
return new Response(null, {
|
|
status: 302,
|
|
statusText: "Passthrough",
|
|
headers: {
|
|
"x-msw-intention": "passthrough"
|
|
}
|
|
});
|
|
}
|
|
const replacePercentageRE = /%/g;
|
|
function injectQuery(url, queryToInject) {
|
|
const resolvedUrl = new URL(
|
|
url.replace(replacePercentageRE, "%25"),
|
|
location.href
|
|
);
|
|
const { search, hash } = resolvedUrl;
|
|
const pathname = cleanUrl(url);
|
|
return `${pathname}?${queryToInject}${search ? `&${search.slice(1)}` : ""}${hash ?? ""}`;
|
|
}
|
|
|
|
export { ModuleMockerMSWInterceptor };
|