{"_id":"@aegis-fluxion/browser-client","name":"@aegis-fluxion/browser-client","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@aegis-fluxion/browser-client","version":"0.1.0","description":"Browser-native SecureClient for aegis-fluxion using Web Crypto and WebSocket APIs.","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs","default":"./dist/index.js"}},"publishConfig":{"access":"public"},"sideEffects":false,"scripts":{"build":"tsup","dev":"tsup --watch","typecheck":"tsc --noEmit","test":"vitest run --passWithNoTests","test:watch":"vitest --passWithNoTests","clean":"node -e \"import('node:fs').then(({ rmSync }) => rmSync('dist', { recursive: true, force: true }))\"","prepublishOnly":"npm run clean && npm run build && npm run typecheck && npm run test"},"devDependencies":{"tsup":"^8.3.5","typescript":"^5.8.3","vitest":"^3.2.4"},"engines":{"node":">=18.18.0"},"keywords":["browser","websocket","webcrypto","e2e-encryption","aes-gcm","ecdh","react"],"license":"MIT","gitHead":"f25e895b1d9fbfdca7536bfa16c11876a2743769","_id":"@aegis-fluxion/browser-client@0.1.0","_nodeVersion":"24.13.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-/Uk6puDo1GnXgBJTxBg0OPHy+m/MNiHwI7hrIsBRLEMae46e7KoV10r7gBxn/BHvdyf7qXHtP3SgjBwgHjUHyQ==","shasum":"5b2fa266d1842e2599dc4b211a93b538264d91b8","tarball":"https://registry.npmjs.org/@aegis-fluxion/browser-client/-/browser-client-0.1.0.tgz","fileCount":8,"unpackedSize":365979,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIAtlgxSCLYsX7n6mLasLZikuXf9BfgSGkIg3kIYukD5dAiEA4hnb4Ucc7H8F5FzRrJXgiuO9l0XYDJN32NMsZgXNvHM="}]},"_npmUser":{"name":"donboz","email":"muratbatak052005@gmail.com"},"directories":{},"maintainers":[{"name":"donboz","email":"muratbatak052005@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/browser-client_0.1.0_1776330438457_0.4844527393933715"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-16T09:07:18.339Z","0.1.0":"2026-04-16T09:07:18.586Z","modified":"2026-04-16T09:07:18.862Z"},"maintainers":[{"name":"donboz","email":"muratbatak052005@gmail.com"}],"description":"Browser-native SecureClient for aegis-fluxion using Web Crypto and WebSocket APIs.","keywords":["browser","websocket","webcrypto","e2e-encryption","aes-gcm","ecdh","react"],"license":"MIT","readme":"# @aegis-fluxion/browser-client\r\n\r\nBrowser-native secure WebSocket client for the `aegis-fluxion` ecosystem.\r\n\r\nVersion: **0.1.0**\r\n\r\n---\r\n\r\n## Why this package\r\n\r\n`@aegis-fluxion/browser-client` is a lightweight browser SDK that mirrors the\r\ncore `SecureClient` messaging model without Node.js built-ins.\r\n\r\nIt uses only:\r\n\r\n- Browser **Web Crypto API** (`crypto.subtle`) for ECDH + AES-256-GCM\r\n- Browser-native **WebSocket** for transport\r\n\r\nNo `node:crypto`, no `ws`, no Buffer dependency at runtime.\r\n\r\n---\r\n\r\n## Install\r\n\r\n```bash\r\nnpm install @aegis-fluxion/browser-client\r\n```\r\n\r\n---\r\n\r\n## Features\r\n\r\n- Ephemeral ECDH handshake (`P-256`) against `SecureServer`\r\n- AES-256-GCM encrypted event envelopes\r\n- Promise/callback ACK flow (`emit(..., { timeoutMs })`)\r\n- Binary payload support (`Uint8Array`, `ArrayBuffer`, `Blob`)\r\n- Encrypted chunked streaming:\r\n  - `emitStream(...)`\r\n  - `onStream(...)`\r\n- Optional reconnect backoff\r\n\r\n---\r\n\r\n## Basic browser usage\r\n\r\n```ts\r\nimport { BrowserSecureClient } from \"@aegis-fluxion/browser-client\";\r\n\r\nconst client = new BrowserSecureClient(\"wss://api.example.com/socket\", {\r\n  autoConnect: true,\r\n  reconnect: {\r\n    enabled: true,\r\n    initialDelayMs: 250,\r\n    maxDelayMs: 10_000,\r\n    factor: 2,\r\n    jitterRatio: 0.2,\r\n    maxAttempts: null\r\n  }\r\n});\r\n\r\nclient.on(\"ready\", async () => {\r\n  const ack = await client.emit(\r\n    \"notes:create\",\r\n    { title: \"From browser\" },\r\n    { timeoutMs: 1500 }\r\n  );\r\n\r\n  console.log(\"ACK\", ack);\r\n});\r\n\r\nclient.on(\"notes:created\", (payload) => {\r\n  console.log(\"push event\", payload);\r\n});\r\n```\r\n\r\n---\r\n\r\n## React example\r\n\r\n```tsx\r\nimport { useEffect, useMemo, useState } from \"react\";\r\nimport { BrowserSecureClient } from \"@aegis-fluxion/browser-client\";\r\n\r\nexport function SecureNotesWidget() {\r\n  const [status, setStatus] = useState(\"connecting\");\r\n  const [notes, setNotes] = useState<string[]>([]);\r\n\r\n  const client = useMemo(() => {\r\n    return new BrowserSecureClient(\"wss://api.example.com/socket\", {\r\n      autoConnect: false,\r\n      reconnect: true\r\n    });\r\n  }, []);\r\n\r\n  useEffect(() => {\r\n    const handleReady = () => setStatus(\"ready\");\r\n    const handleDisconnect = () => setStatus(\"disconnected\");\r\n    const handleNewNote = (payload: unknown) => {\r\n      const data = payload as { title?: string };\r\n      if (typeof data.title === \"string\") {\r\n        setNotes((prev) => [data.title, ...prev]);\r\n      }\r\n    };\r\n\r\n    client.on(\"ready\", handleReady);\r\n    client.on(\"disconnect\", handleDisconnect);\r\n    client.on(\"notes:created\", handleNewNote);\r\n\r\n    client.connect();\r\n\r\n    return () => {\r\n      client.off(\"ready\", handleReady);\r\n      client.off(\"disconnect\", handleDisconnect);\r\n      client.off(\"notes:created\", handleNewNote);\r\n      client.disconnect();\r\n    };\r\n  }, [client]);\r\n\r\n  const createNote = async () => {\r\n    await client.emit(\"notes:create\", { title: \"React note\" }, { timeoutMs: 1500 });\r\n  };\r\n\r\n  return (\r\n    <section>\r\n      <p>Secure socket status: {status}</p>\r\n      <button onClick={createNote}>Create note</button>\r\n      <ul>\r\n        {notes.map((note) => (\r\n          <li key={note}>{note}</li>\r\n        ))}\r\n      </ul>\r\n    </section>\r\n  );\r\n}\r\n```\r\n\r\n---\r\n\r\n## Chunked stream example\r\n\r\n```ts\r\nimport { BrowserSecureClient } from \"@aegis-fluxion/browser-client\";\r\n\r\nconst client = new BrowserSecureClient(\"wss://api.example.com/socket\");\r\n\r\nclient.on(\"ready\", async () => {\r\n  const selectedFile = (document.querySelector(\"#file\") as HTMLInputElement)\r\n    .files?.[0];\r\n\r\n  if (!selectedFile) {\r\n    return;\r\n  }\r\n\r\n  const result = await client.emitStream(\"files:upload\", selectedFile, {\r\n    chunkSizeBytes: 64 * 1024,\r\n    totalBytes: selectedFile.size,\r\n    metadata: { filename: selectedFile.name }\r\n  });\r\n\r\n  console.log(result);\r\n});\r\n\r\nclient.onStream(\"files:download\", async (stream) => {\r\n  const reader = stream.getReader();\r\n  const chunks: Uint8Array[] = [];\r\n\r\n  while (true) {\r\n    const { value, done } = await reader.read();\r\n    if (done) {\r\n      break;\r\n    }\r\n\r\n    if (value) {\r\n      chunks.push(value);\r\n    }\r\n  }\r\n\r\n  const merged = new Uint8Array(chunks.reduce((a, c) => a + c.length, 0));\r\n  let offset = 0;\r\n\r\n  for (const chunk of chunks) {\r\n    merged.set(chunk, offset);\r\n    offset += chunk.length;\r\n  }\r\n\r\n  console.log(\"Downloaded bytes\", merged.byteLength);\r\n});\r\n```\r\n\r\n---\r\n\r\n## API summary\r\n\r\n- `new BrowserSecureClient(url, options?)`\r\n- `connect()` / `disconnect(code?, reason?)`\r\n- `isConnected()` / `readyState`\r\n- `on(...)` / `off(...)`\r\n- `emit(event, data, ackOptionsOrCallback?)`\r\n- `emitStream(event, source, options?)`\r\n- `onStream(event, handler)` / `offStream(event, handler)`\r\n\r\n---\r\n\r\n## Compatibility notes\r\n\r\n- Requires a runtime with `WebSocket`, `crypto.subtle`, and `ReadableStream`.\r\n- Designed to connect to `@aegis-fluxion/core` `SecureServer` endpoints.\r\n- This package is transport/client focused; server-side primitives remain in `@aegis-fluxion/core`.\r\n\r\n---\r\n\r\n## License\r\n\r\nMIT\r\n","readmeFilename":"README.md","_rev":"1-f6a8310c695d81b45e6243ce12d40fb3"}