@dynamic-labs-wallet/forward-mpc-client 0.10.0 → 0.10.1

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.
@@ -166,14 +166,23 @@ interface ForwardMpcErrorClassification {
166
166
  * }
167
167
  * }
168
168
  * ```
169
+ *
170
+ * `shouldFallback` is `true` for every Forward MPC failure mode. The
171
+ * relay-based MPC path is a fully functional safety net; treating any
172
+ * Forward MPC failure as terminal (refusing to fall back) blocks user
173
+ * wallet operations end-to-end on transient Forward MPC degradation.
169
174
  */
170
175
  declare function classifyForwardMpcError(error: unknown): ForwardMpcErrorClassification;
171
176
  /**
172
177
  * Type guard to check if an error is a Forward MPC error that should be handled.
178
+ * Matches by both `instanceof` and `.name` to survive multiple bundled
179
+ * copies of this package in the same JS realm.
173
180
  */
174
181
  declare function isForwardMpcError(error: unknown): error is ForwardMPCError;
175
182
  /**
176
183
  * Type guard to check if an error is an attestation failure.
184
+ * Matches by both `instanceof` and `.name` to survive multiple bundled
185
+ * copies of this package in the same JS realm.
177
186
  */
178
187
  declare function isAttestationError(error: unknown): error is SessionAttestationError;
179
188
 
@@ -166,14 +166,23 @@ interface ForwardMpcErrorClassification {
166
166
  * }
167
167
  * }
168
168
  * ```
169
+ *
170
+ * `shouldFallback` is `true` for every Forward MPC failure mode. The
171
+ * relay-based MPC path is a fully functional safety net; treating any
172
+ * Forward MPC failure as terminal (refusing to fall back) blocks user
173
+ * wallet operations end-to-end on transient Forward MPC degradation.
169
174
  */
170
175
  declare function classifyForwardMpcError(error: unknown): ForwardMpcErrorClassification;
171
176
  /**
172
177
  * Type guard to check if an error is a Forward MPC error that should be handled.
178
+ * Matches by both `instanceof` and `.name` to survive multiple bundled
179
+ * copies of this package in the same JS realm.
173
180
  */
174
181
  declare function isForwardMpcError(error: unknown): error is ForwardMPCError;
175
182
  /**
176
183
  * Type guard to check if an error is an attestation failure.
184
+ * Matches by both `instanceof` and `.name` to survive multiple bundled
185
+ * copies of this package in the same JS realm.
177
186
  */
178
187
  declare function isAttestationError(error: unknown): error is SessionAttestationError;
179
188
 
package/dist/utils.cjs CHANGED
@@ -63,10 +63,39 @@ var SessionRequestTimeoutError = class extends SessionError {
63
63
  super("Request timed out waiting for server response", ErrorCode.REQUEST_TIMEOUT, context);
64
64
  }
65
65
  };
66
+ var FORWARD_MPC_ERROR_NAMES = /* @__PURE__ */ new Set([
67
+ "TransportConnectionError",
68
+ "TransportConnectionTimeoutError",
69
+ "TransportNotConnectedError",
70
+ "SessionHandshakeError",
71
+ "SessionHandshakeInvalidResponseError",
72
+ "SessionAttestationError",
73
+ "SessionRequestTimeoutError",
74
+ "SessionDisposedError",
75
+ "SessionServerError",
76
+ "SessionMessageParseError",
77
+ "SessionRemoteError",
78
+ "ClientUnsupportedAlgorithmError",
79
+ "ClientSessionEstablishFailedError"
80
+ ]);
66
81
 
67
82
  // src/client-v2/error-classification.ts
83
+ function matchesForwardMpcErrorClass(error, ctor, name) {
84
+ if (error instanceof ctor) {
85
+ return true;
86
+ }
87
+ return error instanceof Error && error.name === name && typeof error.code === "string";
88
+ }
89
+ __name(matchesForwardMpcErrorClass, "matchesForwardMpcErrorClass");
90
+ function matchesAnyForwardMpcError(error) {
91
+ if (error instanceof ForwardMPCError) {
92
+ return true;
93
+ }
94
+ return error instanceof Error && FORWARD_MPC_ERROR_NAMES.has(error.name) && typeof error.code === "string";
95
+ }
96
+ __name(matchesAnyForwardMpcError, "matchesAnyForwardMpcError");
68
97
  function classifyForwardMpcError(error) {
69
- if (error instanceof SessionAttestationError) {
98
+ if (matchesForwardMpcErrorClass(error, SessionAttestationError, "SessionAttestationError")) {
70
99
  return {
71
100
  errorType: "ATTESTATION_FAILURE",
72
101
  errorCode: error.code,
@@ -76,22 +105,22 @@ function classifyForwardMpcError(error) {
76
105
  shouldFallback: true
77
106
  };
78
107
  }
79
- if (error instanceof SessionRequestTimeoutError) {
108
+ if (matchesForwardMpcErrorClass(error, SessionRequestTimeoutError, "SessionRequestTimeoutError")) {
80
109
  return {
81
110
  errorType: "FORWARD_MPC_TIMEOUT",
82
111
  errorCode: error.code,
83
112
  errorMessage: error.message,
84
113
  sessionEstablished: true,
85
- shouldFallback: false
114
+ shouldFallback: true
86
115
  };
87
116
  }
88
- if (error instanceof ForwardMPCError) {
117
+ if (matchesAnyForwardMpcError(error)) {
89
118
  return {
90
119
  errorType: "FORWARD_MPC_ERROR",
91
120
  errorCode: error.code,
92
121
  errorMessage: error.message,
93
122
  sessionEstablished: true,
94
- shouldFallback: false
123
+ shouldFallback: true
95
124
  };
96
125
  }
97
126
  return {
@@ -99,16 +128,16 @@ function classifyForwardMpcError(error) {
99
128
  errorCode: void 0,
100
129
  errorMessage: error instanceof Error ? error.message : String(error),
101
130
  sessionEstablished: false,
102
- shouldFallback: false
131
+ shouldFallback: true
103
132
  };
104
133
  }
105
134
  __name(classifyForwardMpcError, "classifyForwardMpcError");
106
135
  function isForwardMpcError(error) {
107
- return error instanceof ForwardMPCError;
136
+ return matchesAnyForwardMpcError(error);
108
137
  }
109
138
  __name(isForwardMpcError, "isForwardMpcError");
110
139
  function isAttestationError(error) {
111
- return error instanceof SessionAttestationError;
140
+ return matchesForwardMpcErrorClass(error, SessionAttestationError, "SessionAttestationError");
112
141
  }
113
142
  __name(isAttestationError, "isAttestationError");
114
143
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client-v2/errors.ts","../src/client-v2/error-classification.ts"],"names":["ErrorCode","ATTESTATION_FAILED","REQUEST_TIMEOUT","ForwardMPCErrorType","SESSION","ForwardMPCError","Error","code","type","context","message","name","Object","setPrototypeOf","prototype","toJSON","stack","SessionError","SessionAttestationError","cause","undefined","SessionRequestTimeoutError","classifyForwardMpcError","error","errorType","errorCode","errorMessage","attestationErrors","sessionEstablished","shouldFallback","String","isForwardMpcError","isAttestationError"],"mappings":";;;;;;AAIO,IAAMA,SAAAA,GAAY;EAQvBC,kBAAAA,EAAoB,oBAAA;EAMpBC,eAAAA,EAAiB,iBAOnB,CAAA;AA4BO,IAAMC,mBAAAA,GAAsB;EAEjCC,OAAAA,EAAS,SAEX,CAAA;AAWO,IAAeC,eAAAA,GAAf,cAAuCC,KAAAA,CAAAA;EAlE9C;;;AAmEkBC,EAAAA,IAAAA;AACAC,EAAAA,IAAAA;AACAC,EAAAA,OAAAA;EAEhB,WAAA,CACEC,OAAAA,EACAH,IAAAA,EACAC,IAAAA,EACAC,OAAAA,EACA;AACA,IAAA,KAAA,CAAMC,OAAAA,CAAAA;AACN,IAAA,IAAA,CAAKC,IAAAA,GAAO,KAAK,WAAA,CAAYA,IAAAA;AAC7B,IAAA,IAAA,CAAKJ,IAAAA,GAAOA,IAAAA;AACZ,IAAA,IAAA,CAAKC,IAAAA,GAAOA,IAAAA;AACZ,IAAA,IAAA,CAAKC,OAAAA,GAAUA,OAAAA;AACfG,IAAAA,MAAAA,CAAOC,cAAAA,CAAe,IAAA,EAAM,GAAA,CAAA,MAAA,CAAWC,SAAS,CAAA;AAClD,EAAA;EAEAC,MAAAA,GAAkC;AAChC,IAAA,OAAO;AACLJ,MAAAA,IAAAA,EAAM,IAAA,CAAKA,IAAAA;AACXD,MAAAA,OAAAA,EAAS,IAAA,CAAKA,OAAAA;AACdH,MAAAA,IAAAA,EAAM,IAAA,CAAKA,IAAAA;AACXC,MAAAA,IAAAA,EAAM,IAAA,CAAKA,IAAAA;AACXQ,MAAAA,KAAAA,EAAO,IAAA,CAAKA,KAAAA;AACZP,MAAAA,OAAAA,EAAS,IAAA,CAAKA;AAChB,KAAA;AACF,EAAA;AACF,CAAA;AAgBO,IAAeQ,YAAAA,GAAf,cAAoCZ,eAAAA,CAAAA;EA/G3C;;;EAgHE,WAAA,CACEK,OAAAA,EACAH,MACAE,OAAAA,EACA;AACA,IAAA,KAAA,CAAMC,OAAAA,EAASH,IAAAA,EAAMJ,mBAAAA,CAAoBC,OAAAA,EAASK,OAAAA,CAAAA;AACpD,EAAA;AACF,CAAA;AAgEO,IAAMS,uBAAAA,GAAN,cAAsCD,YAAAA,CAAAA;EAvL7C;;;AAwLkBE,EAAAA,KAAAA;AAEhB,EAAA,WAAA,CACET,UAAkB,iCAAA,EAClBH,IAAAA,GAAkBP,SAAAA,CAAUC,kBAAAA,EAC5BQ,SACAU,KAAAA,EACA;AACA,IAAA,KAAA,CAAMT,OAAAA,EAASH,MAAME,OAAAA,CAAAA;AACrB,IAAA,IAAIU,UAAUC,MAAAA,EAAW;AACvB,MAAA,IAAA,CAAKD,KAAAA,GAAQA,KAAAA;AACf,IAAA;AACF,EAAA;AACF,CAAA;AAEO,IAAME,0BAAAA,GAAN,cAAyCJ,YAAAA,CAAAA;EAvMhD;;;AAwME,EAAA,WAAA,CAAYR,OAAAA,EAAmC;AAC7C,IAAA,KAAA,CACE,+CAAA,EACAT,SAAAA,CAAUE,eAAAA,EACVO,OAAAA,CAAAA;AAEJ,EAAA;AACF,CAAA;;;ACvJO,SAASa,wBACdC,KAAAA,EAAc;AAEd,EAAA,IAAIA,iBAAiBL,uBAAAA,EAAyB;AAC5C,IAAA,OAAO;MACLM,SAAAA,EAAW,qBAAA;AACXC,MAAAA,SAAAA,EAAWF,KAAAA,CAAMhB,IAAAA;AACjBmB,MAAAA,YAAAA,EAAcH,KAAAA,CAAMb,OAAAA;MACpBiB,iBAAAA,EAAmBJ,KAAAA,CAAMd,UAAU,QAAA,CAAA;MACnCmB,kBAAAA,EAAoB,KAAA;MACpBC,cAAAA,EAAgB;AAClB,KAAA;AACF,EAAA;AAEA,EAAA,IAAIN,iBAAiBF,0BAAAA,EAA4B;AAC/C,IAAA,OAAO;MACLG,SAAAA,EAAW,qBAAA;AACXC,MAAAA,SAAAA,EAAWF,KAAAA,CAAMhB,IAAAA;AACjBmB,MAAAA,YAAAA,EAAcH,KAAAA,CAAMb,OAAAA;MACpBkB,kBAAAA,EAAoB,IAAA;MACpBC,cAAAA,EAAgB;AAClB,KAAA;AACF,EAAA;AAEA,EAAA,IAAIN,iBAAiBlB,eAAAA,EAAiB;AACpC,IAAA,OAAO;MACLmB,SAAAA,EAAW,mBAAA;AACXC,MAAAA,SAAAA,EAAWF,KAAAA,CAAMhB,IAAAA;AACjBmB,MAAAA,YAAAA,EAAcH,KAAAA,CAAMb,OAAAA;MACpBkB,kBAAAA,EAAoB,IAAA;MACpBC,cAAAA,EAAgB;AAClB,KAAA;AACF,EAAA;AAGA,EAAA,OAAO;IACLL,SAAAA,EAAW,mBAAA;IACXC,SAAAA,EAAWL,MAAAA;AACXM,IAAAA,YAAAA,EAAcH,KAAAA,YAAiBjB,KAAAA,GAAQiB,KAAAA,CAAMb,OAAAA,GAAUoB,OAAOP,KAAAA,CAAAA;IAC9DK,kBAAAA,EAAoB,KAAA;IACpBC,cAAAA,EAAgB;AAClB,GAAA;AACF;AA1CgBP,MAAAA,CAAAA,uBAAAA,EAAAA,yBAAAA,CAAAA;AA+CT,SAASS,kBAAkBR,KAAAA,EAAc;AAC9C,EAAA,OAAOA,KAAAA,YAAiBlB,eAAAA;AAC1B;AAFgB0B,MAAAA,CAAAA,iBAAAA,EAAAA,mBAAAA,CAAAA;AAOT,SAASC,mBACdT,KAAAA,EAAc;AAEd,EAAA,OAAOA,KAAAA,YAAiBL,uBAAAA;AAC1B;AAJgBc,MAAAA,CAAAA,kBAAAA,EAAAA,oBAAAA,CAAAA","file":"utils.cjs","sourcesContent":["import { type WebSocketError } from '@dynamic-labs-wallet/forward-mpc-shared';\n\n// ─── Error codes ──────────────────────────────────────────────────────────────\n\nexport const ErrorCode = {\n // Transport\n CONNECTION_FAILED: 'CONNECTION_FAILED',\n CONNECTION_TIMEOUT: 'CONNECTION_TIMEOUT',\n NOT_CONNECTED: 'NOT_CONNECTED',\n // Session\n HANDSHAKE_FAILED: 'HANDSHAKE_FAILED',\n HANDSHAKE_INVALID_RESPONSE: 'HANDSHAKE_INVALID_RESPONSE',\n ATTESTATION_FAILED: 'ATTESTATION_FAILED',\n ATTESTATION_PCR_MISMATCH: 'ATTESTATION_PCR_MISMATCH',\n ATTESTATION_CHALLENGE_MISMATCH: 'ATTESTATION_CHALLENGE_MISMATCH',\n ATTESTATION_NONCE_MISMATCH: 'ATTESTATION_NONCE_MISMATCH',\n ATTESTATION_NONCE_MISSING: 'ATTESTATION_NONCE_MISSING',\n ATTESTATION_DOCUMENT_MISSING: 'ATTESTATION_DOCUMENT_MISSING',\n REQUEST_TIMEOUT: 'REQUEST_TIMEOUT',\n SESSION_DISPOSED: 'SESSION_DISPOSED',\n SERVER_ERROR: 'SERVER_ERROR',\n MESSAGE_PARSE_FAILED: 'MESSAGE_PARSE_FAILED',\n // Client\n SESSION_ESTABLISH_FAILED: 'SESSION_ESTABLISH_FAILED',\n UNSUPPORTED_ALGORITHM: 'UNSUPPORTED_ALGORITHM',\n} as const;\n\nexport type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];\n\n/**\n * Focused subset of ErrorCode for attestation verification failures.\n * Use with `error.code` to distinguish failure modes on SessionAttestationError.\n */\nexport const AttestationErrorCode = {\n /** Generic / unrecognised attestation failure */\n FAILED: ErrorCode.ATTESTATION_FAILED,\n /** PCR8 hash mismatch — enclave measurement changed */\n PCR_MISMATCH: ErrorCode.ATTESTATION_PCR_MISMATCH,\n /** Challenge / ciphertext binding mismatch */\n CHALLENGE_MISMATCH: ErrorCode.ATTESTATION_CHALLENGE_MISMATCH,\n /** Nonce value mismatch — possible tampering */\n NONCE_MISMATCH: ErrorCode.ATTESTATION_NONCE_MISMATCH,\n /** Nonce field missing from attestation document */\n NONCE_MISSING: ErrorCode.ATTESTATION_NONCE_MISSING,\n /** Server did not return an attestation document */\n DOCUMENT_MISSING: ErrorCode.ATTESTATION_DOCUMENT_MISSING,\n} as const;\n\nexport type AttestationErrorCode =\n (typeof AttestationErrorCode)[keyof typeof AttestationErrorCode];\n\n// ─── Error types ──────────────────────────────────────────────────────────────\n\nexport const ForwardMPCErrorType = {\n TRANSPORT: 'transport',\n SESSION: 'session',\n CLIENT: 'client',\n} as const;\n\nexport type ForwardMPCErrorType =\n (typeof ForwardMPCErrorType)[keyof typeof ForwardMPCErrorType];\n\n// ─── Root base class ──────────────────────────────────────────────────────────\n\n/**\n * Abstract root for all Forward MPC errors.\n * `instanceof ForwardMPCError` is true for every error thrown by this library.\n */\nexport abstract class ForwardMPCError extends Error {\n public readonly code: ErrorCode;\n public readonly type: ForwardMPCErrorType;\n public readonly context?: Record<string, unknown>;\n\n constructor(\n message: string,\n code: ErrorCode,\n type: ForwardMPCErrorType,\n context?: Record<string, unknown>\n ) {\n super(message);\n this.name = this.constructor.name;\n this.code = code;\n this.type = type;\n this.context = context;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n type: this.type,\n stack: this.stack,\n context: this.context,\n };\n }\n}\n\n// ─── Layer base classes ───────────────────────────────────────────────────────\n\n/** Abstract base for errors originating from the WebSocket / transport layer. */\nexport abstract class TransportError extends ForwardMPCError {\n constructor(\n message: string,\n code: ErrorCode,\n context?: Record<string, unknown>\n ) {\n super(message, code, ForwardMPCErrorType.TRANSPORT, context);\n }\n}\n\n/** Abstract base for errors originating from the session / crypto / protocol layer. */\nexport abstract class SessionError extends ForwardMPCError {\n constructor(\n message: string,\n code: ErrorCode,\n context?: Record<string, unknown>\n ) {\n super(message, code, ForwardMPCErrorType.SESSION, context);\n }\n}\n\n/** Abstract base for errors originating from the client / application layer. */\nexport abstract class ClientError extends ForwardMPCError {\n constructor(\n message: string,\n code: ErrorCode,\n context?: Record<string, unknown>\n ) {\n super(message, code, ForwardMPCErrorType.CLIENT, context);\n }\n}\n\n// ─── Transport errors ─────────────────────────────────────────────────────────\n\nexport class TransportConnectionError extends TransportError {\n constructor(context?: Record<string, unknown>) {\n super('WebSocket connection failed', ErrorCode.CONNECTION_FAILED, context);\n }\n}\n\nexport class TransportConnectionTimeoutError extends TransportError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'WebSocket connection timed out',\n ErrorCode.CONNECTION_TIMEOUT,\n context\n );\n }\n}\n\nexport class TransportNotConnectedError extends TransportError {\n constructor(context?: Record<string, unknown>) {\n super('WebSocket is not connected', ErrorCode.NOT_CONNECTED, context);\n }\n}\n\n// ─── Session errors ───────────────────────────────────────────────────────────\n\nexport class SessionHandshakeError extends SessionError {\n constructor(reason: string, context?: Record<string, unknown>) {\n super(\n `ML-KEM-768 handshake failed: ${reason}`,\n ErrorCode.HANDSHAKE_FAILED,\n { reason, ...context }\n );\n }\n}\n\nexport class SessionHandshakeInvalidResponseError extends SessionError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'Handshake response was invalid or incomplete',\n ErrorCode.HANDSHAKE_INVALID_RESPONSE,\n context\n );\n }\n}\n\n/**\n * Attestation verification failure.\n * Use `error.code` to distinguish failure reasons (e.g. ATTESTATION_PCR_MISMATCH)\n * and `error.cause` to inspect the original verifier error.\n */\nexport class SessionAttestationError extends SessionError {\n public readonly cause?: unknown;\n\n constructor(\n message: string = 'Attestation verification failed',\n code: ErrorCode = ErrorCode.ATTESTATION_FAILED,\n context?: Record<string, unknown>,\n cause?: unknown\n ) {\n super(message, code, context);\n if (cause !== undefined) {\n this.cause = cause;\n }\n }\n}\n\nexport class SessionRequestTimeoutError extends SessionError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'Request timed out waiting for server response',\n ErrorCode.REQUEST_TIMEOUT,\n context\n );\n }\n}\n\nexport class SessionDisposedError extends SessionError {\n constructor(context?: Record<string, unknown>) {\n super('Session has been disposed', ErrorCode.SESSION_DISPOSED, context);\n }\n}\n\nexport class SessionServerError extends SessionError {\n constructor(reason: string, context?: Record<string, unknown>) {\n super(\n `Server returned an error response: ${reason}`,\n ErrorCode.SERVER_ERROR,\n { reason, ...context }\n );\n }\n}\n\nexport class SessionMessageParseError extends SessionError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'Failed to parse server message',\n ErrorCode.MESSAGE_PARSE_FAILED,\n context\n );\n }\n}\n\n/**\n * The remote server returned an explicit error response.\n * Carries the full WebSocketError payload so callers can inspect\n * `serverError.type` and `serverError.details`.\n */\nexport class SessionRemoteError extends SessionError {\n constructor(\n public readonly serverError: WebSocketError,\n context?: Record<string, unknown>\n ) {\n super(serverError.message, ErrorCode.SERVER_ERROR, context);\n }\n}\n\n// ─── Client errors ────────────────────────────────────────────────────────────\n\nexport class ClientUnsupportedAlgorithmError extends ClientError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'Signing algorithm is not supported',\n ErrorCode.UNSUPPORTED_ALGORITHM,\n context\n );\n }\n}\n\nexport class ClientSessionEstablishFailedError extends ClientError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'Failed to establish session',\n ErrorCode.SESSION_ESTABLISH_FAILED,\n context\n );\n }\n}\n","import {\n ForwardMPCError,\n SessionAttestationError,\n SessionRequestTimeoutError,\n} from './errors';\n\n/**\n * Error classification result from Forward MPC operations.\n */\nexport type ForwardMpcErrorType =\n | 'ATTESTATION_FAILURE'\n | 'FORWARD_MPC_TIMEOUT'\n | 'FORWARD_MPC_ERROR';\n\n/**\n * Result of classifying a Forward MPC error.\n */\nexport interface ForwardMpcErrorClassification {\n /** The type of error encountered */\n errorType: ForwardMpcErrorType;\n /** Error code from ForwardMPCError, if available */\n errorCode: string | undefined;\n /** Error message */\n errorMessage: string;\n /** Attestation verification errors, if this is an attestation failure */\n attestationErrors?: unknown[];\n /** Whether the session was established before the error occurred */\n sessionEstablished: boolean;\n /** Whether this error should trigger a fallback to relay-based MPC */\n shouldFallback: boolean;\n}\n\n/**\n * Classifies a Forward MPC error and returns structured data for logging.\n * Use this to standardize error handling across keygen, signing, and connect operations.\n *\n * @param error - The error to classify\n * @returns Classification result with error details and recommended action\n *\n * @example\n * ```typescript\n * try {\n * await forwardMpcClient.sign(...);\n * } catch (error) {\n * const classification = classifyForwardMpcError(error);\n * logger.warn(`Forward MPC ${operation} failed`, {\n * ...classification,\n * chainName,\n * environmentId,\n * });\n * if (classification.shouldFallback) {\n * // Fall through to relay-based MPC\n * } else {\n * throw error;\n * }\n * }\n * ```\n */\nexport function classifyForwardMpcError(\n error: unknown\n): ForwardMpcErrorClassification {\n if (error instanceof SessionAttestationError) {\n return {\n errorType: 'ATTESTATION_FAILURE',\n errorCode: error.code,\n errorMessage: error.message,\n attestationErrors: error.context?.['errors'] as unknown[] | undefined,\n sessionEstablished: false,\n shouldFallback: true,\n };\n }\n\n if (error instanceof SessionRequestTimeoutError) {\n return {\n errorType: 'FORWARD_MPC_TIMEOUT',\n errorCode: error.code,\n errorMessage: error.message,\n sessionEstablished: true,\n shouldFallback: false,\n };\n }\n\n if (error instanceof ForwardMPCError) {\n return {\n errorType: 'FORWARD_MPC_ERROR',\n errorCode: error.code,\n errorMessage: error.message,\n sessionEstablished: true,\n shouldFallback: false,\n };\n }\n\n // Unknown error type\n return {\n errorType: 'FORWARD_MPC_ERROR',\n errorCode: undefined,\n errorMessage: error instanceof Error ? error.message : String(error),\n sessionEstablished: false,\n shouldFallback: false,\n };\n}\n\n/**\n * Type guard to check if an error is a Forward MPC error that should be handled.\n */\nexport function isForwardMpcError(error: unknown): error is ForwardMPCError {\n return error instanceof ForwardMPCError;\n}\n\n/**\n * Type guard to check if an error is an attestation failure.\n */\nexport function isAttestationError(\n error: unknown\n): error is SessionAttestationError {\n return error instanceof SessionAttestationError;\n}\n"]}
1
+ {"version":3,"sources":["../src/client-v2/errors.ts","../src/client-v2/error-classification.ts"],"names":["ErrorCode","ATTESTATION_FAILED","REQUEST_TIMEOUT","ForwardMPCErrorType","SESSION","ForwardMPCError","Error","code","type","context","message","name","Object","setPrototypeOf","prototype","toJSON","stack","SessionError","SessionAttestationError","cause","undefined","SessionRequestTimeoutError","FORWARD_MPC_ERROR_NAMES","Set","matchesForwardMpcErrorClass","error","ctor","matchesAnyForwardMpcError","has","classifyForwardMpcError","errorType","errorCode","errorMessage","attestationErrors","sessionEstablished","shouldFallback","String","isForwardMpcError","isAttestationError"],"mappings":"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","file":"utils.cjs","sourcesContent":["import { type WebSocketError } from '@dynamic-labs-wallet/forward-mpc-shared';\n\n// ─── Error codes ──────────────────────────────────────────────────────────────\n\nexport const ErrorCode = {\n // Transport\n CONNECTION_FAILED: 'CONNECTION_FAILED',\n CONNECTION_TIMEOUT: 'CONNECTION_TIMEOUT',\n NOT_CONNECTED: 'NOT_CONNECTED',\n // Session\n HANDSHAKE_FAILED: 'HANDSHAKE_FAILED',\n HANDSHAKE_INVALID_RESPONSE: 'HANDSHAKE_INVALID_RESPONSE',\n ATTESTATION_FAILED: 'ATTESTATION_FAILED',\n ATTESTATION_PCR_MISMATCH: 'ATTESTATION_PCR_MISMATCH',\n ATTESTATION_CHALLENGE_MISMATCH: 'ATTESTATION_CHALLENGE_MISMATCH',\n ATTESTATION_NONCE_MISMATCH: 'ATTESTATION_NONCE_MISMATCH',\n ATTESTATION_NONCE_MISSING: 'ATTESTATION_NONCE_MISSING',\n ATTESTATION_DOCUMENT_MISSING: 'ATTESTATION_DOCUMENT_MISSING',\n REQUEST_TIMEOUT: 'REQUEST_TIMEOUT',\n SESSION_DISPOSED: 'SESSION_DISPOSED',\n SERVER_ERROR: 'SERVER_ERROR',\n MESSAGE_PARSE_FAILED: 'MESSAGE_PARSE_FAILED',\n // Client\n SESSION_ESTABLISH_FAILED: 'SESSION_ESTABLISH_FAILED',\n UNSUPPORTED_ALGORITHM: 'UNSUPPORTED_ALGORITHM',\n} as const;\n\nexport type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];\n\n/**\n * Focused subset of ErrorCode for attestation verification failures.\n * Use with `error.code` to distinguish failure modes on SessionAttestationError.\n */\nexport const AttestationErrorCode = {\n /** Generic / unrecognised attestation failure */\n FAILED: ErrorCode.ATTESTATION_FAILED,\n /** PCR8 hash mismatch — enclave measurement changed */\n PCR_MISMATCH: ErrorCode.ATTESTATION_PCR_MISMATCH,\n /** Challenge / ciphertext binding mismatch */\n CHALLENGE_MISMATCH: ErrorCode.ATTESTATION_CHALLENGE_MISMATCH,\n /** Nonce value mismatch — possible tampering */\n NONCE_MISMATCH: ErrorCode.ATTESTATION_NONCE_MISMATCH,\n /** Nonce field missing from attestation document */\n NONCE_MISSING: ErrorCode.ATTESTATION_NONCE_MISSING,\n /** Server did not return an attestation document */\n DOCUMENT_MISSING: ErrorCode.ATTESTATION_DOCUMENT_MISSING,\n} as const;\n\nexport type AttestationErrorCode =\n (typeof AttestationErrorCode)[keyof typeof AttestationErrorCode];\n\n// ─── Error types ──────────────────────────────────────────────────────────────\n\nexport const ForwardMPCErrorType = {\n TRANSPORT: 'transport',\n SESSION: 'session',\n CLIENT: 'client',\n} as const;\n\nexport type ForwardMPCErrorType =\n (typeof ForwardMPCErrorType)[keyof typeof ForwardMPCErrorType];\n\n// ─── Root base class ──────────────────────────────────────────────────────────\n\n/**\n * Abstract root for all Forward MPC errors.\n * `instanceof ForwardMPCError` is true for every error thrown by this library.\n */\nexport abstract class ForwardMPCError extends Error {\n public readonly code: ErrorCode;\n public readonly type: ForwardMPCErrorType;\n public readonly context?: Record<string, unknown>;\n\n constructor(\n message: string,\n code: ErrorCode,\n type: ForwardMPCErrorType,\n context?: Record<string, unknown>\n ) {\n super(message);\n this.name = this.constructor.name;\n this.code = code;\n this.type = type;\n this.context = context;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n type: this.type,\n stack: this.stack,\n context: this.context,\n };\n }\n}\n\n// ─── Layer base classes ───────────────────────────────────────────────────────\n\n/** Abstract base for errors originating from the WebSocket / transport layer. */\nexport abstract class TransportError extends ForwardMPCError {\n constructor(\n message: string,\n code: ErrorCode,\n context?: Record<string, unknown>\n ) {\n super(message, code, ForwardMPCErrorType.TRANSPORT, context);\n }\n}\n\n/** Abstract base for errors originating from the session / crypto / protocol layer. */\nexport abstract class SessionError extends ForwardMPCError {\n constructor(\n message: string,\n code: ErrorCode,\n context?: Record<string, unknown>\n ) {\n super(message, code, ForwardMPCErrorType.SESSION, context);\n }\n}\n\n/** Abstract base for errors originating from the client / application layer. */\nexport abstract class ClientError extends ForwardMPCError {\n constructor(\n message: string,\n code: ErrorCode,\n context?: Record<string, unknown>\n ) {\n super(message, code, ForwardMPCErrorType.CLIENT, context);\n }\n}\n\n// ─── Transport errors ─────────────────────────────────────────────────────────\n\nexport class TransportConnectionError extends TransportError {\n constructor(context?: Record<string, unknown>) {\n super('WebSocket connection failed', ErrorCode.CONNECTION_FAILED, context);\n }\n}\n\nexport class TransportConnectionTimeoutError extends TransportError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'WebSocket connection timed out',\n ErrorCode.CONNECTION_TIMEOUT,\n context\n );\n }\n}\n\nexport class TransportNotConnectedError extends TransportError {\n constructor(context?: Record<string, unknown>) {\n super('WebSocket is not connected', ErrorCode.NOT_CONNECTED, context);\n }\n}\n\n// ─── Session errors ───────────────────────────────────────────────────────────\n\nexport class SessionHandshakeError extends SessionError {\n constructor(reason: string, context?: Record<string, unknown>) {\n super(\n `ML-KEM-768 handshake failed: ${reason}`,\n ErrorCode.HANDSHAKE_FAILED,\n { reason, ...context }\n );\n }\n}\n\nexport class SessionHandshakeInvalidResponseError extends SessionError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'Handshake response was invalid or incomplete',\n ErrorCode.HANDSHAKE_INVALID_RESPONSE,\n context\n );\n }\n}\n\n/**\n * Attestation verification failure.\n * Use `error.code` to distinguish failure reasons (e.g. ATTESTATION_PCR_MISMATCH)\n * and `error.cause` to inspect the original verifier error.\n */\nexport class SessionAttestationError extends SessionError {\n public readonly cause?: unknown;\n\n constructor(\n message: string = 'Attestation verification failed',\n code: ErrorCode = ErrorCode.ATTESTATION_FAILED,\n context?: Record<string, unknown>,\n cause?: unknown\n ) {\n super(message, code, context);\n if (cause !== undefined) {\n this.cause = cause;\n }\n }\n}\n\nexport class SessionRequestTimeoutError extends SessionError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'Request timed out waiting for server response',\n ErrorCode.REQUEST_TIMEOUT,\n context\n );\n }\n}\n\nexport class SessionDisposedError extends SessionError {\n constructor(context?: Record<string, unknown>) {\n super('Session has been disposed', ErrorCode.SESSION_DISPOSED, context);\n }\n}\n\nexport class SessionServerError extends SessionError {\n constructor(reason: string, context?: Record<string, unknown>) {\n super(\n `Server returned an error response: ${reason}`,\n ErrorCode.SERVER_ERROR,\n { reason, ...context }\n );\n }\n}\n\nexport class SessionMessageParseError extends SessionError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'Failed to parse server message',\n ErrorCode.MESSAGE_PARSE_FAILED,\n context\n );\n }\n}\n\n/**\n * The remote server returned an explicit error response.\n * Carries the full WebSocketError payload so callers can inspect\n * `serverError.type` and `serverError.details`.\n */\nexport class SessionRemoteError extends SessionError {\n constructor(\n public readonly serverError: WebSocketError,\n context?: Record<string, unknown>\n ) {\n super(serverError.message, ErrorCode.SERVER_ERROR, context);\n }\n}\n\n// ─── Client errors ────────────────────────────────────────────────────────────\n\nexport class ClientUnsupportedAlgorithmError extends ClientError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'Signing algorithm is not supported',\n ErrorCode.UNSUPPORTED_ALGORITHM,\n context\n );\n }\n}\n\nexport class ClientSessionEstablishFailedError extends ClientError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'Failed to establish session',\n ErrorCode.SESSION_ESTABLISH_FAILED,\n context\n );\n }\n}\n\n// ─── Name registry ────────────────────────────────────────────────────────────\n\n/**\n * Names of every concrete ForwardMPCError subclass in this module.\n *\n * Use this for `.name`-based identity checks when `instanceof` is\n * unreliable — e.g., multiple bundled copies of this package in the same\n * JS realm (each copy has its own constructor function, so cross-copy\n * `instanceof` returns false even for legitimate instances). `.name` is\n * set by the base ForwardMPCError constructor via\n * `this.name = this.constructor.name` and survives that boundary.\n *\n * When adding a new concrete subclass above, add its name here. The\n * exhaustiveness test in `error-classification.spec.ts` walks every\n * exported class in this module and asserts that it appears in this set,\n * so a missing entry will fail CI.\n *\n * NOTE: This is purely an in-bundle string list. It does not survive\n * aggressive minifier configurations that rename class identifiers\n * (e.g. `SessionAttestationError` -> `e_42`). Bundlers that preserve\n * class names (Vite/Rollup default, esbuild with `keepNames`) work\n * correctly. If a consumer ships a build that mangles class names, the\n * cross-bundle fallback path will degrade silently — `instanceof` still\n * catches in-bundle errors.\n */\nexport const FORWARD_MPC_ERROR_NAMES: ReadonlySet<string> = new Set([\n 'TransportConnectionError',\n 'TransportConnectionTimeoutError',\n 'TransportNotConnectedError',\n 'SessionHandshakeError',\n 'SessionHandshakeInvalidResponseError',\n 'SessionAttestationError',\n 'SessionRequestTimeoutError',\n 'SessionDisposedError',\n 'SessionServerError',\n 'SessionMessageParseError',\n 'SessionRemoteError',\n 'ClientUnsupportedAlgorithmError',\n 'ClientSessionEstablishFailedError',\n]);\n","import {\n FORWARD_MPC_ERROR_NAMES,\n ForwardMPCError,\n SessionAttestationError,\n SessionRequestTimeoutError,\n} from './errors';\n\n/**\n * Error classification result from Forward MPC operations.\n */\nexport type ForwardMpcErrorType =\n | 'ATTESTATION_FAILURE'\n | 'FORWARD_MPC_TIMEOUT'\n | 'FORWARD_MPC_ERROR';\n\n/**\n * Result of classifying a Forward MPC error.\n */\nexport interface ForwardMpcErrorClassification {\n /** The type of error encountered */\n errorType: ForwardMpcErrorType;\n /** Error code from ForwardMPCError, if available */\n errorCode: string | undefined;\n /** Error message */\n errorMessage: string;\n /** Attestation verification errors, if this is an attestation failure */\n attestationErrors?: unknown[];\n /** Whether the session was established before the error occurred */\n sessionEstablished: boolean;\n /** Whether this error should trigger a fallback to relay-based MPC */\n shouldFallback: boolean;\n}\n\n/**\n * Matches an error against a ForwardMPC error class using both `instanceof`\n * and `.name`. The name-based check is the safety net for when multiple\n * bundle copies of this package exist in the same JS realm — in that case\n * `instanceof` returns false even for legitimate instances, because the\n * class constructor identities differ across copies. `.name` is set on\n * every ForwardMPCError subclass via `this.name = this.constructor.name`\n * in the base constructor, so it survives cross-bundle dispatch.\n */\nfunction matchesForwardMpcErrorClass<T extends ForwardMPCError>(\n error: unknown,\n ctor: abstract new (...args: never[]) => T,\n name: string\n): error is T {\n if (error instanceof ctor) {\n return true;\n }\n return (\n error instanceof Error &&\n error.name === name &&\n typeof (error as { code?: unknown }).code === 'string'\n );\n}\n\n/**\n * Generic ForwardMPCError detection used by the catch-all branch and the\n * public `isForwardMpcError` type guard.\n *\n * `instanceof ForwardMPCError` works for in-bundle errors but fails\n * for cross-bundle instances. `ForwardMPCError` itself is abstract, so\n * its `.name` never appears on a real error — instead we match any\n * concrete subclass name from `FORWARD_MPC_ERROR_NAMES`.\n */\nfunction matchesAnyForwardMpcError(error: unknown): error is ForwardMPCError {\n if (error instanceof ForwardMPCError) {\n return true;\n }\n return (\n error instanceof Error &&\n FORWARD_MPC_ERROR_NAMES.has(error.name) &&\n typeof (error as { code?: unknown }).code === 'string'\n );\n}\n\n/**\n * Classifies a Forward MPC error and returns structured data for logging.\n * Use this to standardize error handling across keygen, signing, and connect operations.\n *\n * @param error - The error to classify\n * @returns Classification result with error details and recommended action\n *\n * @example\n * ```typescript\n * try {\n * await forwardMpcClient.sign(...);\n * } catch (error) {\n * const classification = classifyForwardMpcError(error);\n * logger.warn(`Forward MPC ${operation} failed`, {\n * ...classification,\n * chainName,\n * environmentId,\n * });\n * if (classification.shouldFallback) {\n * // Fall through to relay-based MPC\n * } else {\n * throw error;\n * }\n * }\n * ```\n *\n * `shouldFallback` is `true` for every Forward MPC failure mode. The\n * relay-based MPC path is a fully functional safety net; treating any\n * Forward MPC failure as terminal (refusing to fall back) blocks user\n * wallet operations end-to-end on transient Forward MPC degradation.\n */\nexport function classifyForwardMpcError(\n error: unknown\n): ForwardMpcErrorClassification {\n if (\n matchesForwardMpcErrorClass(\n error,\n SessionAttestationError,\n 'SessionAttestationError'\n )\n ) {\n return {\n errorType: 'ATTESTATION_FAILURE',\n errorCode: error.code,\n errorMessage: error.message,\n attestationErrors: error.context?.['errors'] as unknown[] | undefined,\n sessionEstablished: false,\n shouldFallback: true,\n };\n }\n\n if (\n matchesForwardMpcErrorClass(\n error,\n SessionRequestTimeoutError,\n 'SessionRequestTimeoutError'\n )\n ) {\n return {\n errorType: 'FORWARD_MPC_TIMEOUT',\n errorCode: error.code,\n errorMessage: error.message,\n sessionEstablished: true,\n shouldFallback: true,\n };\n }\n\n if (matchesAnyForwardMpcError(error)) {\n return {\n errorType: 'FORWARD_MPC_ERROR',\n errorCode: error.code,\n errorMessage: error.message,\n sessionEstablished: true,\n shouldFallback: true,\n };\n }\n\n // Unknown error type — still fall back to relay so an unexpected error\n // from a future ForwardMPC code path doesn't block the user.\n return {\n errorType: 'FORWARD_MPC_ERROR',\n errorCode: undefined,\n errorMessage: error instanceof Error ? error.message : String(error),\n sessionEstablished: false,\n shouldFallback: true,\n };\n}\n\n/**\n * Type guard to check if an error is a Forward MPC error that should be handled.\n * Matches by both `instanceof` and `.name` to survive multiple bundled\n * copies of this package in the same JS realm.\n */\nexport function isForwardMpcError(error: unknown): error is ForwardMPCError {\n return matchesAnyForwardMpcError(error);\n}\n\n/**\n * Type guard to check if an error is an attestation failure.\n * Matches by both `instanceof` and `.name` to survive multiple bundled\n * copies of this package in the same JS realm.\n */\nexport function isAttestationError(\n error: unknown\n): error is SessionAttestationError {\n return matchesForwardMpcErrorClass(\n error,\n SessionAttestationError,\n 'SessionAttestationError'\n );\n}\n"]}
package/dist/utils.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- export { d as ForwardMpcErrorClassification, e as ForwardMpcErrorType, q as classifyForwardMpcError, r as isAttestationError, s as isForwardMpcError } from './utils-pzTJsc5g.cjs';
1
+ export { d as ForwardMpcErrorClassification, e as ForwardMpcErrorType, q as classifyForwardMpcError, r as isAttestationError, s as isForwardMpcError } from './utils-D77Qzra4.cjs';
2
2
  import '@dynamic-labs-wallet/forward-mpc-shared';
package/dist/utils.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { d as ForwardMpcErrorClassification, e as ForwardMpcErrorType, q as classifyForwardMpcError, r as isAttestationError, s as isForwardMpcError } from './utils-pzTJsc5g.js';
1
+ export { d as ForwardMpcErrorClassification, e as ForwardMpcErrorType, q as classifyForwardMpcError, r as isAttestationError, s as isForwardMpcError } from './utils-D77Qzra4.js';
2
2
  import '@dynamic-labs-wallet/forward-mpc-shared';
package/dist/utils.js CHANGED
@@ -61,10 +61,39 @@ var SessionRequestTimeoutError = class extends SessionError {
61
61
  super("Request timed out waiting for server response", ErrorCode.REQUEST_TIMEOUT, context);
62
62
  }
63
63
  };
64
+ var FORWARD_MPC_ERROR_NAMES = /* @__PURE__ */ new Set([
65
+ "TransportConnectionError",
66
+ "TransportConnectionTimeoutError",
67
+ "TransportNotConnectedError",
68
+ "SessionHandshakeError",
69
+ "SessionHandshakeInvalidResponseError",
70
+ "SessionAttestationError",
71
+ "SessionRequestTimeoutError",
72
+ "SessionDisposedError",
73
+ "SessionServerError",
74
+ "SessionMessageParseError",
75
+ "SessionRemoteError",
76
+ "ClientUnsupportedAlgorithmError",
77
+ "ClientSessionEstablishFailedError"
78
+ ]);
64
79
 
65
80
  // src/client-v2/error-classification.ts
81
+ function matchesForwardMpcErrorClass(error, ctor, name) {
82
+ if (error instanceof ctor) {
83
+ return true;
84
+ }
85
+ return error instanceof Error && error.name === name && typeof error.code === "string";
86
+ }
87
+ __name(matchesForwardMpcErrorClass, "matchesForwardMpcErrorClass");
88
+ function matchesAnyForwardMpcError(error) {
89
+ if (error instanceof ForwardMPCError) {
90
+ return true;
91
+ }
92
+ return error instanceof Error && FORWARD_MPC_ERROR_NAMES.has(error.name) && typeof error.code === "string";
93
+ }
94
+ __name(matchesAnyForwardMpcError, "matchesAnyForwardMpcError");
66
95
  function classifyForwardMpcError(error) {
67
- if (error instanceof SessionAttestationError) {
96
+ if (matchesForwardMpcErrorClass(error, SessionAttestationError, "SessionAttestationError")) {
68
97
  return {
69
98
  errorType: "ATTESTATION_FAILURE",
70
99
  errorCode: error.code,
@@ -74,22 +103,22 @@ function classifyForwardMpcError(error) {
74
103
  shouldFallback: true
75
104
  };
76
105
  }
77
- if (error instanceof SessionRequestTimeoutError) {
106
+ if (matchesForwardMpcErrorClass(error, SessionRequestTimeoutError, "SessionRequestTimeoutError")) {
78
107
  return {
79
108
  errorType: "FORWARD_MPC_TIMEOUT",
80
109
  errorCode: error.code,
81
110
  errorMessage: error.message,
82
111
  sessionEstablished: true,
83
- shouldFallback: false
112
+ shouldFallback: true
84
113
  };
85
114
  }
86
- if (error instanceof ForwardMPCError) {
115
+ if (matchesAnyForwardMpcError(error)) {
87
116
  return {
88
117
  errorType: "FORWARD_MPC_ERROR",
89
118
  errorCode: error.code,
90
119
  errorMessage: error.message,
91
120
  sessionEstablished: true,
92
- shouldFallback: false
121
+ shouldFallback: true
93
122
  };
94
123
  }
95
124
  return {
@@ -97,16 +126,16 @@ function classifyForwardMpcError(error) {
97
126
  errorCode: void 0,
98
127
  errorMessage: error instanceof Error ? error.message : String(error),
99
128
  sessionEstablished: false,
100
- shouldFallback: false
129
+ shouldFallback: true
101
130
  };
102
131
  }
103
132
  __name(classifyForwardMpcError, "classifyForwardMpcError");
104
133
  function isForwardMpcError(error) {
105
- return error instanceof ForwardMPCError;
134
+ return matchesAnyForwardMpcError(error);
106
135
  }
107
136
  __name(isForwardMpcError, "isForwardMpcError");
108
137
  function isAttestationError(error) {
109
- return error instanceof SessionAttestationError;
138
+ return matchesForwardMpcErrorClass(error, SessionAttestationError, "SessionAttestationError");
110
139
  }
111
140
  __name(isAttestationError, "isAttestationError");
112
141
 
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client-v2/errors.ts","../src/client-v2/error-classification.ts"],"names":["ErrorCode","ATTESTATION_FAILED","REQUEST_TIMEOUT","ForwardMPCErrorType","SESSION","ForwardMPCError","Error","code","type","context","message","name","Object","setPrototypeOf","prototype","toJSON","stack","SessionError","SessionAttestationError","cause","undefined","SessionRequestTimeoutError","classifyForwardMpcError","error","errorType","errorCode","errorMessage","attestationErrors","sessionEstablished","shouldFallback","String","isForwardMpcError","isAttestationError"],"mappings":"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","file":"utils.js","sourcesContent":["import { type WebSocketError } from '@dynamic-labs-wallet/forward-mpc-shared';\n\n// ─── Error codes ──────────────────────────────────────────────────────────────\n\nexport const ErrorCode = {\n // Transport\n CONNECTION_FAILED: 'CONNECTION_FAILED',\n CONNECTION_TIMEOUT: 'CONNECTION_TIMEOUT',\n NOT_CONNECTED: 'NOT_CONNECTED',\n // Session\n HANDSHAKE_FAILED: 'HANDSHAKE_FAILED',\n HANDSHAKE_INVALID_RESPONSE: 'HANDSHAKE_INVALID_RESPONSE',\n ATTESTATION_FAILED: 'ATTESTATION_FAILED',\n ATTESTATION_PCR_MISMATCH: 'ATTESTATION_PCR_MISMATCH',\n ATTESTATION_CHALLENGE_MISMATCH: 'ATTESTATION_CHALLENGE_MISMATCH',\n ATTESTATION_NONCE_MISMATCH: 'ATTESTATION_NONCE_MISMATCH',\n ATTESTATION_NONCE_MISSING: 'ATTESTATION_NONCE_MISSING',\n ATTESTATION_DOCUMENT_MISSING: 'ATTESTATION_DOCUMENT_MISSING',\n REQUEST_TIMEOUT: 'REQUEST_TIMEOUT',\n SESSION_DISPOSED: 'SESSION_DISPOSED',\n SERVER_ERROR: 'SERVER_ERROR',\n MESSAGE_PARSE_FAILED: 'MESSAGE_PARSE_FAILED',\n // Client\n SESSION_ESTABLISH_FAILED: 'SESSION_ESTABLISH_FAILED',\n UNSUPPORTED_ALGORITHM: 'UNSUPPORTED_ALGORITHM',\n} as const;\n\nexport type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];\n\n/**\n * Focused subset of ErrorCode for attestation verification failures.\n * Use with `error.code` to distinguish failure modes on SessionAttestationError.\n */\nexport const AttestationErrorCode = {\n /** Generic / unrecognised attestation failure */\n FAILED: ErrorCode.ATTESTATION_FAILED,\n /** PCR8 hash mismatch — enclave measurement changed */\n PCR_MISMATCH: ErrorCode.ATTESTATION_PCR_MISMATCH,\n /** Challenge / ciphertext binding mismatch */\n CHALLENGE_MISMATCH: ErrorCode.ATTESTATION_CHALLENGE_MISMATCH,\n /** Nonce value mismatch — possible tampering */\n NONCE_MISMATCH: ErrorCode.ATTESTATION_NONCE_MISMATCH,\n /** Nonce field missing from attestation document */\n NONCE_MISSING: ErrorCode.ATTESTATION_NONCE_MISSING,\n /** Server did not return an attestation document */\n DOCUMENT_MISSING: ErrorCode.ATTESTATION_DOCUMENT_MISSING,\n} as const;\n\nexport type AttestationErrorCode =\n (typeof AttestationErrorCode)[keyof typeof AttestationErrorCode];\n\n// ─── Error types ──────────────────────────────────────────────────────────────\n\nexport const ForwardMPCErrorType = {\n TRANSPORT: 'transport',\n SESSION: 'session',\n CLIENT: 'client',\n} as const;\n\nexport type ForwardMPCErrorType =\n (typeof ForwardMPCErrorType)[keyof typeof ForwardMPCErrorType];\n\n// ─── Root base class ──────────────────────────────────────────────────────────\n\n/**\n * Abstract root for all Forward MPC errors.\n * `instanceof ForwardMPCError` is true for every error thrown by this library.\n */\nexport abstract class ForwardMPCError extends Error {\n public readonly code: ErrorCode;\n public readonly type: ForwardMPCErrorType;\n public readonly context?: Record<string, unknown>;\n\n constructor(\n message: string,\n code: ErrorCode,\n type: ForwardMPCErrorType,\n context?: Record<string, unknown>\n ) {\n super(message);\n this.name = this.constructor.name;\n this.code = code;\n this.type = type;\n this.context = context;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n type: this.type,\n stack: this.stack,\n context: this.context,\n };\n }\n}\n\n// ─── Layer base classes ───────────────────────────────────────────────────────\n\n/** Abstract base for errors originating from the WebSocket / transport layer. */\nexport abstract class TransportError extends ForwardMPCError {\n constructor(\n message: string,\n code: ErrorCode,\n context?: Record<string, unknown>\n ) {\n super(message, code, ForwardMPCErrorType.TRANSPORT, context);\n }\n}\n\n/** Abstract base for errors originating from the session / crypto / protocol layer. */\nexport abstract class SessionError extends ForwardMPCError {\n constructor(\n message: string,\n code: ErrorCode,\n context?: Record<string, unknown>\n ) {\n super(message, code, ForwardMPCErrorType.SESSION, context);\n }\n}\n\n/** Abstract base for errors originating from the client / application layer. */\nexport abstract class ClientError extends ForwardMPCError {\n constructor(\n message: string,\n code: ErrorCode,\n context?: Record<string, unknown>\n ) {\n super(message, code, ForwardMPCErrorType.CLIENT, context);\n }\n}\n\n// ─── Transport errors ─────────────────────────────────────────────────────────\n\nexport class TransportConnectionError extends TransportError {\n constructor(context?: Record<string, unknown>) {\n super('WebSocket connection failed', ErrorCode.CONNECTION_FAILED, context);\n }\n}\n\nexport class TransportConnectionTimeoutError extends TransportError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'WebSocket connection timed out',\n ErrorCode.CONNECTION_TIMEOUT,\n context\n );\n }\n}\n\nexport class TransportNotConnectedError extends TransportError {\n constructor(context?: Record<string, unknown>) {\n super('WebSocket is not connected', ErrorCode.NOT_CONNECTED, context);\n }\n}\n\n// ─── Session errors ───────────────────────────────────────────────────────────\n\nexport class SessionHandshakeError extends SessionError {\n constructor(reason: string, context?: Record<string, unknown>) {\n super(\n `ML-KEM-768 handshake failed: ${reason}`,\n ErrorCode.HANDSHAKE_FAILED,\n { reason, ...context }\n );\n }\n}\n\nexport class SessionHandshakeInvalidResponseError extends SessionError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'Handshake response was invalid or incomplete',\n ErrorCode.HANDSHAKE_INVALID_RESPONSE,\n context\n );\n }\n}\n\n/**\n * Attestation verification failure.\n * Use `error.code` to distinguish failure reasons (e.g. ATTESTATION_PCR_MISMATCH)\n * and `error.cause` to inspect the original verifier error.\n */\nexport class SessionAttestationError extends SessionError {\n public readonly cause?: unknown;\n\n constructor(\n message: string = 'Attestation verification failed',\n code: ErrorCode = ErrorCode.ATTESTATION_FAILED,\n context?: Record<string, unknown>,\n cause?: unknown\n ) {\n super(message, code, context);\n if (cause !== undefined) {\n this.cause = cause;\n }\n }\n}\n\nexport class SessionRequestTimeoutError extends SessionError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'Request timed out waiting for server response',\n ErrorCode.REQUEST_TIMEOUT,\n context\n );\n }\n}\n\nexport class SessionDisposedError extends SessionError {\n constructor(context?: Record<string, unknown>) {\n super('Session has been disposed', ErrorCode.SESSION_DISPOSED, context);\n }\n}\n\nexport class SessionServerError extends SessionError {\n constructor(reason: string, context?: Record<string, unknown>) {\n super(\n `Server returned an error response: ${reason}`,\n ErrorCode.SERVER_ERROR,\n { reason, ...context }\n );\n }\n}\n\nexport class SessionMessageParseError extends SessionError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'Failed to parse server message',\n ErrorCode.MESSAGE_PARSE_FAILED,\n context\n );\n }\n}\n\n/**\n * The remote server returned an explicit error response.\n * Carries the full WebSocketError payload so callers can inspect\n * `serverError.type` and `serverError.details`.\n */\nexport class SessionRemoteError extends SessionError {\n constructor(\n public readonly serverError: WebSocketError,\n context?: Record<string, unknown>\n ) {\n super(serverError.message, ErrorCode.SERVER_ERROR, context);\n }\n}\n\n// ─── Client errors ────────────────────────────────────────────────────────────\n\nexport class ClientUnsupportedAlgorithmError extends ClientError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'Signing algorithm is not supported',\n ErrorCode.UNSUPPORTED_ALGORITHM,\n context\n );\n }\n}\n\nexport class ClientSessionEstablishFailedError extends ClientError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'Failed to establish session',\n ErrorCode.SESSION_ESTABLISH_FAILED,\n context\n );\n }\n}\n","import {\n ForwardMPCError,\n SessionAttestationError,\n SessionRequestTimeoutError,\n} from './errors';\n\n/**\n * Error classification result from Forward MPC operations.\n */\nexport type ForwardMpcErrorType =\n | 'ATTESTATION_FAILURE'\n | 'FORWARD_MPC_TIMEOUT'\n | 'FORWARD_MPC_ERROR';\n\n/**\n * Result of classifying a Forward MPC error.\n */\nexport interface ForwardMpcErrorClassification {\n /** The type of error encountered */\n errorType: ForwardMpcErrorType;\n /** Error code from ForwardMPCError, if available */\n errorCode: string | undefined;\n /** Error message */\n errorMessage: string;\n /** Attestation verification errors, if this is an attestation failure */\n attestationErrors?: unknown[];\n /** Whether the session was established before the error occurred */\n sessionEstablished: boolean;\n /** Whether this error should trigger a fallback to relay-based MPC */\n shouldFallback: boolean;\n}\n\n/**\n * Classifies a Forward MPC error and returns structured data for logging.\n * Use this to standardize error handling across keygen, signing, and connect operations.\n *\n * @param error - The error to classify\n * @returns Classification result with error details and recommended action\n *\n * @example\n * ```typescript\n * try {\n * await forwardMpcClient.sign(...);\n * } catch (error) {\n * const classification = classifyForwardMpcError(error);\n * logger.warn(`Forward MPC ${operation} failed`, {\n * ...classification,\n * chainName,\n * environmentId,\n * });\n * if (classification.shouldFallback) {\n * // Fall through to relay-based MPC\n * } else {\n * throw error;\n * }\n * }\n * ```\n */\nexport function classifyForwardMpcError(\n error: unknown\n): ForwardMpcErrorClassification {\n if (error instanceof SessionAttestationError) {\n return {\n errorType: 'ATTESTATION_FAILURE',\n errorCode: error.code,\n errorMessage: error.message,\n attestationErrors: error.context?.['errors'] as unknown[] | undefined,\n sessionEstablished: false,\n shouldFallback: true,\n };\n }\n\n if (error instanceof SessionRequestTimeoutError) {\n return {\n errorType: 'FORWARD_MPC_TIMEOUT',\n errorCode: error.code,\n errorMessage: error.message,\n sessionEstablished: true,\n shouldFallback: false,\n };\n }\n\n if (error instanceof ForwardMPCError) {\n return {\n errorType: 'FORWARD_MPC_ERROR',\n errorCode: error.code,\n errorMessage: error.message,\n sessionEstablished: true,\n shouldFallback: false,\n };\n }\n\n // Unknown error type\n return {\n errorType: 'FORWARD_MPC_ERROR',\n errorCode: undefined,\n errorMessage: error instanceof Error ? error.message : String(error),\n sessionEstablished: false,\n shouldFallback: false,\n };\n}\n\n/**\n * Type guard to check if an error is a Forward MPC error that should be handled.\n */\nexport function isForwardMpcError(error: unknown): error is ForwardMPCError {\n return error instanceof ForwardMPCError;\n}\n\n/**\n * Type guard to check if an error is an attestation failure.\n */\nexport function isAttestationError(\n error: unknown\n): error is SessionAttestationError {\n return error instanceof SessionAttestationError;\n}\n"]}
1
+ {"version":3,"sources":["../src/client-v2/errors.ts","../src/client-v2/error-classification.ts"],"names":["ErrorCode","ATTESTATION_FAILED","REQUEST_TIMEOUT","ForwardMPCErrorType","SESSION","ForwardMPCError","Error","code","type","context","message","name","Object","setPrototypeOf","prototype","toJSON","stack","SessionError","SessionAttestationError","cause","undefined","SessionRequestTimeoutError","FORWARD_MPC_ERROR_NAMES","Set","matchesForwardMpcErrorClass","error","ctor","matchesAnyForwardMpcError","has","classifyForwardMpcError","errorType","errorCode","errorMessage","attestationErrors","sessionEstablished","shouldFallback","String","isForwardMpcError","isAttestationError"],"mappings":";;;;AAIO,IAAMA,SAAAA,GAAY;EAQvBC,kBAAAA,EAAoB,oBAAA;EAMpBC,eAAAA,EAAiB,iBAOnB,CAAA;AA4BO,IAAMC,mBAAAA,GAAsB;EAEjCC,OAAAA,EAAS,SAEX,CAAA;AAWO,IAAeC,eAAAA,GAAf,cAAuCC,KAAAA,CAAAA;EAlE9C;;;AAmEkBC,EAAAA,IAAAA;AACAC,EAAAA,IAAAA;AACAC,EAAAA,OAAAA;EAEhB,WAAA,CACEC,OAAAA,EACAH,IAAAA,EACAC,IAAAA,EACAC,OAAAA,EACA;AACA,IAAA,KAAA,CAAMC,OAAAA,CAAAA;AACN,IAAA,IAAA,CAAKC,IAAAA,GAAO,KAAK,WAAA,CAAYA,IAAAA;AAC7B,IAAA,IAAA,CAAKJ,IAAAA,GAAOA,IAAAA;AACZ,IAAA,IAAA,CAAKC,IAAAA,GAAOA,IAAAA;AACZ,IAAA,IAAA,CAAKC,OAAAA,GAAUA,OAAAA;AACfG,IAAAA,MAAAA,CAAOC,cAAAA,CAAe,IAAA,EAAM,GAAA,CAAA,MAAA,CAAWC,SAAS,CAAA;AAClD,EAAA;EAEAC,MAAAA,GAAkC;AAChC,IAAA,OAAO;AACLJ,MAAAA,IAAAA,EAAM,IAAA,CAAKA,IAAAA;AACXD,MAAAA,OAAAA,EAAS,IAAA,CAAKA,OAAAA;AACdH,MAAAA,IAAAA,EAAM,IAAA,CAAKA,IAAAA;AACXC,MAAAA,IAAAA,EAAM,IAAA,CAAKA,IAAAA;AACXQ,MAAAA,KAAAA,EAAO,IAAA,CAAKA,KAAAA;AACZP,MAAAA,OAAAA,EAAS,IAAA,CAAKA;AAChB,KAAA;AACF,EAAA;AACF,CAAA;AAgBO,IAAeQ,YAAAA,GAAf,cAAoCZ,eAAAA,CAAAA;EA/G3C;;;EAgHE,WAAA,CACEK,OAAAA,EACAH,MACAE,OAAAA,EACA;AACA,IAAA,KAAA,CAAMC,OAAAA,EAASH,IAAAA,EAAMJ,mBAAAA,CAAoBC,OAAAA,EAASK,OAAAA,CAAAA;AACpD,EAAA;AACF,CAAA;AAgEO,IAAMS,uBAAAA,GAAN,cAAsCD,YAAAA,CAAAA;EAvL7C;;;AAwLkBE,EAAAA,KAAAA;AAEhB,EAAA,WAAA,CACET,UAAkB,iCAAA,EAClBH,IAAAA,GAAkBP,SAAAA,CAAUC,kBAAAA,EAC5BQ,SACAU,KAAAA,EACA;AACA,IAAA,KAAA,CAAMT,OAAAA,EAASH,MAAME,OAAAA,CAAAA;AACrB,IAAA,IAAIU,UAAUC,MAAAA,EAAW;AACvB,MAAA,IAAA,CAAKD,KAAAA,GAAQA,KAAAA;AACf,IAAA;AACF,EAAA;AACF,CAAA;AAEO,IAAME,0BAAAA,GAAN,cAAyCJ,YAAAA,CAAAA;EAvMhD;;;AAwME,EAAA,WAAA,CAAYR,OAAAA,EAAmC;AAC7C,IAAA,KAAA,CACE,+CAAA,EACAT,SAAAA,CAAUE,eAAAA,EACVO,OAAAA,CAAAA;AAEJ,EAAA;AACF,CAAA;AAyFO,IAAMa,uBAAAA,uBAAmDC,GAAAA,CAAI;AAClE,EAAA,0BAAA;AACA,EAAA,iCAAA;AACA,EAAA,4BAAA;AACA,EAAA,uBAAA;AACA,EAAA,sCAAA;AACA,EAAA,yBAAA;AACA,EAAA,4BAAA;AACA,EAAA,sBAAA;AACA,EAAA,oBAAA;AACA,EAAA,0BAAA;AACA,EAAA,oBAAA;AACA,EAAA,iCAAA;AACA,EAAA;AACD,CAAA,CAAA;;;AC9QD,SAASC,2BAAAA,CACPC,KAAAA,EACAC,IAAAA,EACAf,IAAAA,EAAY;AAEZ,EAAA,IAAIc,iBAAiBC,IAAAA,EAAM;AACzB,IAAA,OAAO,IAAA;AACT,EAAA;AACA,EAAA,OACED,iBAAiBnB,KAAAA,IACjBmB,KAAAA,CAAMd,SAASA,IAAAA,IACf,OAAQc,MAA6BlB,IAAAA,KAAS,QAAA;AAElD;AAbSiB,MAAAA,CAAAA,2BAAAA,EAAAA,6BAAAA,CAAAA;AAwBT,SAASG,0BAA0BF,KAAAA,EAAc;AAC/C,EAAA,IAAIA,iBAAiBpB,eAAAA,EAAiB;AACpC,IAAA,OAAO,IAAA;AACT,EAAA;AACA,EAAA,OACEoB,KAAAA,YAAiBnB,SACjBgB,uBAAAA,CAAwBM,GAAAA,CAAIH,MAAMd,IAAI,CAAA,IACtC,OAAQc,KAAAA,CAA6BlB,IAAAA,KAAS,QAAA;AAElD;AATSoB,MAAAA,CAAAA,yBAAAA,EAAAA,2BAAAA,CAAAA;AA0CF,SAASE,wBACdJ,KAAAA,EAAc;AAEd,EAAA,IACED,2BAAAA,CACEC,KAAAA,EACAP,uBAAAA,EACA,yBAAA,CAAA,EAEF;AACA,IAAA,OAAO;MACLY,SAAAA,EAAW,qBAAA;AACXC,MAAAA,SAAAA,EAAWN,KAAAA,CAAMlB,IAAAA;AACjByB,MAAAA,YAAAA,EAAcP,KAAAA,CAAMf,OAAAA;MACpBuB,iBAAAA,EAAmBR,KAAAA,CAAMhB,UAAU,QAAA,CAAA;MACnCyB,kBAAAA,EAAoB,KAAA;MACpBC,cAAAA,EAAgB;AAClB,KAAA;AACF,EAAA;AAEA,EAAA,IACEX,2BAAAA,CACEC,KAAAA,EACAJ,0BAAAA,EACA,4BAAA,CAAA,EAEF;AACA,IAAA,OAAO;MACLS,SAAAA,EAAW,qBAAA;AACXC,MAAAA,SAAAA,EAAWN,KAAAA,CAAMlB,IAAAA;AACjByB,MAAAA,YAAAA,EAAcP,KAAAA,CAAMf,OAAAA;MACpBwB,kBAAAA,EAAoB,IAAA;MACpBC,cAAAA,EAAgB;AAClB,KAAA;AACF,EAAA;AAEA,EAAA,IAAIR,yBAAAA,CAA0BF,KAAAA,CAAAA,EAAQ;AACpC,IAAA,OAAO;MACLK,SAAAA,EAAW,mBAAA;AACXC,MAAAA,SAAAA,EAAWN,KAAAA,CAAMlB,IAAAA;AACjByB,MAAAA,YAAAA,EAAcP,KAAAA,CAAMf,OAAAA;MACpBwB,kBAAAA,EAAoB,IAAA;MACpBC,cAAAA,EAAgB;AAClB,KAAA;AACF,EAAA;AAIA,EAAA,OAAO;IACLL,SAAAA,EAAW,mBAAA;IACXC,SAAAA,EAAWX,MAAAA;AACXY,IAAAA,YAAAA,EAAcP,KAAAA,YAAiBnB,KAAAA,GAAQmB,KAAAA,CAAMf,OAAAA,GAAU0B,OAAOX,KAAAA,CAAAA;IAC9DS,kBAAAA,EAAoB,KAAA;IACpBC,cAAAA,EAAgB;AAClB,GAAA;AACF;AAvDgBN,MAAAA,CAAAA,uBAAAA,EAAAA,yBAAAA,CAAAA;AA8DT,SAASQ,kBAAkBZ,KAAAA,EAAc;AAC9C,EAAA,OAAOE,0BAA0BF,KAAAA,CAAAA;AACnC;AAFgBY,MAAAA,CAAAA,iBAAAA,EAAAA,mBAAAA,CAAAA;AAST,SAASC,mBACdb,KAAAA,EAAc;AAEd,EAAA,OAAOD,2BAAAA,CACLC,KAAAA,EACAP,uBAAAA,EACA,yBAAA,CAAA;AAEJ;AARgBoB,MAAAA,CAAAA,kBAAAA,EAAAA,oBAAAA,CAAAA","file":"utils.js","sourcesContent":["import { type WebSocketError } from '@dynamic-labs-wallet/forward-mpc-shared';\n\n// ─── Error codes ──────────────────────────────────────────────────────────────\n\nexport const ErrorCode = {\n // Transport\n CONNECTION_FAILED: 'CONNECTION_FAILED',\n CONNECTION_TIMEOUT: 'CONNECTION_TIMEOUT',\n NOT_CONNECTED: 'NOT_CONNECTED',\n // Session\n HANDSHAKE_FAILED: 'HANDSHAKE_FAILED',\n HANDSHAKE_INVALID_RESPONSE: 'HANDSHAKE_INVALID_RESPONSE',\n ATTESTATION_FAILED: 'ATTESTATION_FAILED',\n ATTESTATION_PCR_MISMATCH: 'ATTESTATION_PCR_MISMATCH',\n ATTESTATION_CHALLENGE_MISMATCH: 'ATTESTATION_CHALLENGE_MISMATCH',\n ATTESTATION_NONCE_MISMATCH: 'ATTESTATION_NONCE_MISMATCH',\n ATTESTATION_NONCE_MISSING: 'ATTESTATION_NONCE_MISSING',\n ATTESTATION_DOCUMENT_MISSING: 'ATTESTATION_DOCUMENT_MISSING',\n REQUEST_TIMEOUT: 'REQUEST_TIMEOUT',\n SESSION_DISPOSED: 'SESSION_DISPOSED',\n SERVER_ERROR: 'SERVER_ERROR',\n MESSAGE_PARSE_FAILED: 'MESSAGE_PARSE_FAILED',\n // Client\n SESSION_ESTABLISH_FAILED: 'SESSION_ESTABLISH_FAILED',\n UNSUPPORTED_ALGORITHM: 'UNSUPPORTED_ALGORITHM',\n} as const;\n\nexport type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];\n\n/**\n * Focused subset of ErrorCode for attestation verification failures.\n * Use with `error.code` to distinguish failure modes on SessionAttestationError.\n */\nexport const AttestationErrorCode = {\n /** Generic / unrecognised attestation failure */\n FAILED: ErrorCode.ATTESTATION_FAILED,\n /** PCR8 hash mismatch — enclave measurement changed */\n PCR_MISMATCH: ErrorCode.ATTESTATION_PCR_MISMATCH,\n /** Challenge / ciphertext binding mismatch */\n CHALLENGE_MISMATCH: ErrorCode.ATTESTATION_CHALLENGE_MISMATCH,\n /** Nonce value mismatch — possible tampering */\n NONCE_MISMATCH: ErrorCode.ATTESTATION_NONCE_MISMATCH,\n /** Nonce field missing from attestation document */\n NONCE_MISSING: ErrorCode.ATTESTATION_NONCE_MISSING,\n /** Server did not return an attestation document */\n DOCUMENT_MISSING: ErrorCode.ATTESTATION_DOCUMENT_MISSING,\n} as const;\n\nexport type AttestationErrorCode =\n (typeof AttestationErrorCode)[keyof typeof AttestationErrorCode];\n\n// ─── Error types ──────────────────────────────────────────────────────────────\n\nexport const ForwardMPCErrorType = {\n TRANSPORT: 'transport',\n SESSION: 'session',\n CLIENT: 'client',\n} as const;\n\nexport type ForwardMPCErrorType =\n (typeof ForwardMPCErrorType)[keyof typeof ForwardMPCErrorType];\n\n// ─── Root base class ──────────────────────────────────────────────────────────\n\n/**\n * Abstract root for all Forward MPC errors.\n * `instanceof ForwardMPCError` is true for every error thrown by this library.\n */\nexport abstract class ForwardMPCError extends Error {\n public readonly code: ErrorCode;\n public readonly type: ForwardMPCErrorType;\n public readonly context?: Record<string, unknown>;\n\n constructor(\n message: string,\n code: ErrorCode,\n type: ForwardMPCErrorType,\n context?: Record<string, unknown>\n ) {\n super(message);\n this.name = this.constructor.name;\n this.code = code;\n this.type = type;\n this.context = context;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n type: this.type,\n stack: this.stack,\n context: this.context,\n };\n }\n}\n\n// ─── Layer base classes ───────────────────────────────────────────────────────\n\n/** Abstract base for errors originating from the WebSocket / transport layer. */\nexport abstract class TransportError extends ForwardMPCError {\n constructor(\n message: string,\n code: ErrorCode,\n context?: Record<string, unknown>\n ) {\n super(message, code, ForwardMPCErrorType.TRANSPORT, context);\n }\n}\n\n/** Abstract base for errors originating from the session / crypto / protocol layer. */\nexport abstract class SessionError extends ForwardMPCError {\n constructor(\n message: string,\n code: ErrorCode,\n context?: Record<string, unknown>\n ) {\n super(message, code, ForwardMPCErrorType.SESSION, context);\n }\n}\n\n/** Abstract base for errors originating from the client / application layer. */\nexport abstract class ClientError extends ForwardMPCError {\n constructor(\n message: string,\n code: ErrorCode,\n context?: Record<string, unknown>\n ) {\n super(message, code, ForwardMPCErrorType.CLIENT, context);\n }\n}\n\n// ─── Transport errors ─────────────────────────────────────────────────────────\n\nexport class TransportConnectionError extends TransportError {\n constructor(context?: Record<string, unknown>) {\n super('WebSocket connection failed', ErrorCode.CONNECTION_FAILED, context);\n }\n}\n\nexport class TransportConnectionTimeoutError extends TransportError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'WebSocket connection timed out',\n ErrorCode.CONNECTION_TIMEOUT,\n context\n );\n }\n}\n\nexport class TransportNotConnectedError extends TransportError {\n constructor(context?: Record<string, unknown>) {\n super('WebSocket is not connected', ErrorCode.NOT_CONNECTED, context);\n }\n}\n\n// ─── Session errors ───────────────────────────────────────────────────────────\n\nexport class SessionHandshakeError extends SessionError {\n constructor(reason: string, context?: Record<string, unknown>) {\n super(\n `ML-KEM-768 handshake failed: ${reason}`,\n ErrorCode.HANDSHAKE_FAILED,\n { reason, ...context }\n );\n }\n}\n\nexport class SessionHandshakeInvalidResponseError extends SessionError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'Handshake response was invalid or incomplete',\n ErrorCode.HANDSHAKE_INVALID_RESPONSE,\n context\n );\n }\n}\n\n/**\n * Attestation verification failure.\n * Use `error.code` to distinguish failure reasons (e.g. ATTESTATION_PCR_MISMATCH)\n * and `error.cause` to inspect the original verifier error.\n */\nexport class SessionAttestationError extends SessionError {\n public readonly cause?: unknown;\n\n constructor(\n message: string = 'Attestation verification failed',\n code: ErrorCode = ErrorCode.ATTESTATION_FAILED,\n context?: Record<string, unknown>,\n cause?: unknown\n ) {\n super(message, code, context);\n if (cause !== undefined) {\n this.cause = cause;\n }\n }\n}\n\nexport class SessionRequestTimeoutError extends SessionError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'Request timed out waiting for server response',\n ErrorCode.REQUEST_TIMEOUT,\n context\n );\n }\n}\n\nexport class SessionDisposedError extends SessionError {\n constructor(context?: Record<string, unknown>) {\n super('Session has been disposed', ErrorCode.SESSION_DISPOSED, context);\n }\n}\n\nexport class SessionServerError extends SessionError {\n constructor(reason: string, context?: Record<string, unknown>) {\n super(\n `Server returned an error response: ${reason}`,\n ErrorCode.SERVER_ERROR,\n { reason, ...context }\n );\n }\n}\n\nexport class SessionMessageParseError extends SessionError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'Failed to parse server message',\n ErrorCode.MESSAGE_PARSE_FAILED,\n context\n );\n }\n}\n\n/**\n * The remote server returned an explicit error response.\n * Carries the full WebSocketError payload so callers can inspect\n * `serverError.type` and `serverError.details`.\n */\nexport class SessionRemoteError extends SessionError {\n constructor(\n public readonly serverError: WebSocketError,\n context?: Record<string, unknown>\n ) {\n super(serverError.message, ErrorCode.SERVER_ERROR, context);\n }\n}\n\n// ─── Client errors ────────────────────────────────────────────────────────────\n\nexport class ClientUnsupportedAlgorithmError extends ClientError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'Signing algorithm is not supported',\n ErrorCode.UNSUPPORTED_ALGORITHM,\n context\n );\n }\n}\n\nexport class ClientSessionEstablishFailedError extends ClientError {\n constructor(context?: Record<string, unknown>) {\n super(\n 'Failed to establish session',\n ErrorCode.SESSION_ESTABLISH_FAILED,\n context\n );\n }\n}\n\n// ─── Name registry ────────────────────────────────────────────────────────────\n\n/**\n * Names of every concrete ForwardMPCError subclass in this module.\n *\n * Use this for `.name`-based identity checks when `instanceof` is\n * unreliable — e.g., multiple bundled copies of this package in the same\n * JS realm (each copy has its own constructor function, so cross-copy\n * `instanceof` returns false even for legitimate instances). `.name` is\n * set by the base ForwardMPCError constructor via\n * `this.name = this.constructor.name` and survives that boundary.\n *\n * When adding a new concrete subclass above, add its name here. The\n * exhaustiveness test in `error-classification.spec.ts` walks every\n * exported class in this module and asserts that it appears in this set,\n * so a missing entry will fail CI.\n *\n * NOTE: This is purely an in-bundle string list. It does not survive\n * aggressive minifier configurations that rename class identifiers\n * (e.g. `SessionAttestationError` -> `e_42`). Bundlers that preserve\n * class names (Vite/Rollup default, esbuild with `keepNames`) work\n * correctly. If a consumer ships a build that mangles class names, the\n * cross-bundle fallback path will degrade silently — `instanceof` still\n * catches in-bundle errors.\n */\nexport const FORWARD_MPC_ERROR_NAMES: ReadonlySet<string> = new Set([\n 'TransportConnectionError',\n 'TransportConnectionTimeoutError',\n 'TransportNotConnectedError',\n 'SessionHandshakeError',\n 'SessionHandshakeInvalidResponseError',\n 'SessionAttestationError',\n 'SessionRequestTimeoutError',\n 'SessionDisposedError',\n 'SessionServerError',\n 'SessionMessageParseError',\n 'SessionRemoteError',\n 'ClientUnsupportedAlgorithmError',\n 'ClientSessionEstablishFailedError',\n]);\n","import {\n FORWARD_MPC_ERROR_NAMES,\n ForwardMPCError,\n SessionAttestationError,\n SessionRequestTimeoutError,\n} from './errors';\n\n/**\n * Error classification result from Forward MPC operations.\n */\nexport type ForwardMpcErrorType =\n | 'ATTESTATION_FAILURE'\n | 'FORWARD_MPC_TIMEOUT'\n | 'FORWARD_MPC_ERROR';\n\n/**\n * Result of classifying a Forward MPC error.\n */\nexport interface ForwardMpcErrorClassification {\n /** The type of error encountered */\n errorType: ForwardMpcErrorType;\n /** Error code from ForwardMPCError, if available */\n errorCode: string | undefined;\n /** Error message */\n errorMessage: string;\n /** Attestation verification errors, if this is an attestation failure */\n attestationErrors?: unknown[];\n /** Whether the session was established before the error occurred */\n sessionEstablished: boolean;\n /** Whether this error should trigger a fallback to relay-based MPC */\n shouldFallback: boolean;\n}\n\n/**\n * Matches an error against a ForwardMPC error class using both `instanceof`\n * and `.name`. The name-based check is the safety net for when multiple\n * bundle copies of this package exist in the same JS realm — in that case\n * `instanceof` returns false even for legitimate instances, because the\n * class constructor identities differ across copies. `.name` is set on\n * every ForwardMPCError subclass via `this.name = this.constructor.name`\n * in the base constructor, so it survives cross-bundle dispatch.\n */\nfunction matchesForwardMpcErrorClass<T extends ForwardMPCError>(\n error: unknown,\n ctor: abstract new (...args: never[]) => T,\n name: string\n): error is T {\n if (error instanceof ctor) {\n return true;\n }\n return (\n error instanceof Error &&\n error.name === name &&\n typeof (error as { code?: unknown }).code === 'string'\n );\n}\n\n/**\n * Generic ForwardMPCError detection used by the catch-all branch and the\n * public `isForwardMpcError` type guard.\n *\n * `instanceof ForwardMPCError` works for in-bundle errors but fails\n * for cross-bundle instances. `ForwardMPCError` itself is abstract, so\n * its `.name` never appears on a real error — instead we match any\n * concrete subclass name from `FORWARD_MPC_ERROR_NAMES`.\n */\nfunction matchesAnyForwardMpcError(error: unknown): error is ForwardMPCError {\n if (error instanceof ForwardMPCError) {\n return true;\n }\n return (\n error instanceof Error &&\n FORWARD_MPC_ERROR_NAMES.has(error.name) &&\n typeof (error as { code?: unknown }).code === 'string'\n );\n}\n\n/**\n * Classifies a Forward MPC error and returns structured data for logging.\n * Use this to standardize error handling across keygen, signing, and connect operations.\n *\n * @param error - The error to classify\n * @returns Classification result with error details and recommended action\n *\n * @example\n * ```typescript\n * try {\n * await forwardMpcClient.sign(...);\n * } catch (error) {\n * const classification = classifyForwardMpcError(error);\n * logger.warn(`Forward MPC ${operation} failed`, {\n * ...classification,\n * chainName,\n * environmentId,\n * });\n * if (classification.shouldFallback) {\n * // Fall through to relay-based MPC\n * } else {\n * throw error;\n * }\n * }\n * ```\n *\n * `shouldFallback` is `true` for every Forward MPC failure mode. The\n * relay-based MPC path is a fully functional safety net; treating any\n * Forward MPC failure as terminal (refusing to fall back) blocks user\n * wallet operations end-to-end on transient Forward MPC degradation.\n */\nexport function classifyForwardMpcError(\n error: unknown\n): ForwardMpcErrorClassification {\n if (\n matchesForwardMpcErrorClass(\n error,\n SessionAttestationError,\n 'SessionAttestationError'\n )\n ) {\n return {\n errorType: 'ATTESTATION_FAILURE',\n errorCode: error.code,\n errorMessage: error.message,\n attestationErrors: error.context?.['errors'] as unknown[] | undefined,\n sessionEstablished: false,\n shouldFallback: true,\n };\n }\n\n if (\n matchesForwardMpcErrorClass(\n error,\n SessionRequestTimeoutError,\n 'SessionRequestTimeoutError'\n )\n ) {\n return {\n errorType: 'FORWARD_MPC_TIMEOUT',\n errorCode: error.code,\n errorMessage: error.message,\n sessionEstablished: true,\n shouldFallback: true,\n };\n }\n\n if (matchesAnyForwardMpcError(error)) {\n return {\n errorType: 'FORWARD_MPC_ERROR',\n errorCode: error.code,\n errorMessage: error.message,\n sessionEstablished: true,\n shouldFallback: true,\n };\n }\n\n // Unknown error type — still fall back to relay so an unexpected error\n // from a future ForwardMPC code path doesn't block the user.\n return {\n errorType: 'FORWARD_MPC_ERROR',\n errorCode: undefined,\n errorMessage: error instanceof Error ? error.message : String(error),\n sessionEstablished: false,\n shouldFallback: true,\n };\n}\n\n/**\n * Type guard to check if an error is a Forward MPC error that should be handled.\n * Matches by both `instanceof` and `.name` to survive multiple bundled\n * copies of this package in the same JS realm.\n */\nexport function isForwardMpcError(error: unknown): error is ForwardMPCError {\n return matchesAnyForwardMpcError(error);\n}\n\n/**\n * Type guard to check if an error is an attestation failure.\n * Matches by both `instanceof` and `.name` to survive multiple bundled\n * copies of this package in the same JS realm.\n */\nexport function isAttestationError(\n error: unknown\n): error is SessionAttestationError {\n return matchesForwardMpcErrorClass(\n error,\n SessionAttestationError,\n 'SessionAttestationError'\n );\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamic-labs-wallet/forward-mpc-client",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "description": "WebSocket client for Forward MPC operations",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",