{"_id":"@absolutejs/diagnostics","_rev":"3-df60f30e6c61ec1eec620a55bc773afe","name":"@absolutejs/diagnostics","dist-tags":{"latest":"0.2.1"},"versions":{"0.1.0":{"name":"@absolutejs/diagnostics","version":"0.1.0","keywords":["absolutejs","diagnostics","har","http-archive","browser","playwright","observability","support"],"author":{"name":"Alex Kahn"},"license":"BSL-1.1","_id":"@absolutejs/diagnostics@0.1.0","maintainers":[{"name":"alexkahndev","email":"alexkahn2019@gmail.com"}],"homepage":"https://github.com/absolutejs/diagnostics","bugs":{"url":"https://github.com/absolutejs/diagnostics/issues"},"dist":{"shasum":"a9ac80e5ced49ef17d3563a2cec0bb526bde3d6d","tarball":"https://registry.npmjs.org/@absolutejs/diagnostics/-/diagnostics-0.1.0.tgz","fileCount":44,"integrity":"sha512-gXNV2/dp5vPAjPMgfo6PzYpkj9TQvw7rrM5Bn6azg8zH7cJ9vPqXHQL45Gndwi7+BkPMLF86iBJwKf/5iiUkzQ==","signatures":[{"sig":"MEUCIEfJtmPiSKyWBV0xwnLAVOm9cM2D+c9otN5YGhAHC/UiAiEAwpX3RI9+JWNHOOH9RIk4Ogi4Fa5Oo75ZZ+i4RnAFnas=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":233124},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./har":{"types":"./dist/har.d.ts","import":"./dist/har.js","default":"./dist/har.js"},"./elysia":{"types":"./dist/elysia.d.ts","import":"./dist/elysia.js","default":"./dist/elysia.js"},"./redact":{"types":"./dist/redact.d.ts","import":"./dist/redact.js","default":"./dist/redact.js"},"./browser":{"types":"./dist/browser.d.ts","import":"./dist/browser.js","default":"./dist/browser.js"},"./manifest":{"types":"./dist/manifest.d.ts","import":"./dist/manifest.js","default":"./dist/manifest.js"},"./playwright":{"types":"./dist/playwright.d.ts","import":"./dist/playwright.js","default":"./dist/playwright.js"},"./manifest.json":"./dist/manifest.json"},"gitHead":"2a4e8b3b536fa2ba6532e096b343c00b38045ed8","scripts":{"test":"bun test","build":"rm -rf dist && bun build src/index.ts src/browser.ts src/har.ts src/redact.ts --outdir dist --root ./src --splitting --sourcemap --target=browser && bun build src/playwright.ts src/elysia.ts src/manifest.ts --outdir dist --root ./src --splitting --sourcemap --target=bun --external playwright --external elysia --external @absolutejs/manifest --external '@absolutejs/manifest/*' --external @sinclair/typebox --external '@sinclair/typebox/*' && tsc --project tsconfig.build.json && absolute-manifest emit","format":"prettier --write \"./**/*.{ts,json,md}\"","release":"bun run check:package && npm publish --access public","typecheck":"tsc --noEmit","check:package":"bun run format && bun run typecheck && bun run test && bun run verify-package && bun run build","verify-package":"absolute-manifest verify-package"},"_npmUser":{"name":"alexkahndev","email":"alexkahn2019@gmail.com"},"absolutejs":{"runtimePeers":{"elysia":{"range":"^2.0.0-beta.6","tested":"2.0.0-beta.6","optional":true,"buildExternals":["elysia"],"artifactImports":["elysia"]},"playwright":{"range":">=1.50.0 <2","tested":"1.62.1","optional":true,"buildExternals":["playwright"],"artifactImports":["playwright"]}},"manifestContract":2},"repository":{"url":"git+https://github.com/absolutejs/diagnostics.git","type":"git"},"_npmVersion":"10.9.2","description":"Privacy-first, on-demand browser diagnostics for AbsoluteJS: bounded network and console sessions, redacted HAR 1.2 export, Playwright/CDP capture, and an optional Elysia relay.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"@sinclair/typebox":"0.34.52","@absolutejs/manifest":"0.10.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"elysia":"2.0.0-beta.6","prettier":"3.9.6","@types/bun":"1.3.14","playwright":"1.62.1","typescript":"5.9.3","@happy-dom/global-registrator":"20.11.1"},"peerDependencies":{"elysia":"^2.0.0-beta.6","playwright":">=1.50.0 <2"},"peerDependenciesMeta":{"elysia":{"optional":true},"playwright":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/diagnostics_0.1.0_1787958071163_0.21815429301013523","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@absolutejs/diagnostics","version":"0.2.0","keywords":["absolutejs","diagnostics","har","http-archive","browser","playwright","observability","support"],"author":{"name":"Alex Kahn"},"license":"BSL-1.1","_id":"@absolutejs/diagnostics@0.2.0","maintainers":[{"name":"alexkahndev","email":"alexkahn2019@gmail.com"}],"homepage":"https://github.com/absolutejs/diagnostics","bugs":{"url":"https://github.com/absolutejs/diagnostics/issues"},"dist":{"shasum":"663ef3804c6a200e045c2641114859eb7561586e","tarball":"https://registry.npmjs.org/@absolutejs/diagnostics/-/diagnostics-0.2.0.tgz","fileCount":70,"integrity":"sha512-995PIo0agl6mdwqnb4VtEmCXq+5pSod6mzLD0M0V1SsH2k77Uh8NhwLu6FF0gmo6N+iQZ7l8RaDMvo/8OdOm4g==","signatures":[{"sig":"MEUCIHtfKzBgQsplSGNZhaTFuvabCjmMJofMKrJRgtWsmnJoAiEAy2Y48fQeaPTFp5wneMtj48Tmj4zdjIj4b8HGg55JUQk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":399076},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./ui":{"types":"./dist/ui.d.ts","import":"./dist/ui.js","default":"./dist/ui.js"},"./har":{"types":"./dist/har.d.ts","import":"./dist/har.js","default":"./dist/har.js"},"./blob":{"types":"./dist/blob.d.ts","import":"./dist/blob.js","default":"./dist/blob.js"},"./trace":{"types":"./dist/trace.d.ts","import":"./dist/trace.js","default":"./dist/trace.js"},"./elysia":{"types":"./dist/elysia.d.ts","import":"./dist/elysia.js","default":"./dist/elysia.js"},"./redact":{"types":"./dist/redact.d.ts","import":"./dist/redact.js","default":"./dist/redact.js"},"./viewer":{"types":"./dist/viewer.d.ts","import":"./dist/viewer.js","default":"./dist/viewer.js"},"./browser":{"types":"./dist/browser.d.ts","import":"./dist/browser.js","default":"./dist/browser.js"},"./support":{"types":"./dist/support.d.ts","import":"./dist/support.js","default":"./dist/support.js"},"./manifest":{"types":"./dist/manifest.d.ts","import":"./dist/manifest.js","default":"./dist/manifest.js"},"./playwright":{"types":"./dist/playwright.d.ts","import":"./dist/playwright.js","default":"./dist/playwright.js"},"./manifest.json":"./dist/manifest.json"},"gitHead":"81144a29a96daf6f901dbc1fdf90b0add670909d","scripts":{"test":"bun test","build":"rm -rf dist && bun build src/index.ts src/browser.ts src/har.ts src/redact.ts src/support.ts src/trace.ts src/ui.ts src/viewer.ts --outdir dist --root ./src --splitting --sourcemap --target=browser && bun build src/playwright.ts src/elysia.ts src/blob.ts src/manifest.ts --outdir dist --root ./src --splitting --sourcemap --target=bun --external playwright --external elysia --external @absolutejs/manifest --external '@absolutejs/manifest/*' --external @sinclair/typebox --external '@sinclair/typebox/*' && tsc --project tsconfig.build.json && absolute-manifest emit","format":"prettier --write \"./**/*.{ts,json,md}\"","release":"bun run check:package && npm publish --access public","typecheck":"tsc --noEmit","check:package":"bun run format && bun run typecheck && bun run test && bun run verify-package && bun run build","verify-package":"absolute-manifest verify-package"},"_npmUser":{"name":"alexkahndev","email":"alexkahn2019@gmail.com"},"absolutejs":{"runtimePeers":{"elysia":{"range":"^2.0.0-beta.6","tested":"2.0.0-beta.6","optional":true,"buildExternals":["elysia"],"artifactImports":["elysia"]},"playwright":{"range":">=1.50.0 <2","tested":"1.62.1","optional":true,"buildExternals":["playwright"],"artifactImports":["playwright"]}},"manifestContract":2},"repository":{"url":"git+https://github.com/absolutejs/diagnostics.git","type":"git"},"_npmVersion":"10.9.2","description":"Privacy-first AbsoluteJS Support Mode: bounded browser diagnostics, redacted HAR capture, correlated support bundles, secure lifecycle storage, native UI, and operator tooling.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"@sinclair/typebox":"0.34.52","@absolutejs/manifest":"0.10.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"elysia":"2.0.0-beta.6","prettier":"3.9.6","@types/bun":"1.3.14","playwright":"1.62.1","typescript":"5.9.3","@happy-dom/global-registrator":"20.11.1"},"peerDependencies":{"elysia":"^2.0.0-beta.6","playwright":">=1.50.0 <2"},"peerDependenciesMeta":{"elysia":{"optional":true},"playwright":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/diagnostics_0.2.0_1787960661190_0.2572748717785369","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@absolutejs/diagnostics","version":"0.2.1","description":"Privacy-first AbsoluteJS Support Mode: bounded browser diagnostics, redacted HAR capture, correlated support bundles, secure lifecycle storage, native UI, and operator tooling.","repository":{"type":"git","url":"git+https://github.com/absolutejs/diagnostics.git"},"homepage":"https://github.com/absolutejs/diagnostics","bugs":{"url":"https://github.com/absolutejs/diagnostics/issues"},"main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","type":"module","license":"BSL-1.1","author":{"name":"Alex Kahn"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./browser":{"types":"./dist/browser.d.ts","import":"./dist/browser.js","default":"./dist/browser.js"},"./har":{"types":"./dist/har.d.ts","import":"./dist/har.js","default":"./dist/har.js"},"./redact":{"types":"./dist/redact.d.ts","import":"./dist/redact.js","default":"./dist/redact.js"},"./playwright":{"types":"./dist/playwright.d.ts","import":"./dist/playwright.js","default":"./dist/playwright.js"},"./blob":{"types":"./dist/blob.d.ts","import":"./dist/blob.js","default":"./dist/blob.js"},"./support":{"types":"./dist/support.d.ts","import":"./dist/support.js","default":"./dist/support.js"},"./trace":{"types":"./dist/trace.d.ts","import":"./dist/trace.js","default":"./dist/trace.js"},"./ui":{"types":"./dist/ui.d.ts","import":"./dist/ui.js","default":"./dist/ui.js"},"./viewer":{"types":"./dist/viewer.d.ts","import":"./dist/viewer.js","default":"./dist/viewer.js"},"./elysia":{"types":"./dist/elysia.d.ts","import":"./dist/elysia.js","default":"./dist/elysia.js"},"./manifest":{"types":"./dist/manifest.d.ts","import":"./dist/manifest.js","default":"./dist/manifest.js"},"./manifest.json":"./dist/manifest.json"},"publishConfig":{"access":"public"},"scripts":{"build":"rm -rf dist && bun build src/index.ts src/browser.ts src/har.ts src/redact.ts src/support.ts src/trace.ts src/ui.ts src/viewer.ts --outdir dist --root ./src --splitting --sourcemap --target=browser && bun build src/playwright.ts src/elysia.ts src/blob.ts src/manifest.ts --outdir dist --root ./src --splitting --sourcemap --target=bun --external playwright --external elysia --external @absolutejs/manifest --external '@absolutejs/manifest/*' --external @sinclair/typebox --external '@sinclair/typebox/*' && tsc --project tsconfig.build.json && absolute-manifest emit","test":"bun test","typecheck":"tsc --noEmit","format":"prettier --write \"./**/*.{ts,json,md}\"","verify-package":"absolute-manifest verify-package","check:package":"bun run format && bun run typecheck && bun run test && bun run verify-package && bun run build","release":"bun run check:package && npm publish --access public"},"keywords":["absolutejs","diagnostics","har","http-archive","browser","playwright","observability","support"],"absolutejs":{"manifestContract":2,"runtimePeers":{"elysia":{"artifactImports":["elysia"],"buildExternals":["elysia"],"optional":true,"range":"^2.0.0-beta.6","tested":"2.0.0-beta.6"},"playwright":{"artifactImports":["playwright"],"buildExternals":["playwright"],"optional":true,"range":">=1.50.0 <2","tested":"1.62.1"}}},"dependencies":{"@absolutejs/manifest":"0.10.0","@sinclair/typebox":"0.34.52"},"peerDependencies":{"elysia":"^2.0.0-beta.6","playwright":">=1.50.0 <2"},"peerDependenciesMeta":{"elysia":{"optional":true},"playwright":{"optional":true}},"devDependencies":{"@happy-dom/global-registrator":"20.11.1","@types/bun":"1.3.14","elysia":"2.0.0-beta.6","playwright":"1.62.1","prettier":"3.9.6","typescript":"5.9.3"},"_id":"@absolutejs/diagnostics@0.2.1","gitHead":"69bac32eff08a032cdd035e18e6313502e9ac61c","_nodeVersion":"22.14.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-ltrTKpiwmz/jF+uEkTz0OcIkz/vqXzUfcPutVNAC4BREYNjt41dhUJerl1O0tjTx1cBHNOKA2/JvV8ytI6ufRw==","shasum":"47267fb29f4f884668b16248e3a0df26a3590b08","tarball":"https://registry.npmjs.org/@absolutejs/diagnostics/-/diagnostics-0.2.1.tgz","fileCount":70,"unpackedSize":402813,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCCh1tnYFXnxwb/c4LPC2RTWSpYaLaX8Ve0fUVPbS1l4QIgPrnOtHMy/tEMt8+l81DvZa848xxy02w3U004/bGIXNs="}]},"_npmUser":{"name":"alexkahndev","email":"alexkahn2019@gmail.com"},"directories":{},"maintainers":[{"name":"alexkahndev","email":"alexkahn2019@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/diagnostics_0.2.1_1788213285731_0.6621713081598144"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-28T23:01:11.012Z","modified":"2026-08-31T21:54:46.184Z","0.1.0":"2026-08-28T23:01:11.298Z","0.2.0":"2026-08-28T23:44:21.331Z","0.2.1":"2026-08-31T21:54:45.984Z"},"bugs":{"url":"https://github.com/absolutejs/diagnostics/issues"},"author":{"name":"Alex Kahn"},"license":"BSL-1.1","homepage":"https://github.com/absolutejs/diagnostics","keywords":["absolutejs","diagnostics","har","http-archive","browser","playwright","observability","support"],"repository":{"type":"git","url":"git+https://github.com/absolutejs/diagnostics.git"},"description":"Privacy-first AbsoluteJS Support Mode: bounded browser diagnostics, redacted HAR capture, correlated support bundles, secure lifecycle storage, native UI, and operator tooling.","maintainers":[{"name":"alexkahndev","email":"alexkahn2019@gmail.com"}],"readme":"# @absolutejs/diagnostics\n\nPrivacy-first, on-demand browser diagnostics for AbsoluteJS applications.\n\nIt records a bounded network and console timeline only after an explicit\nsupport/operator action, exports a redacted HAR 1.2 document, and provides a\nPlaywright/CDP path when a vendor needs a complete DevTools capture. Request and\nresponse bodies are off by default. Redaction happens before in-page entries\nare retained and again at export or server ingest.\n\n## Why this is separate from Beacon, Errors, and Replay\n\n- `@absolutejs/beacon` stays tiny and always-on. It records redacted request\n  breadcrumbs and actionable failures, not complete protocol archives.\n- `@absolutejs/errors` groups and persists issues. It should link a diagnostic\n  id, not own large support artifacts.\n- `@absolutejs/replay` records privacy-masked DOM state. A DOM recording is not\n  an HTTP archive.\n- `@absolutejs/observability` can compose all four capabilities and correlate\n  the diagnostic id with Beacon session, Replay, Errors, and traces.\n\n## Install\n\n```sh\nbun add @absolutejs/diagnostics\n```\n\nInstall only the optional capability you use:\n\n```sh\nbun add playwright # complete Chromium HAR capture\nbun add elysia     # upload/retrieval relay\n```\n\n## In-page diagnostics\n\nCreating a controller does not start recording:\n\n```ts\nimport { createBrowserDiagnostics } from \"@absolutejs/diagnostics/browser\";\n\nconst diagnostics = createBrowserDiagnostics({\n  project: \"web\",\n  release: APP_RELEASE,\n  replayId: () => replay.getReplayId(),\n  traceId: () => currentTraceId(),\n  ignoredUrlSubstrings: [\"/api/diagnostics\"],\n});\n\n// Call only after an explicit support/operator action.\nconst session = diagnostics.start({ reason: \"payment provider reproduction\" });\n\n// Reproduce the problem, then stop and download a redacted HAR.\nconst archive = await session.stop();\nsession.downloadHar(\"payment-provider.redacted.har\");\n```\n\nThe recorder wraps the current `fetch`, XHR, and console functions, so it can\ncoexist with Beacon. It also consumes Resource Timing entries for static and\nthird-party resources visible to the page. `stop()` restores only wrappers it\nstill owns.\n\nThe in-page manifest always declares `completeness: \"in-page-partial\"` and\n`cacheDisabled: false`. Application JavaScript cannot truthfully claim a full\nHAR.\n\n### Body capture is deliberately difficult to enable\n\nThere is no `captureBodies: true` switch. Supply a per-request allow function\nand select request and/or response explicitly:\n\n```ts\nconst diagnostics = createBrowserDiagnostics({\n  project: \"web\",\n  bodyCapture: {\n    request: true,\n    response: true,\n    maxBodyBytes: 16_384,\n    allow: ({ sameOrigin, url }) =>\n      sameOrigin && new URL(url).pathname.startsWith(\"/api/support-safe/\"),\n  },\n});\n```\n\nStrings and URL-encoded/JSON bodies are redacted immediately. Blob, stream,\nmultipart, and other binary request bodies are never inspected by the in-page\nrecorder.\n\n## Complete HAR capture with Playwright/CDP\n\nUse the Playwright entry point when a payment processor, identity provider, or\nother vendor requests a real HAR with Preserve Log and Disable Cache:\n\n```ts\nimport { launchPlaywrightHarCapture } from \"@absolutejs/diagnostics/playwright\";\n\nconst capture = await launchPlaywrightHarCapture({\n  cacheDisabled: true,\n  channel: \"chrome\",\n  consoleOutputPath: \"./support/browser-console.redacted.log\",\n  metadataOutputPath: \"./support/capture-metadata.json\",\n  outputPath: \"./support/network.redacted.har\",\n  url: \"https://example.com/checkout\",\n  userDataDir: \"/path/to/a/dedicated-support-profile\",\n  harRedaction: {\n    // Bodies are removed unless a host explicitly retains them.\n    retainResponseBody: ({ url }) =>\n      new URL(url).hostname === \"provider.example.com\",\n  },\n});\n\ncapture.mark(\"payment-button-clicked\");\n\n// Reproduce using capture.page or the visible browser, then close gracefully.\nconst result = await capture.stop();\nif (!result.audit.safeToShare) throw new Error(\"Do not send the export\");\n```\n\nPlaywright first records to a uniquely named raw temporary file. `stop()`\ncloses the context so Playwright flushes the HAR, creates the redacted output,\nruns the sharing audit, and removes the raw temporary file in a `finally`\nblock. Call `stop()` rather than closing the browser window at the OS level.\n\nThe metadata declares `completeness: \"devtools-complete\"`, whether cache was\ndisabled, exact UTC start/end times, and any operator markers.\n\n## Support Mode controller and native UI\n\n`createSupportModeController` provides a consent-shaped state machine:\n`idle → recording → reviewing → sending → sent`. Creating it never starts a\nrecording. A host can use the headless controller directly or connect the\nframework-neutral native element.\n\n```ts\nimport { createBrowserDiagnostics } from \"@absolutejs/diagnostics/browser\";\nimport {\n  connectSupportReportElements,\n  createSupportModeController,\n} from \"@absolutejs/diagnostics/ui\";\n\nconst support = createSupportModeController({\n  diagnostics: createBrowserDiagnostics({ project: \"web\" }),\n  submit: async (bundle) => {\n    const response = await fetch(\"/api/diagnostics\", {\n      body: JSON.stringify({ bundle }),\n      headers: { \"content-type\": \"application/json\" },\n      method: \"POST\",\n    });\n    if (!response.ok) throw new Error(`Upload failed: ${response.status}`);\n  },\n});\n\nconnectSupportReportElements(support);\n```\n\n```html\n<absolute-support-report></absolute-support-report>\n```\n\nThe element explains what will be recorded, requires an explicit Start click,\nshows a persistent recording state, supports named markers, and exposes the\nprivacy-audit result before send.\n\n## Correlated support bundles\n\n`createSupportBundle()` produces one audited JSON artifact containing the\nredacted in-page archive and HAR plus marker, replay, release, environment,\nissue-fingerprint, and W3C trace correlations. It does not embed Replay data,\nserver logs, or issue records; those remain in their purpose-built stores and\nare joined by id.\n\nRequest-level trace correlation recognizes valid `traceparent` headers and\nrecords exposed `Server-Timing` metrics. Diagnostic-id propagation is\nsame-origin, opt-in, and off by default because mutating requests can affect\ncaches or signed requests.\n\nThe optional Elysia correlation plugin makes the bounded correlation available\nto handlers and appends server timing:\n\n```ts\nimport { diagnosticCorrelationPlugin } from \"@absolutejs/diagnostics/elysia\";\n\napp.use(\n  diagnosticCorrelationPlugin({\n    onRequest: ({ diagnosticId, trace }) =>\n      logs.info(\"support request\", { diagnosticId, traceId: trace?.traceId }),\n  }),\n);\n```\n\n## Redaction and audit\n\n```ts\nimport {\n  auditDiagnosticText,\n  redactHarText,\n} from \"@absolutejs/diagnostics/redact\";\n\nconst { text, audit } = redactHarText(rawHar, {\n  // Every query value is removed by default. Preserve only known-safe values.\n  preserveQueryValues: [\"plan\"],\n});\n\nif (!audit.safeToShare) {\n  console.error(audit.findings.map((finding) => finding.code));\n}\n```\n\nDefaults remove or mask:\n\n- `Authorization`, `Cookie`, `Set-Cookie`, proxy credentials, and API-key\n  headers\n- every URL query value and every fragment\n- bearer values and JWT-shaped strings\n- tokens, sessions, credentials, passwords, signatures, wallet payloads,\n  payment fields, card fields, and common personal-contact fields\n- request and response bodies in DevTools HAR captures\n- body content beyond the configured byte limit\n\nThe sharing audit reports finding codes and locations, never the suspected\nsecret value.\n\n## Elysia relay\n\nThe relay defaults closed. A host must provide authorization and storage:\n\n```ts\nimport { diagnosticsPlugin } from \"@absolutejs/diagnostics/elysia\";\n\napp.use(\n  diagnosticsPlugin({\n    authorize: (request) => supportSessionAuthorizes(request),\n    store: {\n      put: (capture) =>\n        privateBlobStore.put(\n          `diagnostics/${capture.id}.json`,\n          JSON.stringify(capture),\n        ),\n      get: (id) => loadPrivateCapture(id),\n    },\n  }),\n);\n```\n\nThe relay enforces a byte limit, validates the archive shape, redacts again,\naudits the serialized HAR, and only then calls the store. Wire storage to\n`@absolutejs/blob` or another private store with an explicit retention policy.\n\n### Secure lifecycle storage\n\n`createDiagnosticBlobCaptureStore()` adapts any `@absolutejs/blob` store. The\nrelay can enforce retention, atomic download limits, short-lived HMAC-signed\ndownload URLs, explicit deletion, and mandatory lifecycle audit hooks.\n\n```ts\nimport { createDiagnosticBlobCaptureStore } from \"@absolutejs/diagnostics/blob\";\n\nconst store = createDiagnosticBlobCaptureStore({\n  blob,\n  prefix: \"support\",\n  singleWriter: true, // only when one process owns this prefix\n});\napp.use(\n  diagnosticsPlugin({\n    authorize: requireSupportAccess,\n    downloadSigningKey: process.env.DIAGNOSTIC_SIGNING_KEY!,\n    maxDownloads: 1,\n    retentionMs: 7 * 24 * 60 * 60 * 1_000,\n    store,\n    onLifecycleEvent: (event) => audit.append(event),\n  }),\n);\n```\n\n`maxDownloads` is accepted only when the store implements atomic `consume()`;\nthe plugin refuses unsafe configuration rather than pretending a normal `get()`\nis sufficient. The Blob adapter exposes serialized `consume()` only with\n`singleWriter: true`. Clustered applications must supply a transactional store\nimplementation instead of relying on an object-store read/modify/write race.\n\n## Operator viewer and comparison\n\n`@absolutejs/diagnostics/viewer` builds a chronological network, console, and\nmarker timeline. `compareSupportBundles(left, right)` reports request-set,\nstatus, failure-count, timing, console, and marker differences—useful for\ncomparing a successful card attempt with a failed wallet attempt.\n\n## Browser limitations\n\nA normal webpage cannot observe:\n\n- protected cookies and browser-owned authorization data\n- most cross-origin response headers or bodies\n- native Apple Pay or browser-wallet network traffic\n- browser cache, connection, and service-worker protocol details with DevTools\n  fidelity\n- requests made by another device during a QR handoff\n\nTherefore an in-page export is useful but partial. Use Playwright/CDP for a\ncomplete desktop-browser HAR. Capturing native iPhone Safari with Web Inspector\nstill requires Apple's supported Mac-connected workflow; this package cannot\nbypass platform security boundaries.\n\n## API\n\n```ts\ncreateBrowserDiagnostics(options) => BrowserDiagnostics\ndiagnosticArchiveToHar(archive, redaction?) => HarArchive\nserializeDiagnosticHar(archive, redaction?) => string\ndownloadDiagnosticHar(archive, filename?) => void\nredactDiagnosticArchive(archive, options?) => DiagnosticArchive\nredactHarText(rawHar, options?) => { text, audit }\nauditDiagnosticText(serialized) => { safeToShare, findings }\nlaunchPlaywrightHarCapture(options) => Promise<PlaywrightHarCaptureSession>\ndiagnosticsPlugin(options) => Elysia\n```\n\n## License\n\nBSL-1.1 with a named carveout against competing hosted diagnostics,\nerror-tracking, session-replay, and observability services. Change date:\nAugust 28, 2030, then Apache 2.0.\n","readmeFilename":"README.md"}