@onekeyfe/hwk-adapter-core 1.2.4 → 1.2.5-alpha.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.
Files changed (37) hide show
  1. package/dist/{chunk-U63HTLGJ.mjs → chunk-AYA6FQES.mjs} +44 -12
  2. package/dist/chunk-AYA6FQES.mjs.map +1 -0
  3. package/dist/chunk-DB4JOPZO.mjs +74 -0
  4. package/dist/chunk-DB4JOPZO.mjs.map +1 -0
  5. package/dist/{chunk-BAGEFFFY.js → chunk-H7BZ2C6J.js} +45 -13
  6. package/dist/chunk-H7BZ2C6J.js.map +1 -0
  7. package/dist/{chunk-YA4BWFTZ.mjs → chunk-KTRQZ5AJ.mjs} +155 -55
  8. package/dist/chunk-KTRQZ5AJ.mjs.map +1 -0
  9. package/dist/{chunk-SZZF623R.js → chunk-T5HIQPNG.js} +155 -55
  10. package/dist/chunk-T5HIQPNG.js.map +1 -0
  11. package/dist/chunk-YLCHSNXO.js +74 -0
  12. package/dist/chunk-YLCHSNXO.js.map +1 -0
  13. package/dist/errors-BgJ4kzRx.d.mts +276 -0
  14. package/dist/errors-BgJ4kzRx.d.ts +276 -0
  15. package/dist/errors.d.mts +1 -207
  16. package/dist/errors.d.ts +1 -207
  17. package/dist/errors.js +8 -2
  18. package/dist/errors.js.map +1 -1
  19. package/dist/errors.mjs +9 -3
  20. package/dist/index.d.mts +578 -104
  21. package/dist/index.d.ts +578 -104
  22. package/dist/index.js +635 -85
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.mjs +626 -76
  25. package/dist/index.mjs.map +1 -1
  26. package/dist/{ui-events-Cb7haAeE.d.mts → ui-events-DdttFKj9.d.mts} +37 -3
  27. package/dist/{ui-events-Cb7haAeE.d.ts → ui-events-DdttFKj9.d.ts} +37 -3
  28. package/dist/ui-events.d.mts +1 -1
  29. package/dist/ui-events.d.ts +1 -1
  30. package/dist/ui-events.js +3 -2
  31. package/dist/ui-events.js.map +1 -1
  32. package/dist/ui-events.mjs +2 -1
  33. package/package.json +2 -2
  34. package/dist/chunk-BAGEFFFY.js.map +0 -1
  35. package/dist/chunk-SZZF623R.js.map +0 -1
  36. package/dist/chunk-U63HTLGJ.mjs.map +0 -1
  37. package/dist/chunk-YA4BWFTZ.mjs.map +0 -1
@@ -1,3 +1,7 @@
1
+ import {
2
+ createHwkError
3
+ } from "./chunk-KTRQZ5AJ.mjs";
4
+
1
5
  // src/events/ui-request.ts
2
6
  var UI_EVENT = "UI_EVENT";
3
7
  var UI_REQUEST = {
@@ -11,6 +15,8 @@ var UI_REQUEST = {
11
15
  REQUEST_QR_SCAN: "ui-request-qr-scan",
12
16
  REQUEST_DEVICE_PERMISSION: "ui-request-device-permission",
13
17
  REQUEST_SELECT_DEVICE: "ui-request-select-device",
18
+ REQUEST_SAVE_DEVICE_BINDING: "ui-request-save-device-binding",
19
+ DEVICE_BINDING_STATUS: "ui-device-binding-status",
14
20
  REQUEST_DEVICE_CONNECT: "ui-request-device-connect",
15
21
  // Ledger BTC App: account index >= 100 requires display=true. Adapter asks
16
22
  // the user once per session before promoting the call.
@@ -30,6 +36,7 @@ var UI_RESPONSE = {
30
36
  RECEIVE_PASSPHRASE: "receive-passphrase",
31
37
  RECEIVE_QR_RESPONSE: "receive-qr-response",
32
38
  RECEIVE_SELECT_DEVICE: "receive-select-device",
39
+ RECEIVE_SAVE_DEVICE_BINDING: "receive-save-device-binding",
33
40
  RECEIVE_DEVICE_CONNECT: "receive-device-connect",
34
41
  RECEIVE_DEVICE_PERMISSION: "receive-device-permission",
35
42
  RECEIVE_BTC_HIGH_INDEX_CONFIRM: "receive-btc-high-index-confirm",
@@ -45,6 +52,7 @@ var RESPONSE_TO_REQUEST = {
45
52
  [UI_RESPONSE.RECEIVE_PIN]: UI_REQUEST.REQUEST_PIN,
46
53
  [UI_RESPONSE.RECEIVE_PASSPHRASE]: UI_REQUEST.REQUEST_PASSPHRASE,
47
54
  [UI_RESPONSE.RECEIVE_SELECT_DEVICE]: UI_REQUEST.REQUEST_SELECT_DEVICE,
55
+ [UI_RESPONSE.RECEIVE_SAVE_DEVICE_BINDING]: UI_REQUEST.REQUEST_SAVE_DEVICE_BINDING,
48
56
  [UI_RESPONSE.RECEIVE_DEVICE_CONNECT]: UI_REQUEST.REQUEST_DEVICE_CONNECT,
49
57
  [UI_RESPONSE.RECEIVE_DEVICE_PERMISSION]: UI_REQUEST.REQUEST_DEVICE_PERMISSION,
50
58
  [UI_RESPONSE.RECEIVE_BTC_HIGH_INDEX_CONFIRM]: UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM,
@@ -56,10 +64,15 @@ var RESPONSE_TO_REQUEST = {
56
64
  var UI_REQUEST_PREEMPTED_TAG = "UiRequestPreempted";
57
65
  var UI_REQUEST_CANCELLED_TAG = "UiRequestCancelled";
58
66
  var UI_REQUEST_TIMEOUT_TAG = "UiRequestTimeout";
67
+ var requestSequence = 0;
59
68
  var UiRequestRegistry = class {
60
69
  constructor() {
61
70
  this.pending = /* @__PURE__ */ new Map();
62
71
  }
72
+ createRequestId() {
73
+ requestSequence += 1;
74
+ return `hwk-ui-${Date.now()}-${requestSequence}`;
75
+ }
63
76
  wait(requestType, options) {
64
77
  const existing = this.pending.get(requestType);
65
78
  if (existing) {
@@ -84,6 +97,8 @@ var UiRequestRegistry = class {
84
97
  }
85
98
  }, timeoutMs);
86
99
  this.pending.set(requestType, {
100
+ requestId: options?.requestId,
101
+ operationId: options?.operationId,
87
102
  resolve,
88
103
  reject,
89
104
  timer
@@ -102,32 +117,49 @@ var UiRequestRegistry = class {
102
117
  if (!requestType) return;
103
118
  const entry = this.pending.get(requestType);
104
119
  if (!entry) return;
120
+ const response = payload && typeof payload === "object" ? payload : void 0;
121
+ if (entry.requestId !== void 0 && response?.requestId !== entry.requestId) return;
105
122
  clearTimeout(entry.timer);
106
123
  this.pending.delete(requestType);
107
- entry.resolve(payload);
108
- }
109
- cancel(requestType) {
110
- if (requestType) {
111
- const entry = this.pending.get(requestType);
112
- if (!entry) return;
113
- clearTimeout(entry.timer);
114
- this.pending.delete(requestType);
124
+ if (requestType === UI_REQUEST.REQUEST_SELECT_DEVICE && response?.cancelled === true) {
115
125
  entry.reject(
116
- Object.assign(new Error(`UI request '${requestType}' was cancelled`), {
126
+ createHwkError({
127
+ code: 10005 /* UserAborted */,
128
+ message: "Device selection was cancelled",
117
129
  _tag: UI_REQUEST_CANCELLED_TAG
118
130
  })
119
131
  );
120
132
  return;
121
133
  }
122
- for (const [type, entry] of this.pending) {
134
+ entry.resolve(payload);
135
+ }
136
+ /**
137
+ * Each argument narrows the cancel: none rejects every waiter (teardown); an `operationId` alone
138
+ * rejects only that operation's prompts and leaves another operation's PIN or QR wait untouched.
139
+ */
140
+ cancel(requestType, requestId, operationId) {
141
+ const reject = (type, entry) => {
123
142
  clearTimeout(entry.timer);
143
+ this.pending.delete(type);
124
144
  entry.reject(
125
145
  Object.assign(new Error(`UI request '${type}' was cancelled`), {
126
146
  _tag: UI_REQUEST_CANCELLED_TAG
127
147
  })
128
148
  );
149
+ };
150
+ if (requestType) {
151
+ const entry = this.pending.get(requestType);
152
+ if (!entry) return;
153
+ if (requestId !== void 0 && entry.requestId !== requestId) return;
154
+ if (operationId !== void 0 && entry.operationId !== operationId) return;
155
+ reject(requestType, entry);
156
+ return;
157
+ }
158
+ for (const [type, entry] of [...this.pending]) {
159
+ if (operationId === void 0 || entry.operationId === operationId) {
160
+ reject(type, entry);
161
+ }
129
162
  }
130
- this.pending.clear();
131
163
  }
132
164
  reset() {
133
165
  this.cancel();
@@ -148,4 +180,4 @@ export {
148
180
  UI_REQUEST_TIMEOUT_TAG,
149
181
  UiRequestRegistry
150
182
  };
151
- //# sourceMappingURL=chunk-U63HTLGJ.mjs.map
183
+ //# sourceMappingURL=chunk-AYA6FQES.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/events/ui-request.ts","../src/utils/UiRequestRegistry.ts"],"sourcesContent":["import type { QrResponseData } from '../types/qr';\n\nexport const UI_EVENT = 'UI_EVENT';\n\nexport const UI_REQUEST = {\n REQUEST_PIN: 'ui-request-pin',\n REQUEST_PASSPHRASE: 'ui-request-passphrase',\n // One-way UI notification: device is waiting for passphrase entry on-device.\n // The prompt response path remains RECEIVE_PASSPHRASE with passphraseOnDevice=true.\n REQUEST_PASSPHRASE_ON_DEVICE: 'ui-request-passphrase-on-device',\n REQUEST_BUTTON: 'ui-request-button',\n REQUEST_QR_DISPLAY: 'ui-request-qr-display',\n REQUEST_QR_SCAN: 'ui-request-qr-scan',\n REQUEST_DEVICE_PERMISSION: 'ui-request-device-permission',\n REQUEST_SELECT_DEVICE: 'ui-request-select-device',\n REQUEST_SAVE_DEVICE_BINDING: 'ui-request-save-device-binding',\n DEVICE_BINDING_STATUS: 'ui-device-binding-status',\n REQUEST_DEVICE_CONNECT: 'ui-request-device-connect',\n // Ledger BTC App: account index >= 100 requires display=true. Adapter asks\n // the user once per session before promoting the call.\n REQUEST_BTC_HIGH_INDEX_CONFIRM: 'ui-request-btc-high-index-confirm',\n // Required app is not installed; with autoInstallApp on, ask the user\n // whether to install it before retrying the operation.\n REQUEST_INSTALL_APP: 'ui-request-install-app',\n REQUEST_PREEMPTION: 'ui-request-preemption',\n REQUEST_TREZOR_THP_PAIRING: 'ui-request-trezor-thp-pairing',\n CLOSE_UI_WINDOW: 'ui-close',\n DEVICE_PROGRESS: 'ui-device_progress',\n FIRMWARE_PROGRESS: 'ui-firmware-progress',\n FIRMWARE_TIP: 'ui-firmware-tip',\n} as const;\n\nexport const UI_RESPONSE = {\n RECEIVE_PIN: 'receive-pin',\n RECEIVE_PASSPHRASE: 'receive-passphrase',\n RECEIVE_QR_RESPONSE: 'receive-qr-response',\n RECEIVE_SELECT_DEVICE: 'receive-select-device',\n RECEIVE_SAVE_DEVICE_BINDING: 'receive-save-device-binding',\n RECEIVE_DEVICE_CONNECT: 'receive-device-connect',\n RECEIVE_DEVICE_PERMISSION: 'receive-device-permission',\n RECEIVE_BTC_HIGH_INDEX_CONFIRM: 'receive-btc-high-index-confirm',\n RECEIVE_INSTALL_APP: 'receive-install-app',\n RECEIVE_PREEMPTION: 'receive-preemption',\n RECEIVE_TREZOR_THP_PAIRING: 'receive-trezor-thp-pairing',\n CANCEL: 'cancel',\n} as const;\n\n/**\n * The operation a UI request was opened under, so a cancel for one operation spares another's\n * prompts. Absent outside any operation (cold start, teardown).\n */\nexport type UiRequestOperationAttribution = {\n operationId?: string;\n};\n\nexport type DevicePermissionDeniedReason =\n | 'bluetoothTurnedOff'\n | 'permissionDenied'\n | (string & Record<never, never>);\n\nexport type DevicePermissionResponse = {\n granted: boolean;\n reason?: DevicePermissionDeniedReason;\n message?: string;\n};\n\n/**\n * `mismatch`: the selected device is not this operation's device, surfaced as `DeviceMismatch`.\n * `skipped`: the host had nothing to bind, a host contract problem rather than a user action.\n */\nexport type SaveDeviceBindingDeclineReason = 'mismatch' | 'skipped';\n\nexport type UiResponseEvent =\n | {\n type: typeof UI_RESPONSE.RECEIVE_SAVE_DEVICE_BINDING;\n payload: {\n requestId: string;\n saved: boolean;\n reason?: SaveDeviceBindingDeclineReason;\n };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_PIN;\n payload: string;\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_PASSPHRASE;\n payload: {\n value: string;\n passphraseOnDevice?: boolean;\n save?: boolean;\n };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_QR_RESPONSE;\n payload: QrResponseData;\n }\n | {\n // sdkConnectId echoes one of the DeviceInfo.connectId values emitted in\n // the REQUEST_SELECT_DEVICE event's `devices` list. Scope is the current\n // search session; may be ephemeral (e.g. Ledger USB DMK UUID).\n type: typeof UI_RESPONSE.RECEIVE_SELECT_DEVICE;\n payload: { requestId?: string } & (\n | { sdkConnectId: string; cancelled?: false }\n | { cancelled: true }\n );\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_DEVICE_CONNECT;\n payload: { confirmed: boolean };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_DEVICE_PERMISSION;\n payload: DevicePermissionResponse;\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_BTC_HIGH_INDEX_CONFIRM;\n payload: { confirmed: boolean };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_INSTALL_APP;\n payload: { confirmed: boolean };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_PREEMPTION;\n payload: { confirmed: boolean };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_TREZOR_THP_PAIRING;\n payload: {\n selectedMethod?: number | string;\n tag?: string;\n };\n }\n | {\n type: typeof UI_RESPONSE.CANCEL;\n payload?: undefined;\n };\n","import { UI_REQUEST, UI_RESPONSE } from '../events/ui-request';\nimport { createHwkError, HardwareErrorCode } from '../types/errors';\n\n/** 10 min — every UI request is human-in-the-loop; bias toward \"wait long\". */\nexport const UI_REQUEST_DEFAULT_TIMEOUT_MS = 600_000;\n\nconst RESPONSE_TO_REQUEST: Record<string, string> = {\n [UI_RESPONSE.RECEIVE_PIN]: UI_REQUEST.REQUEST_PIN,\n [UI_RESPONSE.RECEIVE_PASSPHRASE]: UI_REQUEST.REQUEST_PASSPHRASE,\n [UI_RESPONSE.RECEIVE_SELECT_DEVICE]: UI_REQUEST.REQUEST_SELECT_DEVICE,\n [UI_RESPONSE.RECEIVE_SAVE_DEVICE_BINDING]: UI_REQUEST.REQUEST_SAVE_DEVICE_BINDING,\n [UI_RESPONSE.RECEIVE_DEVICE_CONNECT]: UI_REQUEST.REQUEST_DEVICE_CONNECT,\n [UI_RESPONSE.RECEIVE_DEVICE_PERMISSION]: UI_REQUEST.REQUEST_DEVICE_PERMISSION,\n [UI_RESPONSE.RECEIVE_BTC_HIGH_INDEX_CONFIRM]: UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM,\n [UI_RESPONSE.RECEIVE_INSTALL_APP]: UI_REQUEST.REQUEST_INSTALL_APP,\n [UI_RESPONSE.RECEIVE_PREEMPTION]: UI_REQUEST.REQUEST_PREEMPTION,\n [UI_RESPONSE.RECEIVE_TREZOR_THP_PAIRING]: UI_REQUEST.REQUEST_TREZOR_THP_PAIRING,\n // RECEIVE_QR_RESPONSE maps to QR_DISPLAY or QR_SCAN — resolved dynamically below.\n};\n\nexport const UI_REQUEST_PREEMPTED_TAG = 'UiRequestPreempted';\nexport const UI_REQUEST_CANCELLED_TAG = 'UiRequestCancelled';\nexport const UI_REQUEST_TIMEOUT_TAG = 'UiRequestTimeout';\n\nlet requestSequence = 0;\n\ntype PendingEntry = {\n requestId?: string;\n /** The operation this waiter belongs to, when it was opened under one. */\n operationId?: string;\n resolve: (payload: unknown) => void;\n reject: (error: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n};\n\n/**\n * Per-type single-slot registry for adapter-level UI requests. A new `wait`\n * of the same type supersedes (rejects) the prior pending entry. With the\n * job queue running globally serially, cross-type collisions don't occur in\n * normal flow — same-type preemption is a defensive measure for callers\n * that fire the same request twice without waiting on the first.\n *\n * Unknown response types are dropped silently so the public `uiResponse`\n * entry never throws.\n */\nexport class UiRequestRegistry {\n private pending = new Map<string, PendingEntry>();\n\n createRequestId(): string {\n requestSequence += 1;\n return `hwk-ui-${Date.now()}-${requestSequence}`;\n }\n\n wait<T = unknown>(\n requestType: string,\n options?: { timeoutMs?: number; requestId?: string; operationId?: string }\n ): Promise<T> {\n const existing = this.pending.get(requestType);\n if (existing) {\n clearTimeout(existing.timer);\n this.pending.delete(requestType);\n existing.reject(\n Object.assign(new Error(`UI request '${requestType}' was superseded by a new request`), {\n _tag: UI_REQUEST_PREEMPTED_TAG,\n })\n );\n }\n\n const timeoutMs = options?.timeoutMs ?? UI_REQUEST_DEFAULT_TIMEOUT_MS;\n\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n if (this.pending.get(requestType)?.timer === timer) {\n this.pending.delete(requestType);\n reject(\n Object.assign(new Error(`UI request '${requestType}' timed out after ${timeoutMs}ms`), {\n _tag: UI_REQUEST_TIMEOUT_TAG,\n })\n );\n }\n }, timeoutMs);\n\n this.pending.set(requestType, {\n requestId: options?.requestId,\n operationId: options?.operationId,\n resolve: resolve as (payload: unknown) => void,\n reject,\n timer,\n });\n });\n }\n\n resolve(responseType: string, payload: unknown): void {\n let requestType = RESPONSE_TO_REQUEST[responseType];\n if (responseType === UI_RESPONSE.RECEIVE_QR_RESPONSE) {\n if (this.pending.has(UI_REQUEST.REQUEST_QR_DISPLAY)) {\n requestType = UI_REQUEST.REQUEST_QR_DISPLAY;\n } else if (this.pending.has(UI_REQUEST.REQUEST_QR_SCAN)) {\n requestType = UI_REQUEST.REQUEST_QR_SCAN;\n }\n }\n if (!requestType) return;\n\n const entry = this.pending.get(requestType);\n if (!entry) return;\n\n const response =\n payload && typeof payload === 'object'\n ? (payload as { requestId?: unknown; cancelled?: unknown })\n : undefined;\n // A late response must not resolve (or cancel) a newer request of the same type.\n if (entry.requestId !== undefined && response?.requestId !== entry.requestId) return;\n clearTimeout(entry.timer);\n this.pending.delete(requestType);\n if (requestType === UI_REQUEST.REQUEST_SELECT_DEVICE && response?.cancelled === true) {\n entry.reject(\n createHwkError({\n code: HardwareErrorCode.UserAborted,\n message: 'Device selection was cancelled',\n _tag: UI_REQUEST_CANCELLED_TAG,\n })\n );\n return;\n }\n entry.resolve(payload);\n }\n\n /**\n * Each argument narrows the cancel: none rejects every waiter (teardown); an `operationId` alone\n * rejects only that operation's prompts and leaves another operation's PIN or QR wait untouched.\n */\n cancel(requestType?: string, requestId?: string, operationId?: string): void {\n const reject = (type: string, entry: PendingEntry) => {\n clearTimeout(entry.timer);\n this.pending.delete(type);\n entry.reject(\n Object.assign(new Error(`UI request '${type}' was cancelled`), {\n _tag: UI_REQUEST_CANCELLED_TAG,\n })\n );\n };\n\n if (requestType) {\n const entry = this.pending.get(requestType);\n if (!entry) return;\n if (requestId !== undefined && entry.requestId !== requestId) return;\n if (operationId !== undefined && entry.operationId !== operationId) return;\n reject(requestType, entry);\n return;\n }\n\n for (const [type, entry] of [...this.pending]) {\n if (operationId === undefined || entry.operationId === operationId) {\n reject(type, entry);\n }\n }\n }\n\n reset(): void {\n this.cancel();\n }\n\n hasPending(requestType?: string): boolean {\n if (requestType) return this.pending.has(requestType);\n return this.pending.size > 0;\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,74 @@
1
+ // src/utils/errorMessages.ts
2
+ function enrichErrorMessage(code, originalMessage) {
3
+ switch (code) {
4
+ case 10400 /* PinInvalid */:
5
+ return `${originalMessage}. Please re-enter your PIN.`;
6
+ case 10401 /* PinCancelled */:
7
+ return `${originalMessage}. PIN entry was cancelled.`;
8
+ case 10404 /* PinMismatch */:
9
+ return `${originalMessage}. The entered PINs do not match. Please try again.`;
10
+ case 10102 /* DeviceBusy */:
11
+ return `${originalMessage}. The device is in use by another application. Close other wallet apps and try again.`;
12
+ case 10111 /* DeviceBusyInternal */:
13
+ return `${originalMessage}. The device is busy. Please try again in a moment.`;
14
+ case 10101 /* DeviceDisconnected */:
15
+ return `${originalMessage}. Please reconnect the device and try again.`;
16
+ case 10103 /* DeviceLocked */:
17
+ return `${originalMessage}. Please unlock your device and try again.`;
18
+ case 10109 /* DeviceOneDeviceOnly */:
19
+ return `${originalMessage}. Disconnect the extra device and try again.`;
20
+ case 10001 /* UserRejected */:
21
+ return `${originalMessage}. The request was rejected on the device.`;
22
+ case 10501 /* WrongApp */:
23
+ return `${originalMessage}. Please open the correct app on your device.`;
24
+ case 10500 /* AppNotInstalled */:
25
+ return `${originalMessage}. The required app is not installed on the device.`;
26
+ case 10503 /* DeviceOutOfMemory */:
27
+ return `${originalMessage}. Not enough free space on the device. Please uninstall some apps and try again.`;
28
+ case 10302 /* TransportNotAvailable */:
29
+ return `${originalMessage}. Ensure the device bridge/transport is available and running.`;
30
+ case 10305 /* NetworkError */:
31
+ return `${originalMessage}. Network error \u2014 check your internet connection and try again.`;
32
+ case 10311 /* LedgerSecureChannelError */:
33
+ return `${originalMessage}. The secure connection to Ledger's servers was interrupted. Keep the device unlocked on the dashboard and try again.`;
34
+ case 10312 /* LedgerFirmwareMetadataError */:
35
+ return `${originalMessage}. Ledger's app catalog returned an unusable response. Please try again later.`;
36
+ case 10504 /* AppAlreadyInstalled */:
37
+ return `${originalMessage}. The app is already installed on the device.`;
38
+ case 10200 /* FirmwareTooOld */:
39
+ return `${originalMessage}. Please update your device firmware.`;
40
+ case 10104 /* DeviceNotInitialized */:
41
+ return `${originalMessage}. The device may need to be set up first.`;
42
+ case 10003 /* OperationTimeout */:
43
+ return `${originalMessage}. The operation timed out. Please try again.`;
44
+ case 11e3 /* EvmBlindSigningRequired */:
45
+ return "Ledger: Blind signing not enabled in Ethereum app.";
46
+ case 11001 /* EvmClearSignPluginMissing */:
47
+ return "Ledger: Required plugin not installed (install via Ledger Live).";
48
+ case 11002 /* EvmDataTooLarge */:
49
+ return "Ledger: Transaction data too large for device memory.";
50
+ case 11003 /* EvmTxTypeNotSupported */:
51
+ return "Ledger: Transaction type not supported by current Ethereum app.";
52
+ case 10502 /* AppTooOld */:
53
+ return "Ledger: App out of date (update via Ledger Live).";
54
+ case 11100 /* SolanaBlindSigningRequired */:
55
+ return "Blind signing disabled in the Solana app.";
56
+ case 11200 /* TronCustomContractRequired */:
57
+ return "Custom Contracts setting disabled in the Tron app.";
58
+ case 11201 /* TronDataSigningRequired */:
59
+ return "Transactions Data setting disabled in the Tron app.";
60
+ case 11202 /* TronSignByHashRequired */:
61
+ return "Sign by Hash setting disabled in the Tron app.";
62
+ case 11300 /* BtcWalletPolicyHmacMismatch */:
63
+ return "Wallet policy not registered or HMAC mismatch.";
64
+ case 11301 /* BtcUnexpectedState */:
65
+ return "Signing aborted due to unexpected device state.";
66
+ default:
67
+ return originalMessage;
68
+ }
69
+ }
70
+
71
+ export {
72
+ enrichErrorMessage
73
+ };
74
+ //# sourceMappingURL=chunk-DB4JOPZO.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/errorMessages.ts"],"sourcesContent":["import { HardwareErrorCode } from '../types/errors';\n\n/**\n * Enrich a hardware error message with actionable recovery hints.\n * Shared across adapters (Ledger, Trezor, etc.).\n */\nexport function enrichErrorMessage(code: HardwareErrorCode, originalMessage: string): string {\n switch (code) {\n case HardwareErrorCode.PinInvalid:\n return `${originalMessage}. Please re-enter your PIN.`;\n case HardwareErrorCode.PinCancelled:\n return `${originalMessage}. PIN entry was cancelled.`;\n case HardwareErrorCode.PinMismatch:\n return `${originalMessage}. The entered PINs do not match. Please try again.`;\n case HardwareErrorCode.DeviceBusy:\n return `${originalMessage}. The device is in use by another application. Close other wallet apps and try again.`;\n case HardwareErrorCode.DeviceBusyInternal:\n return `${originalMessage}. The device is busy. Please try again in a moment.`;\n case HardwareErrorCode.DeviceDisconnected:\n return `${originalMessage}. Please reconnect the device and try again.`;\n case HardwareErrorCode.DeviceLocked:\n return `${originalMessage}. Please unlock your device and try again.`;\n case HardwareErrorCode.DeviceOneDeviceOnly:\n return `${originalMessage}. Disconnect the extra device and try again.`;\n case HardwareErrorCode.UserRejected:\n return `${originalMessage}. The request was rejected on the device.`;\n case HardwareErrorCode.WrongApp:\n return `${originalMessage}. Please open the correct app on your device.`;\n case HardwareErrorCode.AppNotInstalled:\n return `${originalMessage}. The required app is not installed on the device.`;\n case HardwareErrorCode.DeviceOutOfMemory:\n return `${originalMessage}. Not enough free space on the device. Please uninstall some apps and try again.`;\n case HardwareErrorCode.TransportNotAvailable:\n return `${originalMessage}. Ensure the device bridge/transport is available and running.`;\n case HardwareErrorCode.NetworkError:\n return `${originalMessage}. Network error — check your internet connection and try again.`;\n case HardwareErrorCode.LedgerSecureChannelError:\n return `${originalMessage}. The secure connection to Ledger's servers was interrupted. Keep the device unlocked on the dashboard and try again.`;\n case HardwareErrorCode.LedgerFirmwareMetadataError:\n return `${originalMessage}. Ledger's app catalog returned an unusable response. Please try again later.`;\n case HardwareErrorCode.AppAlreadyInstalled:\n return `${originalMessage}. The app is already installed on the device.`;\n case HardwareErrorCode.FirmwareTooOld:\n return `${originalMessage}. Please update your device firmware.`;\n case HardwareErrorCode.DeviceNotInitialized:\n return `${originalMessage}. The device may need to be set up first.`;\n case HardwareErrorCode.OperationTimeout:\n return `${originalMessage}. The operation timed out. Please try again.`;\n case HardwareErrorCode.EvmBlindSigningRequired:\n return 'Ledger: Blind signing not enabled in Ethereum app.';\n case HardwareErrorCode.EvmClearSignPluginMissing:\n return 'Ledger: Required plugin not installed (install via Ledger Live).';\n case HardwareErrorCode.EvmDataTooLarge:\n return 'Ledger: Transaction data too large for device memory.';\n case HardwareErrorCode.EvmTxTypeNotSupported:\n return 'Ledger: Transaction type not supported by current Ethereum app.';\n case HardwareErrorCode.AppTooOld:\n return 'Ledger: App out of date (update via Ledger Live).';\n case HardwareErrorCode.SolanaBlindSigningRequired:\n return 'Blind signing disabled in the Solana app.';\n case HardwareErrorCode.TronCustomContractRequired:\n return 'Custom Contracts setting disabled in the Tron app.';\n case HardwareErrorCode.TronDataSigningRequired:\n return 'Transactions Data setting disabled in the Tron app.';\n case HardwareErrorCode.TronSignByHashRequired:\n return 'Sign by Hash setting disabled in the Tron app.';\n case HardwareErrorCode.BtcWalletPolicyHmacMismatch:\n return 'Wallet policy not registered or HMAC mismatch.';\n case HardwareErrorCode.BtcUnexpectedState:\n return 'Signing aborted due to unexpected device state.';\n default:\n return originalMessage;\n }\n}\n"],"mappings":";AAMO,SAAS,mBAAmB,MAAyB,iBAAiC;AAC3F,UAAQ,MAAM;AAAA,IACZ;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO;AAAA,IACT;AACE,aAAO;AAAA,IACT;AACE,aAAO;AAAA,IACT;AACE,aAAO;AAAA,IACT;AACE,aAAO;AAAA,IACT;AACE,aAAO;AAAA,IACT;AACE,aAAO;AAAA,IACT;AACE,aAAO;AAAA,IACT;AACE,aAAO;AAAA,IACT;AACE,aAAO;AAAA,IACT;AACE,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;","names":[]}
@@ -1,4 +1,8 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/events/ui-request.ts
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
+
3
+ var _chunkT5HIQPNGjs = require('./chunk-T5HIQPNG.js');
4
+
5
+ // src/events/ui-request.ts
2
6
  var UI_EVENT = "UI_EVENT";
3
7
  var UI_REQUEST = {
4
8
  REQUEST_PIN: "ui-request-pin",
@@ -11,6 +15,8 @@ var UI_REQUEST = {
11
15
  REQUEST_QR_SCAN: "ui-request-qr-scan",
12
16
  REQUEST_DEVICE_PERMISSION: "ui-request-device-permission",
13
17
  REQUEST_SELECT_DEVICE: "ui-request-select-device",
18
+ REQUEST_SAVE_DEVICE_BINDING: "ui-request-save-device-binding",
19
+ DEVICE_BINDING_STATUS: "ui-device-binding-status",
14
20
  REQUEST_DEVICE_CONNECT: "ui-request-device-connect",
15
21
  // Ledger BTC App: account index >= 100 requires display=true. Adapter asks
16
22
  // the user once per session before promoting the call.
@@ -30,6 +36,7 @@ var UI_RESPONSE = {
30
36
  RECEIVE_PASSPHRASE: "receive-passphrase",
31
37
  RECEIVE_QR_RESPONSE: "receive-qr-response",
32
38
  RECEIVE_SELECT_DEVICE: "receive-select-device",
39
+ RECEIVE_SAVE_DEVICE_BINDING: "receive-save-device-binding",
33
40
  RECEIVE_DEVICE_CONNECT: "receive-device-connect",
34
41
  RECEIVE_DEVICE_PERMISSION: "receive-device-permission",
35
42
  RECEIVE_BTC_HIGH_INDEX_CONFIRM: "receive-btc-high-index-confirm",
@@ -45,6 +52,7 @@ var RESPONSE_TO_REQUEST = {
45
52
  [UI_RESPONSE.RECEIVE_PIN]: UI_REQUEST.REQUEST_PIN,
46
53
  [UI_RESPONSE.RECEIVE_PASSPHRASE]: UI_REQUEST.REQUEST_PASSPHRASE,
47
54
  [UI_RESPONSE.RECEIVE_SELECT_DEVICE]: UI_REQUEST.REQUEST_SELECT_DEVICE,
55
+ [UI_RESPONSE.RECEIVE_SAVE_DEVICE_BINDING]: UI_REQUEST.REQUEST_SAVE_DEVICE_BINDING,
48
56
  [UI_RESPONSE.RECEIVE_DEVICE_CONNECT]: UI_REQUEST.REQUEST_DEVICE_CONNECT,
49
57
  [UI_RESPONSE.RECEIVE_DEVICE_PERMISSION]: UI_REQUEST.REQUEST_DEVICE_PERMISSION,
50
58
  [UI_RESPONSE.RECEIVE_BTC_HIGH_INDEX_CONFIRM]: UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM,
@@ -56,10 +64,15 @@ var RESPONSE_TO_REQUEST = {
56
64
  var UI_REQUEST_PREEMPTED_TAG = "UiRequestPreempted";
57
65
  var UI_REQUEST_CANCELLED_TAG = "UiRequestCancelled";
58
66
  var UI_REQUEST_TIMEOUT_TAG = "UiRequestTimeout";
67
+ var requestSequence = 0;
59
68
  var UiRequestRegistry = class {
60
69
  constructor() {
61
70
  this.pending = /* @__PURE__ */ new Map();
62
71
  }
72
+ createRequestId() {
73
+ requestSequence += 1;
74
+ return `hwk-ui-${Date.now()}-${requestSequence}`;
75
+ }
63
76
  wait(requestType, options) {
64
77
  const existing = this.pending.get(requestType);
65
78
  if (existing) {
@@ -84,6 +97,8 @@ var UiRequestRegistry = class {
84
97
  }
85
98
  }, timeoutMs);
86
99
  this.pending.set(requestType, {
100
+ requestId: _optionalChain([options, 'optionalAccess', _6 => _6.requestId]),
101
+ operationId: _optionalChain([options, 'optionalAccess', _7 => _7.operationId]),
87
102
  resolve,
88
103
  reject,
89
104
  timer
@@ -102,32 +117,49 @@ var UiRequestRegistry = class {
102
117
  if (!requestType) return;
103
118
  const entry = this.pending.get(requestType);
104
119
  if (!entry) return;
120
+ const response = payload && typeof payload === "object" ? payload : void 0;
121
+ if (entry.requestId !== void 0 && _optionalChain([response, 'optionalAccess', _8 => _8.requestId]) !== entry.requestId) return;
105
122
  clearTimeout(entry.timer);
106
123
  this.pending.delete(requestType);
107
- entry.resolve(payload);
108
- }
109
- cancel(requestType) {
110
- if (requestType) {
111
- const entry = this.pending.get(requestType);
112
- if (!entry) return;
113
- clearTimeout(entry.timer);
114
- this.pending.delete(requestType);
124
+ if (requestType === UI_REQUEST.REQUEST_SELECT_DEVICE && _optionalChain([response, 'optionalAccess', _9 => _9.cancelled]) === true) {
115
125
  entry.reject(
116
- Object.assign(new Error(`UI request '${requestType}' was cancelled`), {
126
+ _chunkT5HIQPNGjs.createHwkError.call(void 0, {
127
+ code: 10005 /* UserAborted */,
128
+ message: "Device selection was cancelled",
117
129
  _tag: UI_REQUEST_CANCELLED_TAG
118
130
  })
119
131
  );
120
132
  return;
121
133
  }
122
- for (const [type, entry] of this.pending) {
134
+ entry.resolve(payload);
135
+ }
136
+ /**
137
+ * Each argument narrows the cancel: none rejects every waiter (teardown); an `operationId` alone
138
+ * rejects only that operation's prompts and leaves another operation's PIN or QR wait untouched.
139
+ */
140
+ cancel(requestType, requestId, operationId) {
141
+ const reject = (type, entry) => {
123
142
  clearTimeout(entry.timer);
143
+ this.pending.delete(type);
124
144
  entry.reject(
125
145
  Object.assign(new Error(`UI request '${type}' was cancelled`), {
126
146
  _tag: UI_REQUEST_CANCELLED_TAG
127
147
  })
128
148
  );
149
+ };
150
+ if (requestType) {
151
+ const entry = this.pending.get(requestType);
152
+ if (!entry) return;
153
+ if (requestId !== void 0 && entry.requestId !== requestId) return;
154
+ if (operationId !== void 0 && entry.operationId !== operationId) return;
155
+ reject(requestType, entry);
156
+ return;
157
+ }
158
+ for (const [type, entry] of [...this.pending]) {
159
+ if (operationId === void 0 || entry.operationId === operationId) {
160
+ reject(type, entry);
161
+ }
129
162
  }
130
- this.pending.clear();
131
163
  }
132
164
  reset() {
133
165
  this.cancel();
@@ -148,4 +180,4 @@ var UiRequestRegistry = class {
148
180
 
149
181
 
150
182
  exports.UI_EVENT = UI_EVENT; exports.UI_REQUEST = UI_REQUEST; exports.UI_RESPONSE = UI_RESPONSE; exports.UI_REQUEST_DEFAULT_TIMEOUT_MS = UI_REQUEST_DEFAULT_TIMEOUT_MS; exports.UI_REQUEST_PREEMPTED_TAG = UI_REQUEST_PREEMPTED_TAG; exports.UI_REQUEST_CANCELLED_TAG = UI_REQUEST_CANCELLED_TAG; exports.UI_REQUEST_TIMEOUT_TAG = UI_REQUEST_TIMEOUT_TAG; exports.UiRequestRegistry = UiRequestRegistry;
151
- //# sourceMappingURL=chunk-BAGEFFFY.js.map
183
+ //# sourceMappingURL=chunk-H7BZ2C6J.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/hardware-js-sdk/hardware-js-sdk/packages/hwk-adapter-core/dist/chunk-H7BZ2C6J.js","../src/events/ui-request.ts","../src/utils/UiRequestRegistry.ts"],"names":[],"mappings":"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","file":"/home/runner/work/hardware-js-sdk/hardware-js-sdk/packages/hwk-adapter-core/dist/chunk-H7BZ2C6J.js","sourcesContent":[null,"import type { QrResponseData } from '../types/qr';\n\nexport const UI_EVENT = 'UI_EVENT';\n\nexport const UI_REQUEST = {\n REQUEST_PIN: 'ui-request-pin',\n REQUEST_PASSPHRASE: 'ui-request-passphrase',\n // One-way UI notification: device is waiting for passphrase entry on-device.\n // The prompt response path remains RECEIVE_PASSPHRASE with passphraseOnDevice=true.\n REQUEST_PASSPHRASE_ON_DEVICE: 'ui-request-passphrase-on-device',\n REQUEST_BUTTON: 'ui-request-button',\n REQUEST_QR_DISPLAY: 'ui-request-qr-display',\n REQUEST_QR_SCAN: 'ui-request-qr-scan',\n REQUEST_DEVICE_PERMISSION: 'ui-request-device-permission',\n REQUEST_SELECT_DEVICE: 'ui-request-select-device',\n REQUEST_SAVE_DEVICE_BINDING: 'ui-request-save-device-binding',\n DEVICE_BINDING_STATUS: 'ui-device-binding-status',\n REQUEST_DEVICE_CONNECT: 'ui-request-device-connect',\n // Ledger BTC App: account index >= 100 requires display=true. Adapter asks\n // the user once per session before promoting the call.\n REQUEST_BTC_HIGH_INDEX_CONFIRM: 'ui-request-btc-high-index-confirm',\n // Required app is not installed; with autoInstallApp on, ask the user\n // whether to install it before retrying the operation.\n REQUEST_INSTALL_APP: 'ui-request-install-app',\n REQUEST_PREEMPTION: 'ui-request-preemption',\n REQUEST_TREZOR_THP_PAIRING: 'ui-request-trezor-thp-pairing',\n CLOSE_UI_WINDOW: 'ui-close',\n DEVICE_PROGRESS: 'ui-device_progress',\n FIRMWARE_PROGRESS: 'ui-firmware-progress',\n FIRMWARE_TIP: 'ui-firmware-tip',\n} as const;\n\nexport const UI_RESPONSE = {\n RECEIVE_PIN: 'receive-pin',\n RECEIVE_PASSPHRASE: 'receive-passphrase',\n RECEIVE_QR_RESPONSE: 'receive-qr-response',\n RECEIVE_SELECT_DEVICE: 'receive-select-device',\n RECEIVE_SAVE_DEVICE_BINDING: 'receive-save-device-binding',\n RECEIVE_DEVICE_CONNECT: 'receive-device-connect',\n RECEIVE_DEVICE_PERMISSION: 'receive-device-permission',\n RECEIVE_BTC_HIGH_INDEX_CONFIRM: 'receive-btc-high-index-confirm',\n RECEIVE_INSTALL_APP: 'receive-install-app',\n RECEIVE_PREEMPTION: 'receive-preemption',\n RECEIVE_TREZOR_THP_PAIRING: 'receive-trezor-thp-pairing',\n CANCEL: 'cancel',\n} as const;\n\n/**\n * The operation a UI request was opened under, so a cancel for one operation spares another's\n * prompts. Absent outside any operation (cold start, teardown).\n */\nexport type UiRequestOperationAttribution = {\n operationId?: string;\n};\n\nexport type DevicePermissionDeniedReason =\n | 'bluetoothTurnedOff'\n | 'permissionDenied'\n | (string & Record<never, never>);\n\nexport type DevicePermissionResponse = {\n granted: boolean;\n reason?: DevicePermissionDeniedReason;\n message?: string;\n};\n\n/**\n * `mismatch`: the selected device is not this operation's device, surfaced as `DeviceMismatch`.\n * `skipped`: the host had nothing to bind, a host contract problem rather than a user action.\n */\nexport type SaveDeviceBindingDeclineReason = 'mismatch' | 'skipped';\n\nexport type UiResponseEvent =\n | {\n type: typeof UI_RESPONSE.RECEIVE_SAVE_DEVICE_BINDING;\n payload: {\n requestId: string;\n saved: boolean;\n reason?: SaveDeviceBindingDeclineReason;\n };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_PIN;\n payload: string;\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_PASSPHRASE;\n payload: {\n value: string;\n passphraseOnDevice?: boolean;\n save?: boolean;\n };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_QR_RESPONSE;\n payload: QrResponseData;\n }\n | {\n // sdkConnectId echoes one of the DeviceInfo.connectId values emitted in\n // the REQUEST_SELECT_DEVICE event's `devices` list. Scope is the current\n // search session; may be ephemeral (e.g. Ledger USB DMK UUID).\n type: typeof UI_RESPONSE.RECEIVE_SELECT_DEVICE;\n payload: { requestId?: string } & (\n | { sdkConnectId: string; cancelled?: false }\n | { cancelled: true }\n );\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_DEVICE_CONNECT;\n payload: { confirmed: boolean };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_DEVICE_PERMISSION;\n payload: DevicePermissionResponse;\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_BTC_HIGH_INDEX_CONFIRM;\n payload: { confirmed: boolean };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_INSTALL_APP;\n payload: { confirmed: boolean };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_PREEMPTION;\n payload: { confirmed: boolean };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_TREZOR_THP_PAIRING;\n payload: {\n selectedMethod?: number | string;\n tag?: string;\n };\n }\n | {\n type: typeof UI_RESPONSE.CANCEL;\n payload?: undefined;\n };\n","import { UI_REQUEST, UI_RESPONSE } from '../events/ui-request';\nimport { createHwkError, HardwareErrorCode } from '../types/errors';\n\n/** 10 min — every UI request is human-in-the-loop; bias toward \"wait long\". */\nexport const UI_REQUEST_DEFAULT_TIMEOUT_MS = 600_000;\n\nconst RESPONSE_TO_REQUEST: Record<string, string> = {\n [UI_RESPONSE.RECEIVE_PIN]: UI_REQUEST.REQUEST_PIN,\n [UI_RESPONSE.RECEIVE_PASSPHRASE]: UI_REQUEST.REQUEST_PASSPHRASE,\n [UI_RESPONSE.RECEIVE_SELECT_DEVICE]: UI_REQUEST.REQUEST_SELECT_DEVICE,\n [UI_RESPONSE.RECEIVE_SAVE_DEVICE_BINDING]: UI_REQUEST.REQUEST_SAVE_DEVICE_BINDING,\n [UI_RESPONSE.RECEIVE_DEVICE_CONNECT]: UI_REQUEST.REQUEST_DEVICE_CONNECT,\n [UI_RESPONSE.RECEIVE_DEVICE_PERMISSION]: UI_REQUEST.REQUEST_DEVICE_PERMISSION,\n [UI_RESPONSE.RECEIVE_BTC_HIGH_INDEX_CONFIRM]: UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM,\n [UI_RESPONSE.RECEIVE_INSTALL_APP]: UI_REQUEST.REQUEST_INSTALL_APP,\n [UI_RESPONSE.RECEIVE_PREEMPTION]: UI_REQUEST.REQUEST_PREEMPTION,\n [UI_RESPONSE.RECEIVE_TREZOR_THP_PAIRING]: UI_REQUEST.REQUEST_TREZOR_THP_PAIRING,\n // RECEIVE_QR_RESPONSE maps to QR_DISPLAY or QR_SCAN — resolved dynamically below.\n};\n\nexport const UI_REQUEST_PREEMPTED_TAG = 'UiRequestPreempted';\nexport const UI_REQUEST_CANCELLED_TAG = 'UiRequestCancelled';\nexport const UI_REQUEST_TIMEOUT_TAG = 'UiRequestTimeout';\n\nlet requestSequence = 0;\n\ntype PendingEntry = {\n requestId?: string;\n /** The operation this waiter belongs to, when it was opened under one. */\n operationId?: string;\n resolve: (payload: unknown) => void;\n reject: (error: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n};\n\n/**\n * Per-type single-slot registry for adapter-level UI requests. A new `wait`\n * of the same type supersedes (rejects) the prior pending entry. With the\n * job queue running globally serially, cross-type collisions don't occur in\n * normal flow — same-type preemption is a defensive measure for callers\n * that fire the same request twice without waiting on the first.\n *\n * Unknown response types are dropped silently so the public `uiResponse`\n * entry never throws.\n */\nexport class UiRequestRegistry {\n private pending = new Map<string, PendingEntry>();\n\n createRequestId(): string {\n requestSequence += 1;\n return `hwk-ui-${Date.now()}-${requestSequence}`;\n }\n\n wait<T = unknown>(\n requestType: string,\n options?: { timeoutMs?: number; requestId?: string; operationId?: string }\n ): Promise<T> {\n const existing = this.pending.get(requestType);\n if (existing) {\n clearTimeout(existing.timer);\n this.pending.delete(requestType);\n existing.reject(\n Object.assign(new Error(`UI request '${requestType}' was superseded by a new request`), {\n _tag: UI_REQUEST_PREEMPTED_TAG,\n })\n );\n }\n\n const timeoutMs = options?.timeoutMs ?? UI_REQUEST_DEFAULT_TIMEOUT_MS;\n\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n if (this.pending.get(requestType)?.timer === timer) {\n this.pending.delete(requestType);\n reject(\n Object.assign(new Error(`UI request '${requestType}' timed out after ${timeoutMs}ms`), {\n _tag: UI_REQUEST_TIMEOUT_TAG,\n })\n );\n }\n }, timeoutMs);\n\n this.pending.set(requestType, {\n requestId: options?.requestId,\n operationId: options?.operationId,\n resolve: resolve as (payload: unknown) => void,\n reject,\n timer,\n });\n });\n }\n\n resolve(responseType: string, payload: unknown): void {\n let requestType = RESPONSE_TO_REQUEST[responseType];\n if (responseType === UI_RESPONSE.RECEIVE_QR_RESPONSE) {\n if (this.pending.has(UI_REQUEST.REQUEST_QR_DISPLAY)) {\n requestType = UI_REQUEST.REQUEST_QR_DISPLAY;\n } else if (this.pending.has(UI_REQUEST.REQUEST_QR_SCAN)) {\n requestType = UI_REQUEST.REQUEST_QR_SCAN;\n }\n }\n if (!requestType) return;\n\n const entry = this.pending.get(requestType);\n if (!entry) return;\n\n const response =\n payload && typeof payload === 'object'\n ? (payload as { requestId?: unknown; cancelled?: unknown })\n : undefined;\n // A late response must not resolve (or cancel) a newer request of the same type.\n if (entry.requestId !== undefined && response?.requestId !== entry.requestId) return;\n clearTimeout(entry.timer);\n this.pending.delete(requestType);\n if (requestType === UI_REQUEST.REQUEST_SELECT_DEVICE && response?.cancelled === true) {\n entry.reject(\n createHwkError({\n code: HardwareErrorCode.UserAborted,\n message: 'Device selection was cancelled',\n _tag: UI_REQUEST_CANCELLED_TAG,\n })\n );\n return;\n }\n entry.resolve(payload);\n }\n\n /**\n * Each argument narrows the cancel: none rejects every waiter (teardown); an `operationId` alone\n * rejects only that operation's prompts and leaves another operation's PIN or QR wait untouched.\n */\n cancel(requestType?: string, requestId?: string, operationId?: string): void {\n const reject = (type: string, entry: PendingEntry) => {\n clearTimeout(entry.timer);\n this.pending.delete(type);\n entry.reject(\n Object.assign(new Error(`UI request '${type}' was cancelled`), {\n _tag: UI_REQUEST_CANCELLED_TAG,\n })\n );\n };\n\n if (requestType) {\n const entry = this.pending.get(requestType);\n if (!entry) return;\n if (requestId !== undefined && entry.requestId !== requestId) return;\n if (operationId !== undefined && entry.operationId !== operationId) return;\n reject(requestType, entry);\n return;\n }\n\n for (const [type, entry] of [...this.pending]) {\n if (operationId === undefined || entry.operationId === operationId) {\n reject(type, entry);\n }\n }\n }\n\n reset(): void {\n this.cancel();\n }\n\n hasPending(requestType?: string): boolean {\n if (requestType) return this.pending.has(requestType);\n return this.pending.size > 0;\n }\n}\n"]}