@taceo/oprf-client 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,6 +18,7 @@ Services must be pre-built WebSocket URLs. Use `toOprfUri` to construct them fro
18
18
 
19
19
  ```ts
20
20
  import { distributedOprf, toOprfUri } from '@taceo/oprf-client';
21
+ import { randomBlindingFactor } from '@taceo/oprf-core';
21
22
 
22
23
  const bases = [
23
24
  'http://node1.example.com',
@@ -27,12 +28,14 @@ const bases = [
27
28
 
28
29
  const services = bases.map((s) => toOprfUri(s, 'my-module'));
29
30
 
31
+ const blindingFactor = randomBlindingFactor();
30
32
  const result = await distributedOprf(
31
33
  services,
32
34
  2, // threshold
33
35
  12345n, // query
36
+ blindingFactor,
34
37
  0n, // domain separator
35
- { auth: { api_key: 'secret' } }
38
+ { api_key: 'secret' } // auth (optional)
36
39
  );
37
40
 
38
41
  console.log(result.output); // OPRF output (bigint)
@@ -109,13 +112,13 @@ const output = finalizeOutput(domainSeparator, query, unblinded);
109
112
 
110
113
  ### Main Functions
111
114
 
112
- - **`distributedOprf(services, threshold, query, domainSeparator, options?): Promise<VerifiableOprfOutput>`**
115
+ - **`distributedOprf(services, threshold, query, blindingFactor, domainSeparator, auth?): Promise<VerifiableOprfOutput>`**
113
116
 
114
- End-to-end distributed OPRF: blind → init sessions → challenge → finish → verify → unblind → finalize. Services must be pre-built WS URLs (use `toOprfUri`).
117
+ End-to-end distributed OPRF: blind → init sessions → challenge → finish → verify → unblind → finalize. Services must be pre-built WS URLs (use `toOprfUri`). The caller must provide a `blindingFactor` (use `randomBlindingFactor()` from `@taceo/oprf-core`).
115
118
 
116
- - **`toOprfUri(service, auth, protocolVersion?): string`**
119
+ - **`toOprfUri(service, authModuleName, clientVersion?): string`**
117
120
 
118
- Build a WebSocket URL for a single OPRF service. Converts `http://` → `ws://`, `https://` → `wss://`. Appends `/api/{auth}/oprf?version={protocolVersion}`.
121
+ Build a WebSocket URL for a single OPRF service. Converts `http://` → `ws://`, `https://` → `wss://`. Appends `/api/{authModuleName}/oprf?version={clientVersion}`.
119
122
 
120
123
  - **`initSessions(services, threshold, request): Promise<OprfSessions>`**
121
124
 
@@ -149,10 +152,6 @@ interface VerifiableOprfOutput {
149
152
  oprfPublicKey: AffinePoint<bigint>; // Service public key
150
153
  epoch: number; // Key epoch
151
154
  }
152
-
153
- interface DistributedOprfOptions<Auth = unknown> {
154
- auth?: Auth; // Auth payload for OprfRequest
155
- }
156
155
  ```
157
156
 
158
157
  ### Error Handling
package/dist/index.cjs CHANGED
@@ -37,6 +37,7 @@ module.exports = __toCommonJS(index_exports);
37
37
 
38
38
  // src/client.ts
39
39
  var import_oprf_core = require("@taceo/oprf-core");
40
+ var import_utils = require("@noble/hashes/utils.js");
40
41
 
41
42
  // src/errors.ts
42
43
  var ServiceError = class _ServiceError extends Error {
@@ -213,7 +214,7 @@ var WebSocketSession = class _WebSocketSession {
213
214
  reject(
214
215
  new NodeError("WsError", {
215
216
  reason: `Failed to connect to ${url}`,
216
- cause: event instanceof ErrorEvent ? event.error : void 0
217
+ cause: typeof ErrorEvent !== "undefined" && event instanceof ErrorEvent ? event.error : void 0
217
218
  })
218
219
  );
219
220
  };
@@ -282,7 +283,7 @@ var WebSocketSession = class _WebSocketSession {
282
283
  reject(
283
284
  new NodeError("WsError", {
284
285
  reason: "WebSocket error",
285
- cause: event instanceof ErrorEvent ? event.error : void 0
286
+ cause: typeof ErrorEvent !== "undefined" && event instanceof ErrorEvent ? event.error : void 0
286
287
  })
287
288
  );
288
289
  };
@@ -460,14 +461,12 @@ function verifyDlogEquality(requestId, oprfPublicKey, blindedRequest, proofShare
460
461
  }
461
462
  return proof;
462
463
  }
463
- async function distributedOprf(services, threshold, query, domainSeparator, options = {}) {
464
+ async function distributedOprf(services, threshold, query, blindingFactor, domainSeparator, auth) {
464
465
  if (new Set(services).size !== services.length) {
465
466
  throw new OprfClientError("NonUniqueServices", "Services must be unique");
466
467
  }
467
- const blindingFactor = (0, import_oprf_core.randomBlindingFactor)();
468
468
  const blindedRequest = (0, import_oprf_core.blindQuery)(query, blindingFactor);
469
- const requestId = crypto.randomUUID();
470
- const auth = options.auth ?? {};
469
+ const requestId = generateRequestId();
471
470
  let sessions;
472
471
  try {
473
472
  sessions = await initSessions(services, threshold, {
@@ -526,6 +525,18 @@ async function distributedOprf(services, threshold, query, domainSeparator, opti
526
525
  epoch: sessions.epoch
527
526
  };
528
527
  }
528
+ function generateRequestId() {
529
+ if (typeof globalThis.crypto?.randomUUID === "function") {
530
+ return globalThis.crypto.randomUUID();
531
+ }
532
+ const bytes = (0, import_utils.randomBytes)(16);
533
+ bytes[6] = bytes[6] & 15 | 64;
534
+ bytes[8] = bytes[8] & 63 | 128;
535
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join(
536
+ ""
537
+ );
538
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
539
+ }
529
540
 
530
541
  // src/uri.ts
531
542
  function toOprfUri(service, auth_module_name, clientVersion = DEFAULT_CLIENT_VERSION) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/errors.ts","../src/types.ts","../src/ws.ts","../src/sessions.ts","../src/uri.ts"],"sourcesContent":["/**\n * @taceo/oprf-client – WebSocket client for TACEO OPRF service.\n */\n\nexport {\n distributedOprf,\n generateChallengeRequest,\n verifyDlogEquality,\n type VerifiableOprfOutput,\n type OprfSessions,\n} from './client.js';\nexport { initSessions, finishSessions } from './sessions.js';\nexport type {\n OprfRequest,\n OprfResponse,\n OprfPublicKeyWithEpoch,\n} from './types.js';\nexport {\n OprfClientError,\n isOprfClientError,\n NodeError,\n isNodeError,\n ServiceError,\n aggregateError,\n} from './errors.js';\nexport type { OprfClientErrorCode, NodeErrorCode } from './errors.js';\nexport { toOprfUri } from './uri.js';\n","/**\n * High-level client: generateChallengeRequest, verifyDlogEquality, distributedOprf.\n */\n\nimport {\n blindQuery,\n unblindResponse,\n finalizeOutput,\n randomBlindingFactor,\n prepareBlindingFactor,\n DLogCommitmentsShamir,\n dlogEqualityVerify,\n BABYJUBJUB_SUBGROUP_GENERATOR_AFFINE,\n type BlindingFactor,\n type DLogEqualityProof,\n type DLogProofShareShamir,\n} from '@taceo/oprf-core';\nimport type { AffinePoint } from '@noble/curves/abstract/curve.js';\nimport {\n OprfClientError,\n aggregateError,\n isNodeError,\n type NodeError,\n} from './errors.js';\nimport type { OprfSessions } from './sessions.js';\nimport { initSessions, finishSessions } from './sessions.js';\n\nexport type { OprfSessions };\n\nexport interface VerifiableOprfOutput {\n output: bigint;\n dlogProof: DLogEqualityProof;\n blindedRequest: AffinePoint<bigint>;\n blindedResponse: AffinePoint<bigint>;\n unblindedResponse: AffinePoint<bigint>;\n oprfPublicKey: AffinePoint<bigint>;\n epoch: number;\n}\n\n/**\n * Build challenge from sessions: contributingParties = party_id + 1 per party, then combineCommitments.\n */\nexport function generateChallengeRequest(\n sessions: OprfSessions\n): DLogCommitmentsShamir {\n const contributingParties = sessions.partyIds.map((id) => id + 1);\n return DLogCommitmentsShamir.combineCommitments(\n sessions.commitments,\n contributingParties\n );\n}\n\n/**\n * Combine proof shares, verify DLog proof. Throws OprfClientError if invalid.\n */\nexport function verifyDlogEquality(\n requestId: string,\n oprfPublicKey: AffinePoint<bigint>,\n blindedRequest: AffinePoint<bigint>,\n proofShares: DLogProofShareShamir[],\n challenge: DLogCommitmentsShamir\n): DLogEqualityProof {\n const blindedResponse = challenge.blindedResponse();\n const proof = challenge.combineProofs(\n requestId,\n proofShares,\n oprfPublicKey,\n blindedRequest\n );\n try {\n dlogEqualityVerify(\n proof,\n oprfPublicKey,\n blindedRequest,\n blindedResponse,\n BABYJUBJUB_SUBGROUP_GENERATOR_AFFINE\n );\n } catch {\n throw new OprfClientError(\n 'InvalidDLogProof',\n 'DLog proof could not be verified'\n );\n }\n return proof;\n}\n\nexport interface DistributedOprfOptions<Auth = unknown> {\n /** Auth payload sent in OprfRequest (must be JSON-serializable). */\n auth?: Auth;\n}\n\n/**\n * Full distributed OPRF: blind → init sessions → challenge → finish → verify → unblind → finalize.\n * Services must be pre-built WS URLs (use toOprfUri / toOprfUriMany).\n */\nexport async function distributedOprf(\n services: string[],\n threshold: number,\n query: bigint,\n domainSeparator: bigint,\n options: DistributedOprfOptions = {}\n): Promise<VerifiableOprfOutput> {\n if (new Set(services).size !== services.length) {\n throw new OprfClientError('NonUniqueServices', 'Services must be unique');\n }\n\n const blindingFactor: BlindingFactor = randomBlindingFactor();\n const blindedRequest = blindQuery(query, blindingFactor);\n\n const requestId = crypto.randomUUID();\n const auth = options.auth ?? ({} as unknown);\n\n let sessions: OprfSessions;\n try {\n sessions = await initSessions(services, threshold, {\n request_id: requestId,\n blinded_query: blindedRequest,\n auth,\n });\n } catch (err) {\n // initSessions throws NodeError[] on threshold failure\n if (Array.isArray(err) && err.every(isNodeError)) {\n throw aggregateError(threshold, err as NodeError[]);\n }\n throw err;\n }\n\n const firstKey = sessions.oprfPublicKeys[0]!;\n for (const key of sessions.oprfPublicKeys) {\n if (key.x !== firstKey.x || key.y !== firstKey.y) {\n throw new OprfClientError(\n 'InconsistentOprfPublicKeys',\n 'OPRF nodes returned different public keys'\n );\n }\n }\n const oprfPublicKey = firstKey;\n\n const challenge = generateChallengeRequest(sessions);\n\n let proofShares: DLogProofShareShamir[];\n try {\n proofShares = await finishSessions(sessions, challenge);\n } catch (err) {\n // finishSessions throws NodeError (from ws methods)\n if (isNodeError(err)) {\n throw new OprfClientError(\n 'CannotFinishSession',\n `Failed to finish session: ${err.message}`,\n { cause: err }\n );\n }\n throw err;\n }\n\n const dlogProof = verifyDlogEquality(\n requestId,\n oprfPublicKey,\n blindedRequest,\n proofShares,\n challenge\n );\n\n const blindedResponse = challenge.blindedResponse();\n const prepared = prepareBlindingFactor(blindingFactor);\n const unblindedResponse = unblindResponse(blindedResponse, prepared);\n const output = finalizeOutput(domainSeparator, query, unblindedResponse);\n\n return {\n output,\n dlogProof,\n blindedRequest,\n blindedResponse,\n unblindedResponse,\n oprfPublicKey,\n epoch: sessions.epoch,\n };\n}\n","/**\n * Client error types matching the Rust oprf-client Error enum.\n * Two-tier model: NodeError (per-node) and OprfClientError (protocol-level).\n */\n\n// ── ServiceError ─────────────────────────────────────────────────────────────\n\n/**\n * Application-level error received in a WebSocket close frame from a node.\n */\nexport class ServiceError extends Error {\n readonly errorCode: number;\n readonly msg?: string;\n\n constructor(errorCode: number, msg?: string) {\n super(msg ?? `Service error ${errorCode}`);\n this.name = 'ServiceError';\n this.errorCode = errorCode;\n this.msg = msg;\n Object.setPrototypeOf(this, ServiceError.prototype);\n }\n}\n\n// ── NodeError ─────────────────────────────────────────────────────────────────\n\nexport type NodeErrorCode =\n | 'ServiceError'\n | 'WsError'\n | 'UnexpectedMessage'\n | 'Unknown';\n\n/**\n * Per-node error, discriminated by `code`.\n */\nexport class NodeError extends Error {\n readonly code: NodeErrorCode;\n readonly reason?: string;\n readonly cause?: Error;\n readonly serviceError?: ServiceError;\n\n constructor(\n code: NodeErrorCode,\n opts: {\n reason?: string;\n cause?: Error;\n serviceError?: ServiceError;\n } = {}\n ) {\n super(opts.reason ?? opts.serviceError?.message ?? code);\n this.name = 'NodeError';\n this.code = code;\n this.reason = opts.reason;\n this.cause = opts.cause;\n this.serviceError = opts.serviceError;\n Object.setPrototypeOf(this, NodeError.prototype);\n }\n}\n\nexport function isNodeError(err: unknown): err is NodeError {\n return err instanceof NodeError;\n}\n\n// ── OprfClientError ───────────────────────────────────────────────────────────\n\nexport type OprfClientErrorCode =\n | 'NonUniqueServices'\n | 'InvalidDLogProof'\n | 'InconsistentOprfPublicKeys'\n | 'ThresholdServiceError'\n | 'Networking'\n | 'UnexpectedMessage'\n | 'CannotFinishSession'\n | 'NodeErrorDisagreement'\n | 'Unknown';\n\nexport interface OprfClientErrorDetails {\n nodeErrors?: NodeError[];\n serviceError?: ServiceError;\n cause?: NodeError;\n networkingErrors?: Error[];\n}\n\nexport class OprfClientError extends Error {\n readonly code: OprfClientErrorCode;\n readonly details?: OprfClientErrorDetails;\n\n constructor(\n code: OprfClientErrorCode,\n message: string,\n details?: OprfClientErrorDetails\n ) {\n super(message);\n this.name = 'OprfClientError';\n this.code = code;\n this.details = details;\n Object.setPrototypeOf(this, OprfClientError.prototype);\n }\n}\n\nexport function isOprfClientError(err: unknown): err is OprfClientError {\n return err instanceof OprfClientError;\n}\n\n// ── aggregateError ────────────────────────────────────────────────────────────\n\n/**\n * Aggregate per-node errors into a protocol-level OprfClientError.\n * Mirrors Rust oprf-client aggregate_error logic:\n * - >= threshold ServiceErrors with same code → ThresholdServiceError\n * - >= threshold UnexpectedMessage with same reason → UnexpectedMessage\n * - >= threshold WsErrors → Networking\n * - Otherwise → NodeErrorDisagreement\n */\nexport function aggregateError(\n threshold: number,\n errors: NodeError[]\n): OprfClientError {\n // Count ServiceError by errorCode\n const serviceErrorCounts = new Map<\n number,\n { count: number; err: ServiceError }\n >();\n for (const e of errors) {\n if (e.code === 'ServiceError' && e.serviceError) {\n const code = e.serviceError.errorCode;\n const existing = serviceErrorCounts.get(code);\n if (existing) {\n existing.count++;\n } else {\n serviceErrorCounts.set(code, { count: 1, err: e.serviceError });\n }\n }\n }\n for (const { count, err } of serviceErrorCounts.values()) {\n if (count >= threshold) {\n return new OprfClientError(\n 'ThresholdServiceError',\n `${count} nodes returned service error ${err.errorCode}: ${err.msg ?? ''}`.trim(),\n { serviceError: err }\n );\n }\n }\n\n // Count UnexpectedMessage by reason\n const unexpectedCounts = new Map<string, number>();\n for (const e of errors) {\n if (e.code === 'UnexpectedMessage') {\n const key = e.reason ?? '';\n unexpectedCounts.set(key, (unexpectedCounts.get(key) ?? 0) + 1);\n }\n }\n for (const [reason, count] of unexpectedCounts) {\n if (count >= threshold) {\n return new OprfClientError(\n 'UnexpectedMessage',\n `${count} nodes reported unexpected message: ${reason}`,\n { nodeErrors: errors }\n );\n }\n }\n\n // Count WsErrors\n const wsErrors = errors.filter((e) => e.code === 'WsError');\n if (wsErrors.length >= threshold) {\n return new OprfClientError(\n 'Networking',\n `${wsErrors.length} nodes had networking errors`,\n { networkingErrors: wsErrors.map((e) => e.cause ?? e) }\n );\n }\n\n // Fallback: disagreement\n return new OprfClientError(\n 'NodeErrorDisagreement',\n 'Nodes returned disagreeing errors',\n { nodeErrors: errors }\n );\n}\n","/**\n * Wire and API types for OPRF client.\n * Matches nullifier-oracle-service oprf-types (OprfRequest, OprfResponse, etc.).\n */\n\nimport type { AffinePoint } from '@noble/curves/abstract/curve.js';\nimport type { PartialDLogEqualityCommitments } from '@taceo/oprf-core';\n\n/**\n * Affine point as JSON: a 2-element array [x, y] of decimal strings.\n * Matches Rust ark_serde_compat::babyjubjub::serialize_affine / deserialize_affine.\n */\nexport type AffinePointWire = [string, string];\n\n/** OPRF public key with epoch (wire shape). */\nexport interface OprfPublicKeyWithEpochWire {\n readonly key: AffinePointWire;\n readonly epoch: number;\n}\n\n/** Client request sent to server (wire shape). */\nexport interface OprfRequestWire<Auth = unknown> {\n readonly request_id: string;\n readonly blinded_query: AffinePointWire;\n readonly auth: Auth;\n}\n\n/** Server response (wire shape). */\nexport interface OprfResponseWire {\n readonly commitments: PartialDLogEqualityCommitmentsWire;\n readonly party_id: number;\n readonly oprf_pub_key_with_epoch: OprfPublicKeyWithEpochWire;\n}\n\n/** Per-party commitments (wire shape; points as [x, y] string tuples). */\nexport interface PartialDLogEqualityCommitmentsWire {\n readonly c: AffinePointWire;\n readonly d1: AffinePointWire;\n readonly d2: AffinePointWire;\n readonly e1: AffinePointWire;\n readonly e2: AffinePointWire;\n}\n\n/**\n * DLog proof share (wire: transparent scalar string).\n * Matches Rust DLogProofShareShamir which is #[serde(transparent)] over a scalar field string.\n */\nexport type DLogProofShareShamirWire = string;\n\n/** DLogCommitmentsShamir wire (server expects snake_case: contributing_parties). */\nexport interface DLogCommitmentsShamirWire {\n c: AffinePointWire;\n d1: AffinePointWire;\n d2: AffinePointWire;\n e1: AffinePointWire;\n e2: AffinePointWire;\n contributing_parties: number[];\n}\n\n/** OprfPublicKeyWithEpoch (internal: affine points). */\nexport interface OprfPublicKeyWithEpoch {\n key: AffinePoint<bigint>;\n epoch: number;\n}\n\n/** OprfRequest (internal: affine for blinded_query). */\nexport interface OprfRequest<Auth = unknown> {\n request_id: string;\n blinded_query: AffinePoint<bigint>;\n auth: Auth;\n}\n\n/** OprfResponse (internal: affine points). */\nexport interface OprfResponse {\n commitments: PartialDLogEqualityCommitments;\n party_id: number;\n oprf_pub_key_with_epoch: OprfPublicKeyWithEpoch;\n}\n\nexport function wireToAffine(w: AffinePointWire): AffinePoint<bigint> {\n return { x: BigInt(w[0]), y: BigInt(w[1]) };\n}\n\nexport function affineToWire(p: AffinePoint<bigint>): AffinePointWire {\n return [p.x.toString(), p.y.toString()];\n}\n\nexport function wireToCommitments(\n w: PartialDLogEqualityCommitmentsWire\n): PartialDLogEqualityCommitments {\n return {\n c: wireToAffine(w.c),\n d1: wireToAffine(w.d1),\n d2: wireToAffine(w.d2),\n e1: wireToAffine(w.e1),\n e2: wireToAffine(w.e2),\n };\n}\n\nexport function commitmentsToWire(\n c: PartialDLogEqualityCommitments\n): PartialDLogEqualityCommitmentsWire {\n return {\n c: affineToWire(c.c),\n d1: affineToWire(c.d1),\n d2: affineToWire(c.d2),\n e1: affineToWire(c.e1),\n e2: affineToWire(c.e2),\n };\n}\n\nexport function wireToOprfResponse(w: OprfResponseWire): OprfResponse {\n return {\n commitments: wireToCommitments(w.commitments),\n party_id: w.party_id,\n oprf_pub_key_with_epoch: {\n key: wireToAffine(w.oprf_pub_key_with_epoch.key),\n epoch: w.oprf_pub_key_with_epoch.epoch,\n },\n };\n}\n\nexport function wireToProofShare(w: DLogProofShareShamirWire): {\n value: bigint;\n} {\n return { value: BigInt(w) };\n}\n\nexport function proofShareToWire(p: {\n value: bigint;\n}): DLogProofShareShamirWire {\n return p.value.toString();\n}\n\n/** Convert DLogCommitmentsShamir (data) to wire shape for server. */\nexport function challengeToWire(data: {\n c: AffinePoint<bigint>;\n d1: AffinePoint<bigint>;\n d2: AffinePoint<bigint>;\n e1: AffinePoint<bigint>;\n e2: AffinePoint<bigint>;\n contributingParties: number[];\n}): DLogCommitmentsShamirWire {\n return {\n c: affineToWire(data.c),\n d1: affineToWire(data.d1),\n d2: affineToWire(data.d2),\n e1: affineToWire(data.e1),\n e2: affineToWire(data.e2),\n contributing_parties: data.contributingParties,\n };\n}\n","/**\n * WebSocket session for a single OPRF service connection.\n * Takes a pre-built WS URL (see uri.ts). Errors are NodeError variants.\n */\n\nimport { NodeError, ServiceError } from './errors.js';\nimport type { OprfResponseWire } from './types.js';\nimport { wireToOprfResponse } from './types.js';\nimport type { OprfResponse } from './types.js';\nimport type { DLogCommitmentsShamirWire } from './types.js';\nimport { wireToProofShare, type DLogProofShareShamirWire } from './types.js';\n\n/** Default protocol version sent to server (query param; browser cannot set headers). */\nexport const DEFAULT_CLIENT_VERSION = '0.8.0';\n\n/**\n * Thin WebSocket session: connect, send JSON, receive JSON, handle close.\n * Uses text (JSON) frames. Browser WebSocket cannot set headers; version is sent as query param.\n * All errors are thrown as NodeError.\n */\nexport class WebSocketSession {\n private ws: WebSocket;\n private readonly service: string;\n\n private constructor(ws: WebSocket, service: string) {\n this.ws = ws;\n this.service = service;\n }\n\n get serviceUrl(): string {\n return this.service;\n }\n\n /**\n * Open a new WebSocket to the given pre-built WS URL.\n * Connect errors throw NodeError('WsError', ...).\n */\n static connect(url: string): Promise<WebSocketSession> {\n return new Promise((resolve, reject) => {\n let ws: WebSocket;\n try {\n ws = new WebSocket(url);\n } catch (err) {\n reject(\n new NodeError('WsError', {\n reason: `Failed to construct WebSocket for ${url}`,\n cause: err instanceof Error ? err : undefined,\n })\n );\n return;\n }\n ws.binaryType = 'arraybuffer';\n ws.onopen = () => {\n resolve(new WebSocketSession(ws, url));\n };\n ws.onerror = (event) => {\n reject(\n new NodeError('WsError', {\n reason: `Failed to connect to ${url}`,\n cause: event instanceof ErrorEvent ? event.error : undefined,\n })\n );\n };\n });\n }\n\n /** Send a JSON-serializable message as text frame. Throws NodeError('WsError') on failure. */\n send<T extends object>(msg: T): void {\n if (this.ws.readyState !== WebSocket.OPEN) {\n throw new NodeError('WsError', { reason: 'WebSocket not open' });\n }\n try {\n this.ws.send(JSON.stringify(msg));\n } catch (err) {\n throw new NodeError('WsError', {\n reason: 'Failed to send message',\n cause: err instanceof Error ? err : undefined,\n });\n }\n }\n\n /**\n * Read next message. Returns text data.\n * Rejects with NodeError on:\n * - Binary frame → UnexpectedMessage\n * - Non-normal close → ServiceError\n * - Normal/1005 close → UnexpectedMessage('Server closed websocket')\n * - Error event → WsError\n * - null/end → UnexpectedMessage('Server closed connection')\n */\n private readNext(): Promise<string> {\n return new Promise((resolve, reject) => {\n const onMessage = (event: MessageEvent) => {\n cleanup();\n if (typeof event.data === 'string') {\n resolve(event.data);\n } else {\n reject(\n new NodeError('UnexpectedMessage', {\n reason: 'binary frame received',\n })\n );\n }\n };\n const onClose = (event: CloseEvent) => {\n cleanup();\n if (event.code !== 1000 && event.code !== 1005) {\n const svcErr = new ServiceError(\n event.code,\n event.reason || undefined\n );\n reject(\n new NodeError('ServiceError', {\n reason: svcErr.message,\n serviceError: svcErr,\n })\n );\n } else {\n reject(\n new NodeError('UnexpectedMessage', {\n reason: 'Server closed websocket',\n })\n );\n }\n };\n const onError = (event: Event) => {\n cleanup();\n reject(\n new NodeError('WsError', {\n reason: 'WebSocket error',\n cause: event instanceof ErrorEvent ? event.error : undefined,\n })\n );\n };\n const cleanup = () => {\n this.ws.removeEventListener('message', onMessage);\n this.ws.removeEventListener('close', onClose);\n this.ws.removeEventListener('error', onError);\n };\n this.ws.addEventListener('message', onMessage);\n this.ws.addEventListener('close', onClose);\n this.ws.addEventListener('error', onError);\n });\n }\n\n /** Read and parse OprfResponse. Parse errors → NodeError('UnexpectedMessage'). */\n async readOprfResponse(): Promise<OprfResponse> {\n const raw = await this.readNext();\n try {\n const w = JSON.parse(raw) as OprfResponseWire;\n return wireToOprfResponse(w);\n } catch {\n throw new NodeError('UnexpectedMessage', {\n reason: 'Invalid OprfResponse JSON',\n });\n }\n }\n\n /** Read and parse DLogProofShareShamir. Parse errors → NodeError('UnexpectedMessage'). */\n async readProofShare(): Promise<{ value: bigint }> {\n const raw = await this.readNext();\n try {\n const w = JSON.parse(raw) as DLogProofShareShamirWire;\n return wireToProofShare(w);\n } catch {\n throw new NodeError('UnexpectedMessage', {\n reason: 'Invalid proof share JSON',\n });\n }\n }\n\n /** Send challenge (DLogCommitmentsShamir wire). */\n sendChallenge(wire: DLogCommitmentsShamirWire): void {\n this.send(wire);\n }\n\n /** Gracefully close with normal code. */\n close(): void {\n if (this.ws.readyState === WebSocket.OPEN) {\n this.ws.close(1000, 'success');\n }\n }\n}\n","/**\n * Session management: initSessions (parallel connect, collect by epoch to threshold),\n * finishSessions (send challenge, collect proof shares). Mirrors Rust oprf-client sessions.\n */\n\nimport {\n DLogCommitmentsShamir,\n type PartialDLogCommitmentsShamir,\n type DLogProofShareShamir,\n} from '@taceo/oprf-core';\nimport { NodeError } from './errors.js';\nimport type { OprfRequest, OprfPublicKeyWithEpoch } from './types.js';\nimport { affineToWire, challengeToWire } from './types.js';\nimport { WebSocketSession } from './ws.js';\n\nexport interface OprfSessions {\n readonly ws: WebSocketSession[];\n readonly partyIds: number[];\n readonly commitments: PartialDLogCommitmentsShamir[];\n readonly oprfPublicKeys: AffinePointLike[];\n readonly epoch: number;\n}\n\n/** Internal: we only need key as affine for verification. */\ninterface AffinePointLike {\n x: bigint;\n y: bigint;\n}\n\nfunction oprfPublicKeyToAffine(k: OprfPublicKeyWithEpoch): AffinePointLike {\n return k.key;\n}\n\n/**\n * Open WebSockets to each service in parallel, send oprfRequest, collect OprfResponse.\n * Services must be pre-built WS URLs (see toOprfUri / toOprfUriMany).\n * Group by epoch; when an epoch has >= threshold responses with distinct party_id, return those sessions (sorted by party_id).\n * On failure: throws NodeError[] (caller wraps via aggregateError).\n */\nexport async function initSessions<Auth>(\n services: string[],\n threshold: number,\n oprfRequest: OprfRequest<Auth>\n): Promise<OprfSessions> {\n const requestWire = {\n request_id: oprfRequest.request_id,\n blinded_query: affineToWire(oprfRequest.blinded_query),\n auth: oprfRequest.auth,\n };\n\n const results = await Promise.allSettled(\n services.map(async (service) => {\n const session = await WebSocketSession.connect(service);\n await session.send(requestWire);\n const response = await session.readOprfResponse();\n return { session, response };\n })\n );\n\n const epochMap = new Map<\n number,\n {\n ws: WebSocketSession[];\n partyIds: number[];\n commitments: PartialDLogCommitmentsShamir[];\n oprfPublicKeys: AffinePointLike[];\n }\n >();\n const nodeErrors: NodeError[] = [];\n\n for (let i = 0; i < results.length; i++) {\n const r = results[i]!;\n if (r.status === 'rejected') {\n // NodeError thrown from ws methods; wrap unknown errors\n const err = r.reason;\n if (err && err instanceof NodeError) {\n nodeErrors.push(err);\n } else {\n nodeErrors.push(\n new NodeError('Unknown', {\n reason: String(err),\n cause: err instanceof Error ? err : undefined,\n })\n );\n }\n continue;\n }\n const { session, response } = r.value;\n const epoch = response.oprf_pub_key_with_epoch.epoch;\n let bucket = epochMap.get(epoch);\n if (!bucket) {\n bucket = { ws: [], partyIds: [], commitments: [], oprfPublicKeys: [] };\n epochMap.set(epoch, bucket);\n }\n if (bucket.partyIds.includes(response.party_id)) {\n session.close();\n continue;\n }\n bucket.ws.push(session);\n bucket.partyIds.push(response.party_id);\n bucket.commitments.push(response.commitments);\n bucket.oprfPublicKeys.push(\n oprfPublicKeyToAffine(response.oprf_pub_key_with_epoch)\n );\n\n if (bucket.ws.length >= threshold) {\n // close other sessions that we won't use\n for (let j = i + 1; j < results.length; j++) {\n const r2 = results[j];\n if (r2?.status === 'rejected') {\n continue;\n }\n r2?.value?.session.close();\n }\n break;\n }\n }\n\n for (const [epoch, bucket] of epochMap) {\n if (bucket.ws.length >= threshold) {\n for (const [otherEpoch, otherBucket] of epochMap) {\n if (otherEpoch !== epoch) {\n for (const ws of otherBucket.ws) ws.close();\n }\n }\n const order = bucket.partyIds\n .map((id, i) => ({ id, i }))\n .sort((a, b) => a.id - b.id);\n return {\n ws: order.map(({ i }) => bucket.ws[i]!),\n partyIds: order.map(({ id }) => id),\n commitments: order.map(({ i }) => bucket.commitments[i]!),\n oprfPublicKeys: order.map(({ i }) => bucket.oprfPublicKeys[i]!),\n epoch,\n };\n }\n }\n\n // Not enough responses — throw collected node errors for caller to aggregate\n throw nodeErrors;\n}\n\n/**\n * Send the same challenge to each session, read proof share, then close.\n * Returns proof shares in the same order as sessions.ws.\n * Errors bubble up as NodeError (thrown from ws methods).\n */\nexport async function finishSessions(\n sessions: OprfSessions,\n challenge: DLogCommitmentsShamir\n): Promise<DLogProofShareShamir[]> {\n const wire = challengeToWire(challenge.data);\n const results = await Promise.all(\n sessions.ws.map(async (session) => {\n await session.sendChallenge(wire);\n const share = await session.readProofShare();\n session.close();\n return { value: share.value };\n })\n );\n return results;\n}\n","/**\n * URI construction helpers for OPRF service endpoints.\n */\n\nimport { DEFAULT_CLIENT_VERSION } from './ws.js';\n\n/**\n * Build a WebSocket URL for a single OPRF service.\n * http → ws, https → wss. Appends /api/{auth_module_name}/oprf?version={clientVersion}.\n */\nexport function toOprfUri(\n service: string,\n auth_module_name: string,\n clientVersion: string = DEFAULT_CLIENT_VERSION\n): string {\n const base = service\n .replace(/^https:\\/\\//, 'wss://')\n .replace(/^http:\\/\\//, 'ws://')\n .replace(/\\/$/, '');\n return `${base}/api/${auth_module_name}/oprf?version=${clientVersion}`;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/errors.ts","../src/types.ts","../src/ws.ts","../src/sessions.ts","../src/uri.ts"],"sourcesContent":["/**\n * @taceo/oprf-client – WebSocket client for TACEO OPRF service.\n */\n\nexport {\n distributedOprf,\n generateChallengeRequest,\n verifyDlogEquality,\n type VerifiableOprfOutput,\n type OprfSessions,\n} from './client.js';\nexport { initSessions, finishSessions } from './sessions.js';\nexport type {\n OprfRequest,\n OprfResponse,\n OprfPublicKeyWithEpoch,\n} from './types.js';\nexport {\n OprfClientError,\n isOprfClientError,\n NodeError,\n isNodeError,\n ServiceError,\n aggregateError,\n} from './errors.js';\nexport type { OprfClientErrorCode, NodeErrorCode } from './errors.js';\nexport { toOprfUri } from './uri.js';\n","/**\n * High-level client: generateChallengeRequest, verifyDlogEquality, distributedOprf.\n */\n\nimport {\n blindQuery,\n unblindResponse,\n finalizeOutput,\n prepareBlindingFactor,\n DLogCommitmentsShamir,\n dlogEqualityVerify,\n BABYJUBJUB_SUBGROUP_GENERATOR_AFFINE,\n type BlindingFactor,\n type DLogEqualityProof,\n type DLogProofShareShamir,\n} from '@taceo/oprf-core';\nimport { randomBytes } from '@noble/hashes/utils.js';\nimport type { AffinePoint } from '@noble/curves/abstract/curve.js';\nimport {\n OprfClientError,\n aggregateError,\n isNodeError,\n type NodeError,\n} from './errors.js';\nimport type { OprfSessions } from './sessions.js';\nimport { initSessions, finishSessions } from './sessions.js';\n\nexport type { OprfSessions };\n\nexport interface VerifiableOprfOutput {\n output: bigint;\n dlogProof: DLogEqualityProof;\n blindedRequest: AffinePoint<bigint>;\n blindedResponse: AffinePoint<bigint>;\n unblindedResponse: AffinePoint<bigint>;\n oprfPublicKey: AffinePoint<bigint>;\n epoch: number;\n}\n\n/**\n * Build challenge from sessions: contributingParties = party_id + 1 per party, then combineCommitments.\n */\nexport function generateChallengeRequest(\n sessions: OprfSessions\n): DLogCommitmentsShamir {\n const contributingParties = sessions.partyIds.map((id) => id + 1);\n return DLogCommitmentsShamir.combineCommitments(\n sessions.commitments,\n contributingParties\n );\n}\n\n/**\n * Combine proof shares, verify DLog proof. Throws OprfClientError if invalid.\n */\nexport function verifyDlogEquality(\n requestId: string,\n oprfPublicKey: AffinePoint<bigint>,\n blindedRequest: AffinePoint<bigint>,\n proofShares: DLogProofShareShamir[],\n challenge: DLogCommitmentsShamir\n): DLogEqualityProof {\n const blindedResponse = challenge.blindedResponse();\n const proof = challenge.combineProofs(\n requestId,\n proofShares,\n oprfPublicKey,\n blindedRequest\n );\n try {\n dlogEqualityVerify(\n proof,\n oprfPublicKey,\n blindedRequest,\n blindedResponse,\n BABYJUBJUB_SUBGROUP_GENERATOR_AFFINE\n );\n } catch {\n throw new OprfClientError(\n 'InvalidDLogProof',\n 'DLog proof could not be verified'\n );\n }\n return proof;\n}\n\n/**\n * Full distributed OPRF: blind → init sessions → challenge → finish → verify → unblind → finalize.\n * Services must be pre-built WS URLs (use toOprfUri / toOprfUriMany).\n */\nexport async function distributedOprf(\n services: string[],\n threshold: number,\n query: bigint,\n blindingFactor: BlindingFactor,\n domainSeparator: bigint,\n auth?: unknown\n): Promise<VerifiableOprfOutput> {\n if (new Set(services).size !== services.length) {\n throw new OprfClientError('NonUniqueServices', 'Services must be unique');\n }\n\n const blindedRequest = blindQuery(query, blindingFactor);\n const requestId = generateRequestId();\n\n let sessions: OprfSessions;\n try {\n sessions = await initSessions(services, threshold, {\n request_id: requestId,\n blinded_query: blindedRequest,\n auth,\n });\n } catch (err) {\n // initSessions throws NodeError[] on threshold failure\n if (Array.isArray(err) && err.every(isNodeError)) {\n throw aggregateError(threshold, err as NodeError[]);\n }\n throw err;\n }\n\n const firstKey = sessions.oprfPublicKeys[0]!;\n for (const key of sessions.oprfPublicKeys) {\n if (key.x !== firstKey.x || key.y !== firstKey.y) {\n throw new OprfClientError(\n 'InconsistentOprfPublicKeys',\n 'OPRF nodes returned different public keys'\n );\n }\n }\n const oprfPublicKey = firstKey;\n\n const challenge = generateChallengeRequest(sessions);\n\n let proofShares: DLogProofShareShamir[];\n try {\n proofShares = await finishSessions(sessions, challenge);\n } catch (err) {\n // finishSessions throws NodeError (from ws methods)\n if (isNodeError(err)) {\n throw new OprfClientError(\n 'CannotFinishSession',\n `Failed to finish session: ${err.message}`,\n { cause: err }\n );\n }\n throw err;\n }\n\n const dlogProof = verifyDlogEquality(\n requestId,\n oprfPublicKey,\n blindedRequest,\n proofShares,\n challenge\n );\n\n const blindedResponse = challenge.blindedResponse();\n const prepared = prepareBlindingFactor(blindingFactor);\n const unblindedResponse = unblindResponse(blindedResponse, prepared);\n const output = finalizeOutput(domainSeparator, query, unblindedResponse);\n\n return {\n output,\n dlogProof,\n blindedRequest,\n blindedResponse,\n unblindedResponse,\n oprfPublicKey,\n epoch: sessions.epoch,\n };\n}\n\nfunction generateRequestId(): string {\n if (typeof globalThis.crypto?.randomUUID === 'function') {\n return globalThis.crypto.randomUUID();\n }\n // Fallback for environments without randomUUID\n const bytes = randomBytes(16);\n // Set version (4) and variant (RFC 4122)\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join(\n ''\n );\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n","/**\n * Client error types matching the Rust oprf-client Error enum.\n * Two-tier model: NodeError (per-node) and OprfClientError (protocol-level).\n */\n\n// ── ServiceError ─────────────────────────────────────────────────────────────\n\n/**\n * Application-level error received in a WebSocket close frame from a node.\n */\nexport class ServiceError extends Error {\n readonly errorCode: number;\n readonly msg?: string;\n\n constructor(errorCode: number, msg?: string) {\n super(msg ?? `Service error ${errorCode}`);\n this.name = 'ServiceError';\n this.errorCode = errorCode;\n this.msg = msg;\n Object.setPrototypeOf(this, ServiceError.prototype);\n }\n}\n\n// ── NodeError ─────────────────────────────────────────────────────────────────\n\nexport type NodeErrorCode =\n | 'ServiceError'\n | 'WsError'\n | 'UnexpectedMessage'\n | 'Unknown';\n\n/**\n * Per-node error, discriminated by `code`.\n */\nexport class NodeError extends Error {\n readonly code: NodeErrorCode;\n readonly reason?: string;\n readonly cause?: Error;\n readonly serviceError?: ServiceError;\n\n constructor(\n code: NodeErrorCode,\n opts: {\n reason?: string;\n cause?: Error;\n serviceError?: ServiceError;\n } = {}\n ) {\n super(opts.reason ?? opts.serviceError?.message ?? code);\n this.name = 'NodeError';\n this.code = code;\n this.reason = opts.reason;\n this.cause = opts.cause;\n this.serviceError = opts.serviceError;\n Object.setPrototypeOf(this, NodeError.prototype);\n }\n}\n\nexport function isNodeError(err: unknown): err is NodeError {\n return err instanceof NodeError;\n}\n\n// ── OprfClientError ───────────────────────────────────────────────────────────\n\nexport type OprfClientErrorCode =\n | 'NonUniqueServices'\n | 'InvalidDLogProof'\n | 'InconsistentOprfPublicKeys'\n | 'ThresholdServiceError'\n | 'Networking'\n | 'UnexpectedMessage'\n | 'CannotFinishSession'\n | 'NodeErrorDisagreement'\n | 'Unknown';\n\nexport interface OprfClientErrorDetails {\n nodeErrors?: NodeError[];\n serviceError?: ServiceError;\n cause?: NodeError;\n networkingErrors?: Error[];\n}\n\nexport class OprfClientError extends Error {\n readonly code: OprfClientErrorCode;\n readonly details?: OprfClientErrorDetails;\n\n constructor(\n code: OprfClientErrorCode,\n message: string,\n details?: OprfClientErrorDetails\n ) {\n super(message);\n this.name = 'OprfClientError';\n this.code = code;\n this.details = details;\n Object.setPrototypeOf(this, OprfClientError.prototype);\n }\n}\n\nexport function isOprfClientError(err: unknown): err is OprfClientError {\n return err instanceof OprfClientError;\n}\n\n// ── aggregateError ────────────────────────────────────────────────────────────\n\n/**\n * Aggregate per-node errors into a protocol-level OprfClientError.\n * Mirrors Rust oprf-client aggregate_error logic:\n * - >= threshold ServiceErrors with same code → ThresholdServiceError\n * - >= threshold UnexpectedMessage with same reason → UnexpectedMessage\n * - >= threshold WsErrors → Networking\n * - Otherwise → NodeErrorDisagreement\n */\nexport function aggregateError(\n threshold: number,\n errors: NodeError[]\n): OprfClientError {\n // Count ServiceError by errorCode\n const serviceErrorCounts = new Map<\n number,\n { count: number; err: ServiceError }\n >();\n for (const e of errors) {\n if (e.code === 'ServiceError' && e.serviceError) {\n const code = e.serviceError.errorCode;\n const existing = serviceErrorCounts.get(code);\n if (existing) {\n existing.count++;\n } else {\n serviceErrorCounts.set(code, { count: 1, err: e.serviceError });\n }\n }\n }\n for (const { count, err } of serviceErrorCounts.values()) {\n if (count >= threshold) {\n return new OprfClientError(\n 'ThresholdServiceError',\n `${count} nodes returned service error ${err.errorCode}: ${err.msg ?? ''}`.trim(),\n { serviceError: err }\n );\n }\n }\n\n // Count UnexpectedMessage by reason\n const unexpectedCounts = new Map<string, number>();\n for (const e of errors) {\n if (e.code === 'UnexpectedMessage') {\n const key = e.reason ?? '';\n unexpectedCounts.set(key, (unexpectedCounts.get(key) ?? 0) + 1);\n }\n }\n for (const [reason, count] of unexpectedCounts) {\n if (count >= threshold) {\n return new OprfClientError(\n 'UnexpectedMessage',\n `${count} nodes reported unexpected message: ${reason}`,\n { nodeErrors: errors }\n );\n }\n }\n\n // Count WsErrors\n const wsErrors = errors.filter((e) => e.code === 'WsError');\n if (wsErrors.length >= threshold) {\n return new OprfClientError(\n 'Networking',\n `${wsErrors.length} nodes had networking errors`,\n { networkingErrors: wsErrors.map((e) => e.cause ?? e) }\n );\n }\n\n // Fallback: disagreement\n return new OprfClientError(\n 'NodeErrorDisagreement',\n 'Nodes returned disagreeing errors',\n { nodeErrors: errors }\n );\n}\n","/**\n * Wire and API types for OPRF client.\n * Matches nullifier-oracle-service oprf-types (OprfRequest, OprfResponse, etc.).\n */\n\nimport type { AffinePoint } from '@noble/curves/abstract/curve.js';\nimport type { PartialDLogEqualityCommitments } from '@taceo/oprf-core';\n\n/**\n * Affine point as JSON: a 2-element array [x, y] of decimal strings.\n * Matches Rust ark_serde_compat::babyjubjub::serialize_affine / deserialize_affine.\n */\nexport type AffinePointWire = [string, string];\n\n/** OPRF public key with epoch (wire shape). */\nexport interface OprfPublicKeyWithEpochWire {\n readonly key: AffinePointWire;\n readonly epoch: number;\n}\n\n/** Client request sent to server (wire shape). */\nexport interface OprfRequestWire<Auth = unknown> {\n readonly request_id: string;\n readonly blinded_query: AffinePointWire;\n readonly auth: Auth;\n}\n\n/** Server response (wire shape). */\nexport interface OprfResponseWire {\n readonly commitments: PartialDLogEqualityCommitmentsWire;\n readonly party_id: number;\n readonly oprf_pub_key_with_epoch: OprfPublicKeyWithEpochWire;\n}\n\n/** Per-party commitments (wire shape; points as [x, y] string tuples). */\nexport interface PartialDLogEqualityCommitmentsWire {\n readonly c: AffinePointWire;\n readonly d1: AffinePointWire;\n readonly d2: AffinePointWire;\n readonly e1: AffinePointWire;\n readonly e2: AffinePointWire;\n}\n\n/**\n * DLog proof share (wire: transparent scalar string).\n * Matches Rust DLogProofShareShamir which is #[serde(transparent)] over a scalar field string.\n */\nexport type DLogProofShareShamirWire = string;\n\n/** DLogCommitmentsShamir wire (server expects snake_case: contributing_parties). */\nexport interface DLogCommitmentsShamirWire {\n c: AffinePointWire;\n d1: AffinePointWire;\n d2: AffinePointWire;\n e1: AffinePointWire;\n e2: AffinePointWire;\n contributing_parties: number[];\n}\n\n/** OprfPublicKeyWithEpoch (internal: affine points). */\nexport interface OprfPublicKeyWithEpoch {\n key: AffinePoint<bigint>;\n epoch: number;\n}\n\n/** OprfRequest (internal: affine for blinded_query). */\nexport interface OprfRequest<Auth = unknown> {\n request_id: string;\n blinded_query: AffinePoint<bigint>;\n auth: Auth;\n}\n\n/** OprfResponse (internal: affine points). */\nexport interface OprfResponse {\n commitments: PartialDLogEqualityCommitments;\n party_id: number;\n oprf_pub_key_with_epoch: OprfPublicKeyWithEpoch;\n}\n\nexport function wireToAffine(w: AffinePointWire): AffinePoint<bigint> {\n return { x: BigInt(w[0]), y: BigInt(w[1]) };\n}\n\nexport function affineToWire(p: AffinePoint<bigint>): AffinePointWire {\n return [p.x.toString(), p.y.toString()];\n}\n\nexport function wireToCommitments(\n w: PartialDLogEqualityCommitmentsWire\n): PartialDLogEqualityCommitments {\n return {\n c: wireToAffine(w.c),\n d1: wireToAffine(w.d1),\n d2: wireToAffine(w.d2),\n e1: wireToAffine(w.e1),\n e2: wireToAffine(w.e2),\n };\n}\n\nexport function commitmentsToWire(\n c: PartialDLogEqualityCommitments\n): PartialDLogEqualityCommitmentsWire {\n return {\n c: affineToWire(c.c),\n d1: affineToWire(c.d1),\n d2: affineToWire(c.d2),\n e1: affineToWire(c.e1),\n e2: affineToWire(c.e2),\n };\n}\n\nexport function wireToOprfResponse(w: OprfResponseWire): OprfResponse {\n return {\n commitments: wireToCommitments(w.commitments),\n party_id: w.party_id,\n oprf_pub_key_with_epoch: {\n key: wireToAffine(w.oprf_pub_key_with_epoch.key),\n epoch: w.oprf_pub_key_with_epoch.epoch,\n },\n };\n}\n\nexport function wireToProofShare(w: DLogProofShareShamirWire): {\n value: bigint;\n} {\n return { value: BigInt(w) };\n}\n\nexport function proofShareToWire(p: {\n value: bigint;\n}): DLogProofShareShamirWire {\n return p.value.toString();\n}\n\n/** Convert DLogCommitmentsShamir (data) to wire shape for server. */\nexport function challengeToWire(data: {\n c: AffinePoint<bigint>;\n d1: AffinePoint<bigint>;\n d2: AffinePoint<bigint>;\n e1: AffinePoint<bigint>;\n e2: AffinePoint<bigint>;\n contributingParties: number[];\n}): DLogCommitmentsShamirWire {\n return {\n c: affineToWire(data.c),\n d1: affineToWire(data.d1),\n d2: affineToWire(data.d2),\n e1: affineToWire(data.e1),\n e2: affineToWire(data.e2),\n contributing_parties: data.contributingParties,\n };\n}\n","/**\n * WebSocket session for a single OPRF service connection.\n * Takes a pre-built WS URL (see uri.ts). Errors are NodeError variants.\n */\n\nimport { NodeError, ServiceError } from './errors.js';\nimport type { OprfResponseWire } from './types.js';\nimport { wireToOprfResponse } from './types.js';\nimport type { OprfResponse } from './types.js';\nimport type { DLogCommitmentsShamirWire } from './types.js';\nimport { wireToProofShare, type DLogProofShareShamirWire } from './types.js';\n\n/** Default protocol version sent to server (query param; browser cannot set headers). */\nexport const DEFAULT_CLIENT_VERSION = '0.8.0';\n\n/**\n * Thin WebSocket session: connect, send JSON, receive JSON, handle close.\n * Uses text (JSON) frames. Browser WebSocket cannot set headers; version is sent as query param.\n * All errors are thrown as NodeError.\n */\nexport class WebSocketSession {\n private ws: WebSocket;\n private readonly service: string;\n\n private constructor(ws: WebSocket, service: string) {\n this.ws = ws;\n this.service = service;\n }\n\n get serviceUrl(): string {\n return this.service;\n }\n\n /**\n * Open a new WebSocket to the given pre-built WS URL.\n * Connect errors throw NodeError('WsError', ...).\n */\n static connect(url: string): Promise<WebSocketSession> {\n return new Promise((resolve, reject) => {\n let ws: WebSocket;\n try {\n ws = new WebSocket(url);\n } catch (err) {\n reject(\n new NodeError('WsError', {\n reason: `Failed to construct WebSocket for ${url}`,\n cause: err instanceof Error ? err : undefined,\n })\n );\n return;\n }\n ws.binaryType = 'arraybuffer';\n ws.onopen = () => {\n resolve(new WebSocketSession(ws, url));\n };\n ws.onerror = (event) => {\n reject(\n new NodeError('WsError', {\n reason: `Failed to connect to ${url}`,\n cause:\n typeof ErrorEvent !== 'undefined' && event instanceof ErrorEvent\n ? event.error\n : undefined,\n })\n );\n };\n });\n }\n\n /** Send a JSON-serializable message as text frame. Throws NodeError('WsError') on failure. */\n send<T extends object>(msg: T): void {\n if (this.ws.readyState !== WebSocket.OPEN) {\n throw new NodeError('WsError', { reason: 'WebSocket not open' });\n }\n try {\n this.ws.send(JSON.stringify(msg));\n } catch (err) {\n throw new NodeError('WsError', {\n reason: 'Failed to send message',\n cause: err instanceof Error ? err : undefined,\n });\n }\n }\n\n /**\n * Read next message. Returns text data.\n * Rejects with NodeError on:\n * - Binary frame → UnexpectedMessage\n * - Non-normal close → ServiceError\n * - Normal/1005 close → UnexpectedMessage('Server closed websocket')\n * - Error event → WsError\n * - null/end → UnexpectedMessage('Server closed connection')\n */\n private readNext(): Promise<string> {\n return new Promise((resolve, reject) => {\n const onMessage = (event: MessageEvent) => {\n cleanup();\n if (typeof event.data === 'string') {\n resolve(event.data);\n } else {\n reject(\n new NodeError('UnexpectedMessage', {\n reason: 'binary frame received',\n })\n );\n }\n };\n const onClose = (event: CloseEvent) => {\n cleanup();\n if (event.code !== 1000 && event.code !== 1005) {\n const svcErr = new ServiceError(\n event.code,\n event.reason || undefined\n );\n reject(\n new NodeError('ServiceError', {\n reason: svcErr.message,\n serviceError: svcErr,\n })\n );\n } else {\n reject(\n new NodeError('UnexpectedMessage', {\n reason: 'Server closed websocket',\n })\n );\n }\n };\n const onError = (event: Event) => {\n cleanup();\n reject(\n new NodeError('WsError', {\n reason: 'WebSocket error',\n cause:\n typeof ErrorEvent !== 'undefined' && event instanceof ErrorEvent\n ? event.error\n : undefined,\n })\n );\n };\n const cleanup = () => {\n this.ws.removeEventListener('message', onMessage);\n this.ws.removeEventListener('close', onClose);\n this.ws.removeEventListener('error', onError);\n };\n this.ws.addEventListener('message', onMessage);\n this.ws.addEventListener('close', onClose);\n this.ws.addEventListener('error', onError);\n });\n }\n\n /** Read and parse OprfResponse. Parse errors → NodeError('UnexpectedMessage'). */\n async readOprfResponse(): Promise<OprfResponse> {\n const raw = await this.readNext();\n try {\n const w = JSON.parse(raw) as OprfResponseWire;\n return wireToOprfResponse(w);\n } catch {\n throw new NodeError('UnexpectedMessage', {\n reason: 'Invalid OprfResponse JSON',\n });\n }\n }\n\n /** Read and parse DLogProofShareShamir. Parse errors → NodeError('UnexpectedMessage'). */\n async readProofShare(): Promise<{ value: bigint }> {\n const raw = await this.readNext();\n try {\n const w = JSON.parse(raw) as DLogProofShareShamirWire;\n return wireToProofShare(w);\n } catch {\n throw new NodeError('UnexpectedMessage', {\n reason: 'Invalid proof share JSON',\n });\n }\n }\n\n /** Send challenge (DLogCommitmentsShamir wire). */\n sendChallenge(wire: DLogCommitmentsShamirWire): void {\n this.send(wire);\n }\n\n /** Gracefully close with normal code. */\n close(): void {\n if (this.ws.readyState === WebSocket.OPEN) {\n this.ws.close(1000, 'success');\n }\n }\n}\n","/**\n * Session management: initSessions (parallel connect, collect by epoch to threshold),\n * finishSessions (send challenge, collect proof shares). Mirrors Rust oprf-client sessions.\n */\n\nimport {\n DLogCommitmentsShamir,\n type PartialDLogCommitmentsShamir,\n type DLogProofShareShamir,\n} from '@taceo/oprf-core';\nimport { NodeError } from './errors.js';\nimport type { OprfRequest, OprfPublicKeyWithEpoch } from './types.js';\nimport { affineToWire, challengeToWire } from './types.js';\nimport { WebSocketSession } from './ws.js';\n\nexport interface OprfSessions {\n readonly ws: WebSocketSession[];\n readonly partyIds: number[];\n readonly commitments: PartialDLogCommitmentsShamir[];\n readonly oprfPublicKeys: AffinePointLike[];\n readonly epoch: number;\n}\n\n/** Internal: we only need key as affine for verification. */\ninterface AffinePointLike {\n x: bigint;\n y: bigint;\n}\n\nfunction oprfPublicKeyToAffine(k: OprfPublicKeyWithEpoch): AffinePointLike {\n return k.key;\n}\n\n/**\n * Open WebSockets to each service in parallel, send oprfRequest, collect OprfResponse.\n * Services must be pre-built WS URLs (see toOprfUri / toOprfUriMany).\n * Group by epoch; when an epoch has >= threshold responses with distinct party_id, return those sessions (sorted by party_id).\n * On failure: throws NodeError[] (caller wraps via aggregateError).\n */\nexport async function initSessions<Auth>(\n services: string[],\n threshold: number,\n oprfRequest: OprfRequest<Auth>\n): Promise<OprfSessions> {\n const requestWire = {\n request_id: oprfRequest.request_id,\n blinded_query: affineToWire(oprfRequest.blinded_query),\n auth: oprfRequest.auth,\n };\n\n const results = await Promise.allSettled(\n services.map(async (service) => {\n const session = await WebSocketSession.connect(service);\n await session.send(requestWire);\n const response = await session.readOprfResponse();\n return { session, response };\n })\n );\n\n const epochMap = new Map<\n number,\n {\n ws: WebSocketSession[];\n partyIds: number[];\n commitments: PartialDLogCommitmentsShamir[];\n oprfPublicKeys: AffinePointLike[];\n }\n >();\n const nodeErrors: NodeError[] = [];\n\n for (let i = 0; i < results.length; i++) {\n const r = results[i]!;\n if (r.status === 'rejected') {\n // NodeError thrown from ws methods; wrap unknown errors\n const err = r.reason;\n if (err && err instanceof NodeError) {\n nodeErrors.push(err);\n } else {\n nodeErrors.push(\n new NodeError('Unknown', {\n reason: String(err),\n cause: err instanceof Error ? err : undefined,\n })\n );\n }\n continue;\n }\n const { session, response } = r.value;\n const epoch = response.oprf_pub_key_with_epoch.epoch;\n let bucket = epochMap.get(epoch);\n if (!bucket) {\n bucket = { ws: [], partyIds: [], commitments: [], oprfPublicKeys: [] };\n epochMap.set(epoch, bucket);\n }\n if (bucket.partyIds.includes(response.party_id)) {\n session.close();\n continue;\n }\n bucket.ws.push(session);\n bucket.partyIds.push(response.party_id);\n bucket.commitments.push(response.commitments);\n bucket.oprfPublicKeys.push(\n oprfPublicKeyToAffine(response.oprf_pub_key_with_epoch)\n );\n\n if (bucket.ws.length >= threshold) {\n // close other sessions that we won't use\n for (let j = i + 1; j < results.length; j++) {\n const r2 = results[j];\n if (r2?.status === 'rejected') {\n continue;\n }\n r2?.value?.session.close();\n }\n break;\n }\n }\n\n for (const [epoch, bucket] of epochMap) {\n if (bucket.ws.length >= threshold) {\n for (const [otherEpoch, otherBucket] of epochMap) {\n if (otherEpoch !== epoch) {\n for (const ws of otherBucket.ws) ws.close();\n }\n }\n const order = bucket.partyIds\n .map((id, i) => ({ id, i }))\n .sort((a, b) => a.id - b.id);\n return {\n ws: order.map(({ i }) => bucket.ws[i]!),\n partyIds: order.map(({ id }) => id),\n commitments: order.map(({ i }) => bucket.commitments[i]!),\n oprfPublicKeys: order.map(({ i }) => bucket.oprfPublicKeys[i]!),\n epoch,\n };\n }\n }\n\n // Not enough responses — throw collected node errors for caller to aggregate\n throw nodeErrors;\n}\n\n/**\n * Send the same challenge to each session, read proof share, then close.\n * Returns proof shares in the same order as sessions.ws.\n * Errors bubble up as NodeError (thrown from ws methods).\n */\nexport async function finishSessions(\n sessions: OprfSessions,\n challenge: DLogCommitmentsShamir\n): Promise<DLogProofShareShamir[]> {\n const wire = challengeToWire(challenge.data);\n const results = await Promise.all(\n sessions.ws.map(async (session) => {\n await session.sendChallenge(wire);\n const share = await session.readProofShare();\n session.close();\n return { value: share.value };\n })\n );\n return results;\n}\n","/**\n * URI construction helpers for OPRF service endpoints.\n */\n\nimport { DEFAULT_CLIENT_VERSION } from './ws.js';\n\n/**\n * Build a WebSocket URL for a single OPRF service.\n * http → ws, https → wss. Appends /api/{auth_module_name}/oprf?version={clientVersion}.\n */\nexport function toOprfUri(\n service: string,\n auth_module_name: string,\n clientVersion: string = DEFAULT_CLIENT_VERSION\n): string {\n const base = service\n .replace(/^https:\\/\\//, 'wss://')\n .replace(/^http:\\/\\//, 'ws://')\n .replace(/\\/$/, '');\n return `${base}/api/${auth_module_name}/oprf?version=${clientVersion}`;\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { PartialDLogEqualityCommitments, PartialDLogCommitmentsShamir, DLogCommitmentsShamir, DLogProofShareShamir, DLogEqualityProof } from '@taceo/oprf-core';
1
+ import { PartialDLogEqualityCommitments, PartialDLogCommitmentsShamir, DLogCommitmentsShamir, DLogProofShareShamir, DLogEqualityProof, BlindingFactor } from '@taceo/oprf-core';
2
2
  import { AffinePoint } from '@noble/curves/abstract/curve.js';
3
3
 
4
4
  /**
@@ -134,15 +134,11 @@ declare function generateChallengeRequest(sessions: OprfSessions): DLogCommitmen
134
134
  * Combine proof shares, verify DLog proof. Throws OprfClientError if invalid.
135
135
  */
136
136
  declare function verifyDlogEquality(requestId: string, oprfPublicKey: AffinePoint<bigint>, blindedRequest: AffinePoint<bigint>, proofShares: DLogProofShareShamir[], challenge: DLogCommitmentsShamir): DLogEqualityProof;
137
- interface DistributedOprfOptions<Auth = unknown> {
138
- /** Auth payload sent in OprfRequest (must be JSON-serializable). */
139
- auth?: Auth;
140
- }
141
137
  /**
142
138
  * Full distributed OPRF: blind → init sessions → challenge → finish → verify → unblind → finalize.
143
139
  * Services must be pre-built WS URLs (use toOprfUri / toOprfUriMany).
144
140
  */
145
- declare function distributedOprf(services: string[], threshold: number, query: bigint, domainSeparator: bigint, options?: DistributedOprfOptions): Promise<VerifiableOprfOutput>;
141
+ declare function distributedOprf(services: string[], threshold: number, query: bigint, blindingFactor: BlindingFactor, domainSeparator: bigint, auth?: unknown): Promise<VerifiableOprfOutput>;
146
142
 
147
143
  /**
148
144
  * Client error types matching the Rust oprf-client Error enum.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { PartialDLogEqualityCommitments, PartialDLogCommitmentsShamir, DLogCommitmentsShamir, DLogProofShareShamir, DLogEqualityProof } from '@taceo/oprf-core';
1
+ import { PartialDLogEqualityCommitments, PartialDLogCommitmentsShamir, DLogCommitmentsShamir, DLogProofShareShamir, DLogEqualityProof, BlindingFactor } from '@taceo/oprf-core';
2
2
  import { AffinePoint } from '@noble/curves/abstract/curve.js';
3
3
 
4
4
  /**
@@ -134,15 +134,11 @@ declare function generateChallengeRequest(sessions: OprfSessions): DLogCommitmen
134
134
  * Combine proof shares, verify DLog proof. Throws OprfClientError if invalid.
135
135
  */
136
136
  declare function verifyDlogEquality(requestId: string, oprfPublicKey: AffinePoint<bigint>, blindedRequest: AffinePoint<bigint>, proofShares: DLogProofShareShamir[], challenge: DLogCommitmentsShamir): DLogEqualityProof;
137
- interface DistributedOprfOptions<Auth = unknown> {
138
- /** Auth payload sent in OprfRequest (must be JSON-serializable). */
139
- auth?: Auth;
140
- }
141
137
  /**
142
138
  * Full distributed OPRF: blind → init sessions → challenge → finish → verify → unblind → finalize.
143
139
  * Services must be pre-built WS URLs (use toOprfUri / toOprfUriMany).
144
140
  */
145
- declare function distributedOprf(services: string[], threshold: number, query: bigint, domainSeparator: bigint, options?: DistributedOprfOptions): Promise<VerifiableOprfOutput>;
141
+ declare function distributedOprf(services: string[], threshold: number, query: bigint, blindingFactor: BlindingFactor, domainSeparator: bigint, auth?: unknown): Promise<VerifiableOprfOutput>;
146
142
 
147
143
  /**
148
144
  * Client error types matching the Rust oprf-client Error enum.
package/dist/index.js CHANGED
@@ -3,12 +3,12 @@ import {
3
3
  blindQuery,
4
4
  unblindResponse,
5
5
  finalizeOutput,
6
- randomBlindingFactor,
7
6
  prepareBlindingFactor,
8
7
  DLogCommitmentsShamir,
9
8
  dlogEqualityVerify,
10
9
  BABYJUBJUB_SUBGROUP_GENERATOR_AFFINE
11
10
  } from "@taceo/oprf-core";
11
+ import { randomBytes } from "@noble/hashes/utils.js";
12
12
 
13
13
  // src/errors.ts
14
14
  var ServiceError = class _ServiceError extends Error {
@@ -185,7 +185,7 @@ var WebSocketSession = class _WebSocketSession {
185
185
  reject(
186
186
  new NodeError("WsError", {
187
187
  reason: `Failed to connect to ${url}`,
188
- cause: event instanceof ErrorEvent ? event.error : void 0
188
+ cause: typeof ErrorEvent !== "undefined" && event instanceof ErrorEvent ? event.error : void 0
189
189
  })
190
190
  );
191
191
  };
@@ -254,7 +254,7 @@ var WebSocketSession = class _WebSocketSession {
254
254
  reject(
255
255
  new NodeError("WsError", {
256
256
  reason: "WebSocket error",
257
- cause: event instanceof ErrorEvent ? event.error : void 0
257
+ cause: typeof ErrorEvent !== "undefined" && event instanceof ErrorEvent ? event.error : void 0
258
258
  })
259
259
  );
260
260
  };
@@ -432,14 +432,12 @@ function verifyDlogEquality(requestId, oprfPublicKey, blindedRequest, proofShare
432
432
  }
433
433
  return proof;
434
434
  }
435
- async function distributedOprf(services, threshold, query, domainSeparator, options = {}) {
435
+ async function distributedOprf(services, threshold, query, blindingFactor, domainSeparator, auth) {
436
436
  if (new Set(services).size !== services.length) {
437
437
  throw new OprfClientError("NonUniqueServices", "Services must be unique");
438
438
  }
439
- const blindingFactor = randomBlindingFactor();
440
439
  const blindedRequest = blindQuery(query, blindingFactor);
441
- const requestId = crypto.randomUUID();
442
- const auth = options.auth ?? {};
440
+ const requestId = generateRequestId();
443
441
  let sessions;
444
442
  try {
445
443
  sessions = await initSessions(services, threshold, {
@@ -498,6 +496,18 @@ async function distributedOprf(services, threshold, query, domainSeparator, opti
498
496
  epoch: sessions.epoch
499
497
  };
500
498
  }
499
+ function generateRequestId() {
500
+ if (typeof globalThis.crypto?.randomUUID === "function") {
501
+ return globalThis.crypto.randomUUID();
502
+ }
503
+ const bytes = randomBytes(16);
504
+ bytes[6] = bytes[6] & 15 | 64;
505
+ bytes[8] = bytes[8] & 63 | 128;
506
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join(
507
+ ""
508
+ );
509
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
510
+ }
501
511
 
502
512
  // src/uri.ts
503
513
  function toOprfUri(service, auth_module_name, clientVersion = DEFAULT_CLIENT_VERSION) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/errors.ts","../src/types.ts","../src/ws.ts","../src/sessions.ts","../src/uri.ts"],"sourcesContent":["/**\n * High-level client: generateChallengeRequest, verifyDlogEquality, distributedOprf.\n */\n\nimport {\n blindQuery,\n unblindResponse,\n finalizeOutput,\n randomBlindingFactor,\n prepareBlindingFactor,\n DLogCommitmentsShamir,\n dlogEqualityVerify,\n BABYJUBJUB_SUBGROUP_GENERATOR_AFFINE,\n type BlindingFactor,\n type DLogEqualityProof,\n type DLogProofShareShamir,\n} from '@taceo/oprf-core';\nimport type { AffinePoint } from '@noble/curves/abstract/curve.js';\nimport {\n OprfClientError,\n aggregateError,\n isNodeError,\n type NodeError,\n} from './errors.js';\nimport type { OprfSessions } from './sessions.js';\nimport { initSessions, finishSessions } from './sessions.js';\n\nexport type { OprfSessions };\n\nexport interface VerifiableOprfOutput {\n output: bigint;\n dlogProof: DLogEqualityProof;\n blindedRequest: AffinePoint<bigint>;\n blindedResponse: AffinePoint<bigint>;\n unblindedResponse: AffinePoint<bigint>;\n oprfPublicKey: AffinePoint<bigint>;\n epoch: number;\n}\n\n/**\n * Build challenge from sessions: contributingParties = party_id + 1 per party, then combineCommitments.\n */\nexport function generateChallengeRequest(\n sessions: OprfSessions\n): DLogCommitmentsShamir {\n const contributingParties = sessions.partyIds.map((id) => id + 1);\n return DLogCommitmentsShamir.combineCommitments(\n sessions.commitments,\n contributingParties\n );\n}\n\n/**\n * Combine proof shares, verify DLog proof. Throws OprfClientError if invalid.\n */\nexport function verifyDlogEquality(\n requestId: string,\n oprfPublicKey: AffinePoint<bigint>,\n blindedRequest: AffinePoint<bigint>,\n proofShares: DLogProofShareShamir[],\n challenge: DLogCommitmentsShamir\n): DLogEqualityProof {\n const blindedResponse = challenge.blindedResponse();\n const proof = challenge.combineProofs(\n requestId,\n proofShares,\n oprfPublicKey,\n blindedRequest\n );\n try {\n dlogEqualityVerify(\n proof,\n oprfPublicKey,\n blindedRequest,\n blindedResponse,\n BABYJUBJUB_SUBGROUP_GENERATOR_AFFINE\n );\n } catch {\n throw new OprfClientError(\n 'InvalidDLogProof',\n 'DLog proof could not be verified'\n );\n }\n return proof;\n}\n\nexport interface DistributedOprfOptions<Auth = unknown> {\n /** Auth payload sent in OprfRequest (must be JSON-serializable). */\n auth?: Auth;\n}\n\n/**\n * Full distributed OPRF: blind → init sessions → challenge → finish → verify → unblind → finalize.\n * Services must be pre-built WS URLs (use toOprfUri / toOprfUriMany).\n */\nexport async function distributedOprf(\n services: string[],\n threshold: number,\n query: bigint,\n domainSeparator: bigint,\n options: DistributedOprfOptions = {}\n): Promise<VerifiableOprfOutput> {\n if (new Set(services).size !== services.length) {\n throw new OprfClientError('NonUniqueServices', 'Services must be unique');\n }\n\n const blindingFactor: BlindingFactor = randomBlindingFactor();\n const blindedRequest = blindQuery(query, blindingFactor);\n\n const requestId = crypto.randomUUID();\n const auth = options.auth ?? ({} as unknown);\n\n let sessions: OprfSessions;\n try {\n sessions = await initSessions(services, threshold, {\n request_id: requestId,\n blinded_query: blindedRequest,\n auth,\n });\n } catch (err) {\n // initSessions throws NodeError[] on threshold failure\n if (Array.isArray(err) && err.every(isNodeError)) {\n throw aggregateError(threshold, err as NodeError[]);\n }\n throw err;\n }\n\n const firstKey = sessions.oprfPublicKeys[0]!;\n for (const key of sessions.oprfPublicKeys) {\n if (key.x !== firstKey.x || key.y !== firstKey.y) {\n throw new OprfClientError(\n 'InconsistentOprfPublicKeys',\n 'OPRF nodes returned different public keys'\n );\n }\n }\n const oprfPublicKey = firstKey;\n\n const challenge = generateChallengeRequest(sessions);\n\n let proofShares: DLogProofShareShamir[];\n try {\n proofShares = await finishSessions(sessions, challenge);\n } catch (err) {\n // finishSessions throws NodeError (from ws methods)\n if (isNodeError(err)) {\n throw new OprfClientError(\n 'CannotFinishSession',\n `Failed to finish session: ${err.message}`,\n { cause: err }\n );\n }\n throw err;\n }\n\n const dlogProof = verifyDlogEquality(\n requestId,\n oprfPublicKey,\n blindedRequest,\n proofShares,\n challenge\n );\n\n const blindedResponse = challenge.blindedResponse();\n const prepared = prepareBlindingFactor(blindingFactor);\n const unblindedResponse = unblindResponse(blindedResponse, prepared);\n const output = finalizeOutput(domainSeparator, query, unblindedResponse);\n\n return {\n output,\n dlogProof,\n blindedRequest,\n blindedResponse,\n unblindedResponse,\n oprfPublicKey,\n epoch: sessions.epoch,\n };\n}\n","/**\n * Client error types matching the Rust oprf-client Error enum.\n * Two-tier model: NodeError (per-node) and OprfClientError (protocol-level).\n */\n\n// ── ServiceError ─────────────────────────────────────────────────────────────\n\n/**\n * Application-level error received in a WebSocket close frame from a node.\n */\nexport class ServiceError extends Error {\n readonly errorCode: number;\n readonly msg?: string;\n\n constructor(errorCode: number, msg?: string) {\n super(msg ?? `Service error ${errorCode}`);\n this.name = 'ServiceError';\n this.errorCode = errorCode;\n this.msg = msg;\n Object.setPrototypeOf(this, ServiceError.prototype);\n }\n}\n\n// ── NodeError ─────────────────────────────────────────────────────────────────\n\nexport type NodeErrorCode =\n | 'ServiceError'\n | 'WsError'\n | 'UnexpectedMessage'\n | 'Unknown';\n\n/**\n * Per-node error, discriminated by `code`.\n */\nexport class NodeError extends Error {\n readonly code: NodeErrorCode;\n readonly reason?: string;\n readonly cause?: Error;\n readonly serviceError?: ServiceError;\n\n constructor(\n code: NodeErrorCode,\n opts: {\n reason?: string;\n cause?: Error;\n serviceError?: ServiceError;\n } = {}\n ) {\n super(opts.reason ?? opts.serviceError?.message ?? code);\n this.name = 'NodeError';\n this.code = code;\n this.reason = opts.reason;\n this.cause = opts.cause;\n this.serviceError = opts.serviceError;\n Object.setPrototypeOf(this, NodeError.prototype);\n }\n}\n\nexport function isNodeError(err: unknown): err is NodeError {\n return err instanceof NodeError;\n}\n\n// ── OprfClientError ───────────────────────────────────────────────────────────\n\nexport type OprfClientErrorCode =\n | 'NonUniqueServices'\n | 'InvalidDLogProof'\n | 'InconsistentOprfPublicKeys'\n | 'ThresholdServiceError'\n | 'Networking'\n | 'UnexpectedMessage'\n | 'CannotFinishSession'\n | 'NodeErrorDisagreement'\n | 'Unknown';\n\nexport interface OprfClientErrorDetails {\n nodeErrors?: NodeError[];\n serviceError?: ServiceError;\n cause?: NodeError;\n networkingErrors?: Error[];\n}\n\nexport class OprfClientError extends Error {\n readonly code: OprfClientErrorCode;\n readonly details?: OprfClientErrorDetails;\n\n constructor(\n code: OprfClientErrorCode,\n message: string,\n details?: OprfClientErrorDetails\n ) {\n super(message);\n this.name = 'OprfClientError';\n this.code = code;\n this.details = details;\n Object.setPrototypeOf(this, OprfClientError.prototype);\n }\n}\n\nexport function isOprfClientError(err: unknown): err is OprfClientError {\n return err instanceof OprfClientError;\n}\n\n// ── aggregateError ────────────────────────────────────────────────────────────\n\n/**\n * Aggregate per-node errors into a protocol-level OprfClientError.\n * Mirrors Rust oprf-client aggregate_error logic:\n * - >= threshold ServiceErrors with same code → ThresholdServiceError\n * - >= threshold UnexpectedMessage with same reason → UnexpectedMessage\n * - >= threshold WsErrors → Networking\n * - Otherwise → NodeErrorDisagreement\n */\nexport function aggregateError(\n threshold: number,\n errors: NodeError[]\n): OprfClientError {\n // Count ServiceError by errorCode\n const serviceErrorCounts = new Map<\n number,\n { count: number; err: ServiceError }\n >();\n for (const e of errors) {\n if (e.code === 'ServiceError' && e.serviceError) {\n const code = e.serviceError.errorCode;\n const existing = serviceErrorCounts.get(code);\n if (existing) {\n existing.count++;\n } else {\n serviceErrorCounts.set(code, { count: 1, err: e.serviceError });\n }\n }\n }\n for (const { count, err } of serviceErrorCounts.values()) {\n if (count >= threshold) {\n return new OprfClientError(\n 'ThresholdServiceError',\n `${count} nodes returned service error ${err.errorCode}: ${err.msg ?? ''}`.trim(),\n { serviceError: err }\n );\n }\n }\n\n // Count UnexpectedMessage by reason\n const unexpectedCounts = new Map<string, number>();\n for (const e of errors) {\n if (e.code === 'UnexpectedMessage') {\n const key = e.reason ?? '';\n unexpectedCounts.set(key, (unexpectedCounts.get(key) ?? 0) + 1);\n }\n }\n for (const [reason, count] of unexpectedCounts) {\n if (count >= threshold) {\n return new OprfClientError(\n 'UnexpectedMessage',\n `${count} nodes reported unexpected message: ${reason}`,\n { nodeErrors: errors }\n );\n }\n }\n\n // Count WsErrors\n const wsErrors = errors.filter((e) => e.code === 'WsError');\n if (wsErrors.length >= threshold) {\n return new OprfClientError(\n 'Networking',\n `${wsErrors.length} nodes had networking errors`,\n { networkingErrors: wsErrors.map((e) => e.cause ?? e) }\n );\n }\n\n // Fallback: disagreement\n return new OprfClientError(\n 'NodeErrorDisagreement',\n 'Nodes returned disagreeing errors',\n { nodeErrors: errors }\n );\n}\n","/**\n * Wire and API types for OPRF client.\n * Matches nullifier-oracle-service oprf-types (OprfRequest, OprfResponse, etc.).\n */\n\nimport type { AffinePoint } from '@noble/curves/abstract/curve.js';\nimport type { PartialDLogEqualityCommitments } from '@taceo/oprf-core';\n\n/**\n * Affine point as JSON: a 2-element array [x, y] of decimal strings.\n * Matches Rust ark_serde_compat::babyjubjub::serialize_affine / deserialize_affine.\n */\nexport type AffinePointWire = [string, string];\n\n/** OPRF public key with epoch (wire shape). */\nexport interface OprfPublicKeyWithEpochWire {\n readonly key: AffinePointWire;\n readonly epoch: number;\n}\n\n/** Client request sent to server (wire shape). */\nexport interface OprfRequestWire<Auth = unknown> {\n readonly request_id: string;\n readonly blinded_query: AffinePointWire;\n readonly auth: Auth;\n}\n\n/** Server response (wire shape). */\nexport interface OprfResponseWire {\n readonly commitments: PartialDLogEqualityCommitmentsWire;\n readonly party_id: number;\n readonly oprf_pub_key_with_epoch: OprfPublicKeyWithEpochWire;\n}\n\n/** Per-party commitments (wire shape; points as [x, y] string tuples). */\nexport interface PartialDLogEqualityCommitmentsWire {\n readonly c: AffinePointWire;\n readonly d1: AffinePointWire;\n readonly d2: AffinePointWire;\n readonly e1: AffinePointWire;\n readonly e2: AffinePointWire;\n}\n\n/**\n * DLog proof share (wire: transparent scalar string).\n * Matches Rust DLogProofShareShamir which is #[serde(transparent)] over a scalar field string.\n */\nexport type DLogProofShareShamirWire = string;\n\n/** DLogCommitmentsShamir wire (server expects snake_case: contributing_parties). */\nexport interface DLogCommitmentsShamirWire {\n c: AffinePointWire;\n d1: AffinePointWire;\n d2: AffinePointWire;\n e1: AffinePointWire;\n e2: AffinePointWire;\n contributing_parties: number[];\n}\n\n/** OprfPublicKeyWithEpoch (internal: affine points). */\nexport interface OprfPublicKeyWithEpoch {\n key: AffinePoint<bigint>;\n epoch: number;\n}\n\n/** OprfRequest (internal: affine for blinded_query). */\nexport interface OprfRequest<Auth = unknown> {\n request_id: string;\n blinded_query: AffinePoint<bigint>;\n auth: Auth;\n}\n\n/** OprfResponse (internal: affine points). */\nexport interface OprfResponse {\n commitments: PartialDLogEqualityCommitments;\n party_id: number;\n oprf_pub_key_with_epoch: OprfPublicKeyWithEpoch;\n}\n\nexport function wireToAffine(w: AffinePointWire): AffinePoint<bigint> {\n return { x: BigInt(w[0]), y: BigInt(w[1]) };\n}\n\nexport function affineToWire(p: AffinePoint<bigint>): AffinePointWire {\n return [p.x.toString(), p.y.toString()];\n}\n\nexport function wireToCommitments(\n w: PartialDLogEqualityCommitmentsWire\n): PartialDLogEqualityCommitments {\n return {\n c: wireToAffine(w.c),\n d1: wireToAffine(w.d1),\n d2: wireToAffine(w.d2),\n e1: wireToAffine(w.e1),\n e2: wireToAffine(w.e2),\n };\n}\n\nexport function commitmentsToWire(\n c: PartialDLogEqualityCommitments\n): PartialDLogEqualityCommitmentsWire {\n return {\n c: affineToWire(c.c),\n d1: affineToWire(c.d1),\n d2: affineToWire(c.d2),\n e1: affineToWire(c.e1),\n e2: affineToWire(c.e2),\n };\n}\n\nexport function wireToOprfResponse(w: OprfResponseWire): OprfResponse {\n return {\n commitments: wireToCommitments(w.commitments),\n party_id: w.party_id,\n oprf_pub_key_with_epoch: {\n key: wireToAffine(w.oprf_pub_key_with_epoch.key),\n epoch: w.oprf_pub_key_with_epoch.epoch,\n },\n };\n}\n\nexport function wireToProofShare(w: DLogProofShareShamirWire): {\n value: bigint;\n} {\n return { value: BigInt(w) };\n}\n\nexport function proofShareToWire(p: {\n value: bigint;\n}): DLogProofShareShamirWire {\n return p.value.toString();\n}\n\n/** Convert DLogCommitmentsShamir (data) to wire shape for server. */\nexport function challengeToWire(data: {\n c: AffinePoint<bigint>;\n d1: AffinePoint<bigint>;\n d2: AffinePoint<bigint>;\n e1: AffinePoint<bigint>;\n e2: AffinePoint<bigint>;\n contributingParties: number[];\n}): DLogCommitmentsShamirWire {\n return {\n c: affineToWire(data.c),\n d1: affineToWire(data.d1),\n d2: affineToWire(data.d2),\n e1: affineToWire(data.e1),\n e2: affineToWire(data.e2),\n contributing_parties: data.contributingParties,\n };\n}\n","/**\n * WebSocket session for a single OPRF service connection.\n * Takes a pre-built WS URL (see uri.ts). Errors are NodeError variants.\n */\n\nimport { NodeError, ServiceError } from './errors.js';\nimport type { OprfResponseWire } from './types.js';\nimport { wireToOprfResponse } from './types.js';\nimport type { OprfResponse } from './types.js';\nimport type { DLogCommitmentsShamirWire } from './types.js';\nimport { wireToProofShare, type DLogProofShareShamirWire } from './types.js';\n\n/** Default protocol version sent to server (query param; browser cannot set headers). */\nexport const DEFAULT_CLIENT_VERSION = '0.8.0';\n\n/**\n * Thin WebSocket session: connect, send JSON, receive JSON, handle close.\n * Uses text (JSON) frames. Browser WebSocket cannot set headers; version is sent as query param.\n * All errors are thrown as NodeError.\n */\nexport class WebSocketSession {\n private ws: WebSocket;\n private readonly service: string;\n\n private constructor(ws: WebSocket, service: string) {\n this.ws = ws;\n this.service = service;\n }\n\n get serviceUrl(): string {\n return this.service;\n }\n\n /**\n * Open a new WebSocket to the given pre-built WS URL.\n * Connect errors throw NodeError('WsError', ...).\n */\n static connect(url: string): Promise<WebSocketSession> {\n return new Promise((resolve, reject) => {\n let ws: WebSocket;\n try {\n ws = new WebSocket(url);\n } catch (err) {\n reject(\n new NodeError('WsError', {\n reason: `Failed to construct WebSocket for ${url}`,\n cause: err instanceof Error ? err : undefined,\n })\n );\n return;\n }\n ws.binaryType = 'arraybuffer';\n ws.onopen = () => {\n resolve(new WebSocketSession(ws, url));\n };\n ws.onerror = (event) => {\n reject(\n new NodeError('WsError', {\n reason: `Failed to connect to ${url}`,\n cause: event instanceof ErrorEvent ? event.error : undefined,\n })\n );\n };\n });\n }\n\n /** Send a JSON-serializable message as text frame. Throws NodeError('WsError') on failure. */\n send<T extends object>(msg: T): void {\n if (this.ws.readyState !== WebSocket.OPEN) {\n throw new NodeError('WsError', { reason: 'WebSocket not open' });\n }\n try {\n this.ws.send(JSON.stringify(msg));\n } catch (err) {\n throw new NodeError('WsError', {\n reason: 'Failed to send message',\n cause: err instanceof Error ? err : undefined,\n });\n }\n }\n\n /**\n * Read next message. Returns text data.\n * Rejects with NodeError on:\n * - Binary frame → UnexpectedMessage\n * - Non-normal close → ServiceError\n * - Normal/1005 close → UnexpectedMessage('Server closed websocket')\n * - Error event → WsError\n * - null/end → UnexpectedMessage('Server closed connection')\n */\n private readNext(): Promise<string> {\n return new Promise((resolve, reject) => {\n const onMessage = (event: MessageEvent) => {\n cleanup();\n if (typeof event.data === 'string') {\n resolve(event.data);\n } else {\n reject(\n new NodeError('UnexpectedMessage', {\n reason: 'binary frame received',\n })\n );\n }\n };\n const onClose = (event: CloseEvent) => {\n cleanup();\n if (event.code !== 1000 && event.code !== 1005) {\n const svcErr = new ServiceError(\n event.code,\n event.reason || undefined\n );\n reject(\n new NodeError('ServiceError', {\n reason: svcErr.message,\n serviceError: svcErr,\n })\n );\n } else {\n reject(\n new NodeError('UnexpectedMessage', {\n reason: 'Server closed websocket',\n })\n );\n }\n };\n const onError = (event: Event) => {\n cleanup();\n reject(\n new NodeError('WsError', {\n reason: 'WebSocket error',\n cause: event instanceof ErrorEvent ? event.error : undefined,\n })\n );\n };\n const cleanup = () => {\n this.ws.removeEventListener('message', onMessage);\n this.ws.removeEventListener('close', onClose);\n this.ws.removeEventListener('error', onError);\n };\n this.ws.addEventListener('message', onMessage);\n this.ws.addEventListener('close', onClose);\n this.ws.addEventListener('error', onError);\n });\n }\n\n /** Read and parse OprfResponse. Parse errors → NodeError('UnexpectedMessage'). */\n async readOprfResponse(): Promise<OprfResponse> {\n const raw = await this.readNext();\n try {\n const w = JSON.parse(raw) as OprfResponseWire;\n return wireToOprfResponse(w);\n } catch {\n throw new NodeError('UnexpectedMessage', {\n reason: 'Invalid OprfResponse JSON',\n });\n }\n }\n\n /** Read and parse DLogProofShareShamir. Parse errors → NodeError('UnexpectedMessage'). */\n async readProofShare(): Promise<{ value: bigint }> {\n const raw = await this.readNext();\n try {\n const w = JSON.parse(raw) as DLogProofShareShamirWire;\n return wireToProofShare(w);\n } catch {\n throw new NodeError('UnexpectedMessage', {\n reason: 'Invalid proof share JSON',\n });\n }\n }\n\n /** Send challenge (DLogCommitmentsShamir wire). */\n sendChallenge(wire: DLogCommitmentsShamirWire): void {\n this.send(wire);\n }\n\n /** Gracefully close with normal code. */\n close(): void {\n if (this.ws.readyState === WebSocket.OPEN) {\n this.ws.close(1000, 'success');\n }\n }\n}\n","/**\n * Session management: initSessions (parallel connect, collect by epoch to threshold),\n * finishSessions (send challenge, collect proof shares). Mirrors Rust oprf-client sessions.\n */\n\nimport {\n DLogCommitmentsShamir,\n type PartialDLogCommitmentsShamir,\n type DLogProofShareShamir,\n} from '@taceo/oprf-core';\nimport { NodeError } from './errors.js';\nimport type { OprfRequest, OprfPublicKeyWithEpoch } from './types.js';\nimport { affineToWire, challengeToWire } from './types.js';\nimport { WebSocketSession } from './ws.js';\n\nexport interface OprfSessions {\n readonly ws: WebSocketSession[];\n readonly partyIds: number[];\n readonly commitments: PartialDLogCommitmentsShamir[];\n readonly oprfPublicKeys: AffinePointLike[];\n readonly epoch: number;\n}\n\n/** Internal: we only need key as affine for verification. */\ninterface AffinePointLike {\n x: bigint;\n y: bigint;\n}\n\nfunction oprfPublicKeyToAffine(k: OprfPublicKeyWithEpoch): AffinePointLike {\n return k.key;\n}\n\n/**\n * Open WebSockets to each service in parallel, send oprfRequest, collect OprfResponse.\n * Services must be pre-built WS URLs (see toOprfUri / toOprfUriMany).\n * Group by epoch; when an epoch has >= threshold responses with distinct party_id, return those sessions (sorted by party_id).\n * On failure: throws NodeError[] (caller wraps via aggregateError).\n */\nexport async function initSessions<Auth>(\n services: string[],\n threshold: number,\n oprfRequest: OprfRequest<Auth>\n): Promise<OprfSessions> {\n const requestWire = {\n request_id: oprfRequest.request_id,\n blinded_query: affineToWire(oprfRequest.blinded_query),\n auth: oprfRequest.auth,\n };\n\n const results = await Promise.allSettled(\n services.map(async (service) => {\n const session = await WebSocketSession.connect(service);\n await session.send(requestWire);\n const response = await session.readOprfResponse();\n return { session, response };\n })\n );\n\n const epochMap = new Map<\n number,\n {\n ws: WebSocketSession[];\n partyIds: number[];\n commitments: PartialDLogCommitmentsShamir[];\n oprfPublicKeys: AffinePointLike[];\n }\n >();\n const nodeErrors: NodeError[] = [];\n\n for (let i = 0; i < results.length; i++) {\n const r = results[i]!;\n if (r.status === 'rejected') {\n // NodeError thrown from ws methods; wrap unknown errors\n const err = r.reason;\n if (err && err instanceof NodeError) {\n nodeErrors.push(err);\n } else {\n nodeErrors.push(\n new NodeError('Unknown', {\n reason: String(err),\n cause: err instanceof Error ? err : undefined,\n })\n );\n }\n continue;\n }\n const { session, response } = r.value;\n const epoch = response.oprf_pub_key_with_epoch.epoch;\n let bucket = epochMap.get(epoch);\n if (!bucket) {\n bucket = { ws: [], partyIds: [], commitments: [], oprfPublicKeys: [] };\n epochMap.set(epoch, bucket);\n }\n if (bucket.partyIds.includes(response.party_id)) {\n session.close();\n continue;\n }\n bucket.ws.push(session);\n bucket.partyIds.push(response.party_id);\n bucket.commitments.push(response.commitments);\n bucket.oprfPublicKeys.push(\n oprfPublicKeyToAffine(response.oprf_pub_key_with_epoch)\n );\n\n if (bucket.ws.length >= threshold) {\n // close other sessions that we won't use\n for (let j = i + 1; j < results.length; j++) {\n const r2 = results[j];\n if (r2?.status === 'rejected') {\n continue;\n }\n r2?.value?.session.close();\n }\n break;\n }\n }\n\n for (const [epoch, bucket] of epochMap) {\n if (bucket.ws.length >= threshold) {\n for (const [otherEpoch, otherBucket] of epochMap) {\n if (otherEpoch !== epoch) {\n for (const ws of otherBucket.ws) ws.close();\n }\n }\n const order = bucket.partyIds\n .map((id, i) => ({ id, i }))\n .sort((a, b) => a.id - b.id);\n return {\n ws: order.map(({ i }) => bucket.ws[i]!),\n partyIds: order.map(({ id }) => id),\n commitments: order.map(({ i }) => bucket.commitments[i]!),\n oprfPublicKeys: order.map(({ i }) => bucket.oprfPublicKeys[i]!),\n epoch,\n };\n }\n }\n\n // Not enough responses — throw collected node errors for caller to aggregate\n throw nodeErrors;\n}\n\n/**\n * Send the same challenge to each session, read proof share, then close.\n * Returns proof shares in the same order as sessions.ws.\n * Errors bubble up as NodeError (thrown from ws methods).\n */\nexport async function finishSessions(\n sessions: OprfSessions,\n challenge: DLogCommitmentsShamir\n): Promise<DLogProofShareShamir[]> {\n const wire = challengeToWire(challenge.data);\n const results = await Promise.all(\n sessions.ws.map(async (session) => {\n await session.sendChallenge(wire);\n const share = await session.readProofShare();\n session.close();\n return { value: share.value };\n })\n );\n return results;\n}\n","/**\n * URI construction helpers for OPRF service endpoints.\n */\n\nimport { DEFAULT_CLIENT_VERSION } from './ws.js';\n\n/**\n * Build a WebSocket URL for a single OPRF service.\n * http → ws, https → wss. Appends /api/{auth_module_name}/oprf?version={clientVersion}.\n */\nexport function toOprfUri(\n service: string,\n auth_module_name: string,\n clientVersion: string = DEFAULT_CLIENT_VERSION\n): string {\n const base = service\n .replace(/^https:\\/\\//, 'wss://')\n .replace(/^http:\\/\\//, 'ws://')\n .replace(/\\/$/, '');\n return `${base}/api/${auth_module_name}/oprf?version=${clientVersion}`;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/client.ts","../src/errors.ts","../src/types.ts","../src/ws.ts","../src/sessions.ts","../src/uri.ts"],"sourcesContent":["/**\n * High-level client: generateChallengeRequest, verifyDlogEquality, distributedOprf.\n */\n\nimport {\n blindQuery,\n unblindResponse,\n finalizeOutput,\n prepareBlindingFactor,\n DLogCommitmentsShamir,\n dlogEqualityVerify,\n BABYJUBJUB_SUBGROUP_GENERATOR_AFFINE,\n type BlindingFactor,\n type DLogEqualityProof,\n type DLogProofShareShamir,\n} from '@taceo/oprf-core';\nimport { randomBytes } from '@noble/hashes/utils.js';\nimport type { AffinePoint } from '@noble/curves/abstract/curve.js';\nimport {\n OprfClientError,\n aggregateError,\n isNodeError,\n type NodeError,\n} from './errors.js';\nimport type { OprfSessions } from './sessions.js';\nimport { initSessions, finishSessions } from './sessions.js';\n\nexport type { OprfSessions };\n\nexport interface VerifiableOprfOutput {\n output: bigint;\n dlogProof: DLogEqualityProof;\n blindedRequest: AffinePoint<bigint>;\n blindedResponse: AffinePoint<bigint>;\n unblindedResponse: AffinePoint<bigint>;\n oprfPublicKey: AffinePoint<bigint>;\n epoch: number;\n}\n\n/**\n * Build challenge from sessions: contributingParties = party_id + 1 per party, then combineCommitments.\n */\nexport function generateChallengeRequest(\n sessions: OprfSessions\n): DLogCommitmentsShamir {\n const contributingParties = sessions.partyIds.map((id) => id + 1);\n return DLogCommitmentsShamir.combineCommitments(\n sessions.commitments,\n contributingParties\n );\n}\n\n/**\n * Combine proof shares, verify DLog proof. Throws OprfClientError if invalid.\n */\nexport function verifyDlogEquality(\n requestId: string,\n oprfPublicKey: AffinePoint<bigint>,\n blindedRequest: AffinePoint<bigint>,\n proofShares: DLogProofShareShamir[],\n challenge: DLogCommitmentsShamir\n): DLogEqualityProof {\n const blindedResponse = challenge.blindedResponse();\n const proof = challenge.combineProofs(\n requestId,\n proofShares,\n oprfPublicKey,\n blindedRequest\n );\n try {\n dlogEqualityVerify(\n proof,\n oprfPublicKey,\n blindedRequest,\n blindedResponse,\n BABYJUBJUB_SUBGROUP_GENERATOR_AFFINE\n );\n } catch {\n throw new OprfClientError(\n 'InvalidDLogProof',\n 'DLog proof could not be verified'\n );\n }\n return proof;\n}\n\n/**\n * Full distributed OPRF: blind → init sessions → challenge → finish → verify → unblind → finalize.\n * Services must be pre-built WS URLs (use toOprfUri / toOprfUriMany).\n */\nexport async function distributedOprf(\n services: string[],\n threshold: number,\n query: bigint,\n blindingFactor: BlindingFactor,\n domainSeparator: bigint,\n auth?: unknown\n): Promise<VerifiableOprfOutput> {\n if (new Set(services).size !== services.length) {\n throw new OprfClientError('NonUniqueServices', 'Services must be unique');\n }\n\n const blindedRequest = blindQuery(query, blindingFactor);\n const requestId = generateRequestId();\n\n let sessions: OprfSessions;\n try {\n sessions = await initSessions(services, threshold, {\n request_id: requestId,\n blinded_query: blindedRequest,\n auth,\n });\n } catch (err) {\n // initSessions throws NodeError[] on threshold failure\n if (Array.isArray(err) && err.every(isNodeError)) {\n throw aggregateError(threshold, err as NodeError[]);\n }\n throw err;\n }\n\n const firstKey = sessions.oprfPublicKeys[0]!;\n for (const key of sessions.oprfPublicKeys) {\n if (key.x !== firstKey.x || key.y !== firstKey.y) {\n throw new OprfClientError(\n 'InconsistentOprfPublicKeys',\n 'OPRF nodes returned different public keys'\n );\n }\n }\n const oprfPublicKey = firstKey;\n\n const challenge = generateChallengeRequest(sessions);\n\n let proofShares: DLogProofShareShamir[];\n try {\n proofShares = await finishSessions(sessions, challenge);\n } catch (err) {\n // finishSessions throws NodeError (from ws methods)\n if (isNodeError(err)) {\n throw new OprfClientError(\n 'CannotFinishSession',\n `Failed to finish session: ${err.message}`,\n { cause: err }\n );\n }\n throw err;\n }\n\n const dlogProof = verifyDlogEquality(\n requestId,\n oprfPublicKey,\n blindedRequest,\n proofShares,\n challenge\n );\n\n const blindedResponse = challenge.blindedResponse();\n const prepared = prepareBlindingFactor(blindingFactor);\n const unblindedResponse = unblindResponse(blindedResponse, prepared);\n const output = finalizeOutput(domainSeparator, query, unblindedResponse);\n\n return {\n output,\n dlogProof,\n blindedRequest,\n blindedResponse,\n unblindedResponse,\n oprfPublicKey,\n epoch: sessions.epoch,\n };\n}\n\nfunction generateRequestId(): string {\n if (typeof globalThis.crypto?.randomUUID === 'function') {\n return globalThis.crypto.randomUUID();\n }\n // Fallback for environments without randomUUID\n const bytes = randomBytes(16);\n // Set version (4) and variant (RFC 4122)\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join(\n ''\n );\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n","/**\n * Client error types matching the Rust oprf-client Error enum.\n * Two-tier model: NodeError (per-node) and OprfClientError (protocol-level).\n */\n\n// ── ServiceError ─────────────────────────────────────────────────────────────\n\n/**\n * Application-level error received in a WebSocket close frame from a node.\n */\nexport class ServiceError extends Error {\n readonly errorCode: number;\n readonly msg?: string;\n\n constructor(errorCode: number, msg?: string) {\n super(msg ?? `Service error ${errorCode}`);\n this.name = 'ServiceError';\n this.errorCode = errorCode;\n this.msg = msg;\n Object.setPrototypeOf(this, ServiceError.prototype);\n }\n}\n\n// ── NodeError ─────────────────────────────────────────────────────────────────\n\nexport type NodeErrorCode =\n | 'ServiceError'\n | 'WsError'\n | 'UnexpectedMessage'\n | 'Unknown';\n\n/**\n * Per-node error, discriminated by `code`.\n */\nexport class NodeError extends Error {\n readonly code: NodeErrorCode;\n readonly reason?: string;\n readonly cause?: Error;\n readonly serviceError?: ServiceError;\n\n constructor(\n code: NodeErrorCode,\n opts: {\n reason?: string;\n cause?: Error;\n serviceError?: ServiceError;\n } = {}\n ) {\n super(opts.reason ?? opts.serviceError?.message ?? code);\n this.name = 'NodeError';\n this.code = code;\n this.reason = opts.reason;\n this.cause = opts.cause;\n this.serviceError = opts.serviceError;\n Object.setPrototypeOf(this, NodeError.prototype);\n }\n}\n\nexport function isNodeError(err: unknown): err is NodeError {\n return err instanceof NodeError;\n}\n\n// ── OprfClientError ───────────────────────────────────────────────────────────\n\nexport type OprfClientErrorCode =\n | 'NonUniqueServices'\n | 'InvalidDLogProof'\n | 'InconsistentOprfPublicKeys'\n | 'ThresholdServiceError'\n | 'Networking'\n | 'UnexpectedMessage'\n | 'CannotFinishSession'\n | 'NodeErrorDisagreement'\n | 'Unknown';\n\nexport interface OprfClientErrorDetails {\n nodeErrors?: NodeError[];\n serviceError?: ServiceError;\n cause?: NodeError;\n networkingErrors?: Error[];\n}\n\nexport class OprfClientError extends Error {\n readonly code: OprfClientErrorCode;\n readonly details?: OprfClientErrorDetails;\n\n constructor(\n code: OprfClientErrorCode,\n message: string,\n details?: OprfClientErrorDetails\n ) {\n super(message);\n this.name = 'OprfClientError';\n this.code = code;\n this.details = details;\n Object.setPrototypeOf(this, OprfClientError.prototype);\n }\n}\n\nexport function isOprfClientError(err: unknown): err is OprfClientError {\n return err instanceof OprfClientError;\n}\n\n// ── aggregateError ────────────────────────────────────────────────────────────\n\n/**\n * Aggregate per-node errors into a protocol-level OprfClientError.\n * Mirrors Rust oprf-client aggregate_error logic:\n * - >= threshold ServiceErrors with same code → ThresholdServiceError\n * - >= threshold UnexpectedMessage with same reason → UnexpectedMessage\n * - >= threshold WsErrors → Networking\n * - Otherwise → NodeErrorDisagreement\n */\nexport function aggregateError(\n threshold: number,\n errors: NodeError[]\n): OprfClientError {\n // Count ServiceError by errorCode\n const serviceErrorCounts = new Map<\n number,\n { count: number; err: ServiceError }\n >();\n for (const e of errors) {\n if (e.code === 'ServiceError' && e.serviceError) {\n const code = e.serviceError.errorCode;\n const existing = serviceErrorCounts.get(code);\n if (existing) {\n existing.count++;\n } else {\n serviceErrorCounts.set(code, { count: 1, err: e.serviceError });\n }\n }\n }\n for (const { count, err } of serviceErrorCounts.values()) {\n if (count >= threshold) {\n return new OprfClientError(\n 'ThresholdServiceError',\n `${count} nodes returned service error ${err.errorCode}: ${err.msg ?? ''}`.trim(),\n { serviceError: err }\n );\n }\n }\n\n // Count UnexpectedMessage by reason\n const unexpectedCounts = new Map<string, number>();\n for (const e of errors) {\n if (e.code === 'UnexpectedMessage') {\n const key = e.reason ?? '';\n unexpectedCounts.set(key, (unexpectedCounts.get(key) ?? 0) + 1);\n }\n }\n for (const [reason, count] of unexpectedCounts) {\n if (count >= threshold) {\n return new OprfClientError(\n 'UnexpectedMessage',\n `${count} nodes reported unexpected message: ${reason}`,\n { nodeErrors: errors }\n );\n }\n }\n\n // Count WsErrors\n const wsErrors = errors.filter((e) => e.code === 'WsError');\n if (wsErrors.length >= threshold) {\n return new OprfClientError(\n 'Networking',\n `${wsErrors.length} nodes had networking errors`,\n { networkingErrors: wsErrors.map((e) => e.cause ?? e) }\n );\n }\n\n // Fallback: disagreement\n return new OprfClientError(\n 'NodeErrorDisagreement',\n 'Nodes returned disagreeing errors',\n { nodeErrors: errors }\n );\n}\n","/**\n * Wire and API types for OPRF client.\n * Matches nullifier-oracle-service oprf-types (OprfRequest, OprfResponse, etc.).\n */\n\nimport type { AffinePoint } from '@noble/curves/abstract/curve.js';\nimport type { PartialDLogEqualityCommitments } from '@taceo/oprf-core';\n\n/**\n * Affine point as JSON: a 2-element array [x, y] of decimal strings.\n * Matches Rust ark_serde_compat::babyjubjub::serialize_affine / deserialize_affine.\n */\nexport type AffinePointWire = [string, string];\n\n/** OPRF public key with epoch (wire shape). */\nexport interface OprfPublicKeyWithEpochWire {\n readonly key: AffinePointWire;\n readonly epoch: number;\n}\n\n/** Client request sent to server (wire shape). */\nexport interface OprfRequestWire<Auth = unknown> {\n readonly request_id: string;\n readonly blinded_query: AffinePointWire;\n readonly auth: Auth;\n}\n\n/** Server response (wire shape). */\nexport interface OprfResponseWire {\n readonly commitments: PartialDLogEqualityCommitmentsWire;\n readonly party_id: number;\n readonly oprf_pub_key_with_epoch: OprfPublicKeyWithEpochWire;\n}\n\n/** Per-party commitments (wire shape; points as [x, y] string tuples). */\nexport interface PartialDLogEqualityCommitmentsWire {\n readonly c: AffinePointWire;\n readonly d1: AffinePointWire;\n readonly d2: AffinePointWire;\n readonly e1: AffinePointWire;\n readonly e2: AffinePointWire;\n}\n\n/**\n * DLog proof share (wire: transparent scalar string).\n * Matches Rust DLogProofShareShamir which is #[serde(transparent)] over a scalar field string.\n */\nexport type DLogProofShareShamirWire = string;\n\n/** DLogCommitmentsShamir wire (server expects snake_case: contributing_parties). */\nexport interface DLogCommitmentsShamirWire {\n c: AffinePointWire;\n d1: AffinePointWire;\n d2: AffinePointWire;\n e1: AffinePointWire;\n e2: AffinePointWire;\n contributing_parties: number[];\n}\n\n/** OprfPublicKeyWithEpoch (internal: affine points). */\nexport interface OprfPublicKeyWithEpoch {\n key: AffinePoint<bigint>;\n epoch: number;\n}\n\n/** OprfRequest (internal: affine for blinded_query). */\nexport interface OprfRequest<Auth = unknown> {\n request_id: string;\n blinded_query: AffinePoint<bigint>;\n auth: Auth;\n}\n\n/** OprfResponse (internal: affine points). */\nexport interface OprfResponse {\n commitments: PartialDLogEqualityCommitments;\n party_id: number;\n oprf_pub_key_with_epoch: OprfPublicKeyWithEpoch;\n}\n\nexport function wireToAffine(w: AffinePointWire): AffinePoint<bigint> {\n return { x: BigInt(w[0]), y: BigInt(w[1]) };\n}\n\nexport function affineToWire(p: AffinePoint<bigint>): AffinePointWire {\n return [p.x.toString(), p.y.toString()];\n}\n\nexport function wireToCommitments(\n w: PartialDLogEqualityCommitmentsWire\n): PartialDLogEqualityCommitments {\n return {\n c: wireToAffine(w.c),\n d1: wireToAffine(w.d1),\n d2: wireToAffine(w.d2),\n e1: wireToAffine(w.e1),\n e2: wireToAffine(w.e2),\n };\n}\n\nexport function commitmentsToWire(\n c: PartialDLogEqualityCommitments\n): PartialDLogEqualityCommitmentsWire {\n return {\n c: affineToWire(c.c),\n d1: affineToWire(c.d1),\n d2: affineToWire(c.d2),\n e1: affineToWire(c.e1),\n e2: affineToWire(c.e2),\n };\n}\n\nexport function wireToOprfResponse(w: OprfResponseWire): OprfResponse {\n return {\n commitments: wireToCommitments(w.commitments),\n party_id: w.party_id,\n oprf_pub_key_with_epoch: {\n key: wireToAffine(w.oprf_pub_key_with_epoch.key),\n epoch: w.oprf_pub_key_with_epoch.epoch,\n },\n };\n}\n\nexport function wireToProofShare(w: DLogProofShareShamirWire): {\n value: bigint;\n} {\n return { value: BigInt(w) };\n}\n\nexport function proofShareToWire(p: {\n value: bigint;\n}): DLogProofShareShamirWire {\n return p.value.toString();\n}\n\n/** Convert DLogCommitmentsShamir (data) to wire shape for server. */\nexport function challengeToWire(data: {\n c: AffinePoint<bigint>;\n d1: AffinePoint<bigint>;\n d2: AffinePoint<bigint>;\n e1: AffinePoint<bigint>;\n e2: AffinePoint<bigint>;\n contributingParties: number[];\n}): DLogCommitmentsShamirWire {\n return {\n c: affineToWire(data.c),\n d1: affineToWire(data.d1),\n d2: affineToWire(data.d2),\n e1: affineToWire(data.e1),\n e2: affineToWire(data.e2),\n contributing_parties: data.contributingParties,\n };\n}\n","/**\n * WebSocket session for a single OPRF service connection.\n * Takes a pre-built WS URL (see uri.ts). Errors are NodeError variants.\n */\n\nimport { NodeError, ServiceError } from './errors.js';\nimport type { OprfResponseWire } from './types.js';\nimport { wireToOprfResponse } from './types.js';\nimport type { OprfResponse } from './types.js';\nimport type { DLogCommitmentsShamirWire } from './types.js';\nimport { wireToProofShare, type DLogProofShareShamirWire } from './types.js';\n\n/** Default protocol version sent to server (query param; browser cannot set headers). */\nexport const DEFAULT_CLIENT_VERSION = '0.8.0';\n\n/**\n * Thin WebSocket session: connect, send JSON, receive JSON, handle close.\n * Uses text (JSON) frames. Browser WebSocket cannot set headers; version is sent as query param.\n * All errors are thrown as NodeError.\n */\nexport class WebSocketSession {\n private ws: WebSocket;\n private readonly service: string;\n\n private constructor(ws: WebSocket, service: string) {\n this.ws = ws;\n this.service = service;\n }\n\n get serviceUrl(): string {\n return this.service;\n }\n\n /**\n * Open a new WebSocket to the given pre-built WS URL.\n * Connect errors throw NodeError('WsError', ...).\n */\n static connect(url: string): Promise<WebSocketSession> {\n return new Promise((resolve, reject) => {\n let ws: WebSocket;\n try {\n ws = new WebSocket(url);\n } catch (err) {\n reject(\n new NodeError('WsError', {\n reason: `Failed to construct WebSocket for ${url}`,\n cause: err instanceof Error ? err : undefined,\n })\n );\n return;\n }\n ws.binaryType = 'arraybuffer';\n ws.onopen = () => {\n resolve(new WebSocketSession(ws, url));\n };\n ws.onerror = (event) => {\n reject(\n new NodeError('WsError', {\n reason: `Failed to connect to ${url}`,\n cause:\n typeof ErrorEvent !== 'undefined' && event instanceof ErrorEvent\n ? event.error\n : undefined,\n })\n );\n };\n });\n }\n\n /** Send a JSON-serializable message as text frame. Throws NodeError('WsError') on failure. */\n send<T extends object>(msg: T): void {\n if (this.ws.readyState !== WebSocket.OPEN) {\n throw new NodeError('WsError', { reason: 'WebSocket not open' });\n }\n try {\n this.ws.send(JSON.stringify(msg));\n } catch (err) {\n throw new NodeError('WsError', {\n reason: 'Failed to send message',\n cause: err instanceof Error ? err : undefined,\n });\n }\n }\n\n /**\n * Read next message. Returns text data.\n * Rejects with NodeError on:\n * - Binary frame → UnexpectedMessage\n * - Non-normal close → ServiceError\n * - Normal/1005 close → UnexpectedMessage('Server closed websocket')\n * - Error event → WsError\n * - null/end → UnexpectedMessage('Server closed connection')\n */\n private readNext(): Promise<string> {\n return new Promise((resolve, reject) => {\n const onMessage = (event: MessageEvent) => {\n cleanup();\n if (typeof event.data === 'string') {\n resolve(event.data);\n } else {\n reject(\n new NodeError('UnexpectedMessage', {\n reason: 'binary frame received',\n })\n );\n }\n };\n const onClose = (event: CloseEvent) => {\n cleanup();\n if (event.code !== 1000 && event.code !== 1005) {\n const svcErr = new ServiceError(\n event.code,\n event.reason || undefined\n );\n reject(\n new NodeError('ServiceError', {\n reason: svcErr.message,\n serviceError: svcErr,\n })\n );\n } else {\n reject(\n new NodeError('UnexpectedMessage', {\n reason: 'Server closed websocket',\n })\n );\n }\n };\n const onError = (event: Event) => {\n cleanup();\n reject(\n new NodeError('WsError', {\n reason: 'WebSocket error',\n cause:\n typeof ErrorEvent !== 'undefined' && event instanceof ErrorEvent\n ? event.error\n : undefined,\n })\n );\n };\n const cleanup = () => {\n this.ws.removeEventListener('message', onMessage);\n this.ws.removeEventListener('close', onClose);\n this.ws.removeEventListener('error', onError);\n };\n this.ws.addEventListener('message', onMessage);\n this.ws.addEventListener('close', onClose);\n this.ws.addEventListener('error', onError);\n });\n }\n\n /** Read and parse OprfResponse. Parse errors → NodeError('UnexpectedMessage'). */\n async readOprfResponse(): Promise<OprfResponse> {\n const raw = await this.readNext();\n try {\n const w = JSON.parse(raw) as OprfResponseWire;\n return wireToOprfResponse(w);\n } catch {\n throw new NodeError('UnexpectedMessage', {\n reason: 'Invalid OprfResponse JSON',\n });\n }\n }\n\n /** Read and parse DLogProofShareShamir. Parse errors → NodeError('UnexpectedMessage'). */\n async readProofShare(): Promise<{ value: bigint }> {\n const raw = await this.readNext();\n try {\n const w = JSON.parse(raw) as DLogProofShareShamirWire;\n return wireToProofShare(w);\n } catch {\n throw new NodeError('UnexpectedMessage', {\n reason: 'Invalid proof share JSON',\n });\n }\n }\n\n /** Send challenge (DLogCommitmentsShamir wire). */\n sendChallenge(wire: DLogCommitmentsShamirWire): void {\n this.send(wire);\n }\n\n /** Gracefully close with normal code. */\n close(): void {\n if (this.ws.readyState === WebSocket.OPEN) {\n this.ws.close(1000, 'success');\n }\n }\n}\n","/**\n * Session management: initSessions (parallel connect, collect by epoch to threshold),\n * finishSessions (send challenge, collect proof shares). Mirrors Rust oprf-client sessions.\n */\n\nimport {\n DLogCommitmentsShamir,\n type PartialDLogCommitmentsShamir,\n type DLogProofShareShamir,\n} from '@taceo/oprf-core';\nimport { NodeError } from './errors.js';\nimport type { OprfRequest, OprfPublicKeyWithEpoch } from './types.js';\nimport { affineToWire, challengeToWire } from './types.js';\nimport { WebSocketSession } from './ws.js';\n\nexport interface OprfSessions {\n readonly ws: WebSocketSession[];\n readonly partyIds: number[];\n readonly commitments: PartialDLogCommitmentsShamir[];\n readonly oprfPublicKeys: AffinePointLike[];\n readonly epoch: number;\n}\n\n/** Internal: we only need key as affine for verification. */\ninterface AffinePointLike {\n x: bigint;\n y: bigint;\n}\n\nfunction oprfPublicKeyToAffine(k: OprfPublicKeyWithEpoch): AffinePointLike {\n return k.key;\n}\n\n/**\n * Open WebSockets to each service in parallel, send oprfRequest, collect OprfResponse.\n * Services must be pre-built WS URLs (see toOprfUri / toOprfUriMany).\n * Group by epoch; when an epoch has >= threshold responses with distinct party_id, return those sessions (sorted by party_id).\n * On failure: throws NodeError[] (caller wraps via aggregateError).\n */\nexport async function initSessions<Auth>(\n services: string[],\n threshold: number,\n oprfRequest: OprfRequest<Auth>\n): Promise<OprfSessions> {\n const requestWire = {\n request_id: oprfRequest.request_id,\n blinded_query: affineToWire(oprfRequest.blinded_query),\n auth: oprfRequest.auth,\n };\n\n const results = await Promise.allSettled(\n services.map(async (service) => {\n const session = await WebSocketSession.connect(service);\n await session.send(requestWire);\n const response = await session.readOprfResponse();\n return { session, response };\n })\n );\n\n const epochMap = new Map<\n number,\n {\n ws: WebSocketSession[];\n partyIds: number[];\n commitments: PartialDLogCommitmentsShamir[];\n oprfPublicKeys: AffinePointLike[];\n }\n >();\n const nodeErrors: NodeError[] = [];\n\n for (let i = 0; i < results.length; i++) {\n const r = results[i]!;\n if (r.status === 'rejected') {\n // NodeError thrown from ws methods; wrap unknown errors\n const err = r.reason;\n if (err && err instanceof NodeError) {\n nodeErrors.push(err);\n } else {\n nodeErrors.push(\n new NodeError('Unknown', {\n reason: String(err),\n cause: err instanceof Error ? err : undefined,\n })\n );\n }\n continue;\n }\n const { session, response } = r.value;\n const epoch = response.oprf_pub_key_with_epoch.epoch;\n let bucket = epochMap.get(epoch);\n if (!bucket) {\n bucket = { ws: [], partyIds: [], commitments: [], oprfPublicKeys: [] };\n epochMap.set(epoch, bucket);\n }\n if (bucket.partyIds.includes(response.party_id)) {\n session.close();\n continue;\n }\n bucket.ws.push(session);\n bucket.partyIds.push(response.party_id);\n bucket.commitments.push(response.commitments);\n bucket.oprfPublicKeys.push(\n oprfPublicKeyToAffine(response.oprf_pub_key_with_epoch)\n );\n\n if (bucket.ws.length >= threshold) {\n // close other sessions that we won't use\n for (let j = i + 1; j < results.length; j++) {\n const r2 = results[j];\n if (r2?.status === 'rejected') {\n continue;\n }\n r2?.value?.session.close();\n }\n break;\n }\n }\n\n for (const [epoch, bucket] of epochMap) {\n if (bucket.ws.length >= threshold) {\n for (const [otherEpoch, otherBucket] of epochMap) {\n if (otherEpoch !== epoch) {\n for (const ws of otherBucket.ws) ws.close();\n }\n }\n const order = bucket.partyIds\n .map((id, i) => ({ id, i }))\n .sort((a, b) => a.id - b.id);\n return {\n ws: order.map(({ i }) => bucket.ws[i]!),\n partyIds: order.map(({ id }) => id),\n commitments: order.map(({ i }) => bucket.commitments[i]!),\n oprfPublicKeys: order.map(({ i }) => bucket.oprfPublicKeys[i]!),\n epoch,\n };\n }\n }\n\n // Not enough responses — throw collected node errors for caller to aggregate\n throw nodeErrors;\n}\n\n/**\n * Send the same challenge to each session, read proof share, then close.\n * Returns proof shares in the same order as sessions.ws.\n * Errors bubble up as NodeError (thrown from ws methods).\n */\nexport async function finishSessions(\n sessions: OprfSessions,\n challenge: DLogCommitmentsShamir\n): Promise<DLogProofShareShamir[]> {\n const wire = challengeToWire(challenge.data);\n const results = await Promise.all(\n sessions.ws.map(async (session) => {\n await session.sendChallenge(wire);\n const share = await session.readProofShare();\n session.close();\n return { value: share.value };\n })\n );\n return results;\n}\n","/**\n * URI construction helpers for OPRF service endpoints.\n */\n\nimport { DEFAULT_CLIENT_VERSION } from './ws.js';\n\n/**\n * Build a WebSocket URL for a single OPRF service.\n * http → ws, https → wss. Appends /api/{auth_module_name}/oprf?version={clientVersion}.\n */\nexport function toOprfUri(\n service: string,\n auth_module_name: string,\n clientVersion: string = DEFAULT_CLIENT_VERSION\n): string {\n const base = service\n .replace(/^https:\\/\\//, 'wss://')\n .replace(/^http:\\/\\//, 'ws://')\n .replace(/\\/$/, '');\n return `${base}/api/${auth_module_name}/oprf?version=${clientVersion}`;\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taceo/oprf-client",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "WebSocket client for distributed threshold OPRF over a network of service nodes",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -45,6 +45,7 @@
45
45
  ],
46
46
  "dependencies": {
47
47
  "@noble/curves": "^2.0.1",
48
+ "@noble/hashes": "^2.0.1",
48
49
  "@taceo/oprf-core": "0.4.0"
49
50
  },
50
51
  "devDependencies": {