@metamask/eth-ledger-bridge-keyring 12.0.3 → 12.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/CHANGELOG.md +35 -8
  2. package/dist/dmk/__testhelpers__/mock-error.cjs +24 -0
  3. package/dist/dmk/__testhelpers__/mock-error.cjs.map +1 -0
  4. package/dist/dmk/__testhelpers__/mock-error.d.cts +13 -0
  5. package/dist/dmk/__testhelpers__/mock-error.d.cts.map +1 -0
  6. package/dist/dmk/__testhelpers__/mock-error.d.mts +13 -0
  7. package/dist/dmk/__testhelpers__/mock-error.d.mts.map +1 -0
  8. package/dist/dmk/__testhelpers__/mock-error.mjs +20 -0
  9. package/dist/dmk/__testhelpers__/mock-error.mjs.map +1 -0
  10. package/dist/dmk/dmk-error-translator.cjs +35 -0
  11. package/dist/dmk/dmk-error-translator.cjs.map +1 -0
  12. package/dist/dmk/dmk-error-translator.d.cts +12 -0
  13. package/dist/dmk/dmk-error-translator.d.cts.map +1 -0
  14. package/dist/dmk/dmk-error-translator.d.mts +12 -0
  15. package/dist/dmk/dmk-error-translator.d.mts.map +1 -0
  16. package/dist/dmk/dmk-error-translator.mjs +30 -0
  17. package/dist/dmk/dmk-error-translator.mjs.map +1 -0
  18. package/dist/dmk/eth-get-app-configuration-command.cjs +155 -0
  19. package/dist/dmk/eth-get-app-configuration-command.cjs.map +1 -0
  20. package/dist/dmk/eth-get-app-configuration-command.d.cts +71 -0
  21. package/dist/dmk/eth-get-app-configuration-command.d.cts.map +1 -0
  22. package/dist/dmk/eth-get-app-configuration-command.d.mts +71 -0
  23. package/dist/dmk/eth-get-app-configuration-command.d.mts.map +1 -0
  24. package/dist/dmk/eth-get-app-configuration-command.mjs +151 -0
  25. package/dist/dmk/eth-get-app-configuration-command.mjs.map +1 -0
  26. package/dist/dmk/internal-utils.cjs +81 -0
  27. package/dist/dmk/internal-utils.cjs.map +1 -0
  28. package/dist/dmk/internal-utils.d.cts +44 -0
  29. package/dist/dmk/internal-utils.d.cts.map +1 -0
  30. package/dist/dmk/internal-utils.d.mts +44 -0
  31. package/dist/dmk/internal-utils.d.mts.map +1 -0
  32. package/dist/dmk/internal-utils.mjs +74 -0
  33. package/dist/dmk/internal-utils.mjs.map +1 -0
  34. package/dist/dmk/ledger-dmk-bridge.cjs +379 -0
  35. package/dist/dmk/ledger-dmk-bridge.cjs.map +1 -0
  36. package/dist/dmk/ledger-dmk-bridge.d.cts +157 -0
  37. package/dist/dmk/ledger-dmk-bridge.d.cts.map +1 -0
  38. package/dist/dmk/ledger-dmk-bridge.d.mts +157 -0
  39. package/dist/dmk/ledger-dmk-bridge.d.mts.map +1 -0
  40. package/dist/dmk/ledger-dmk-bridge.mjs +375 -0
  41. package/dist/dmk/ledger-dmk-bridge.mjs.map +1 -0
  42. package/dist/dmk/ledger-dmk-transport-middleware.cjs +170 -0
  43. package/dist/dmk/ledger-dmk-transport-middleware.cjs.map +1 -0
  44. package/dist/dmk/ledger-dmk-transport-middleware.d.cts +99 -0
  45. package/dist/dmk/ledger-dmk-transport-middleware.d.cts.map +1 -0
  46. package/dist/dmk/ledger-dmk-transport-middleware.d.mts +99 -0
  47. package/dist/dmk/ledger-dmk-transport-middleware.d.mts.map +1 -0
  48. package/dist/dmk/ledger-dmk-transport-middleware.mjs +166 -0
  49. package/dist/dmk/ledger-dmk-transport-middleware.mjs.map +1 -0
  50. package/dist/index.cjs +3 -0
  51. package/dist/index.cjs.map +1 -1
  52. package/dist/index.d.cts +3 -0
  53. package/dist/index.d.cts.map +1 -1
  54. package/dist/index.d.mts +3 -0
  55. package/dist/index.d.mts.map +1 -1
  56. package/dist/index.mjs +3 -0
  57. package/dist/index.mjs.map +1 -1
  58. package/dist/ledger-bridge.cjs.map +1 -1
  59. package/dist/ledger-bridge.d.cts +12 -0
  60. package/dist/ledger-bridge.d.cts.map +1 -1
  61. package/dist/ledger-bridge.d.mts +12 -0
  62. package/dist/ledger-bridge.d.mts.map +1 -1
  63. package/dist/ledger-bridge.mjs.map +1 -1
  64. package/dist/ledger-iframe-bridge.cjs +3 -0
  65. package/dist/ledger-iframe-bridge.cjs.map +1 -1
  66. package/dist/ledger-iframe-bridge.d.cts +2 -1
  67. package/dist/ledger-iframe-bridge.d.cts.map +1 -1
  68. package/dist/ledger-iframe-bridge.d.mts +2 -1
  69. package/dist/ledger-iframe-bridge.d.mts.map +1 -1
  70. package/dist/ledger-iframe-bridge.mjs +3 -0
  71. package/dist/ledger-iframe-bridge.mjs.map +1 -1
  72. package/dist/ledger-keyring.cjs +59 -3
  73. package/dist/ledger-keyring.cjs.map +1 -1
  74. package/dist/ledger-keyring.d.cts +1 -0
  75. package/dist/ledger-keyring.d.cts.map +1 -1
  76. package/dist/ledger-keyring.d.mts +1 -0
  77. package/dist/ledger-keyring.d.mts.map +1 -1
  78. package/dist/ledger-keyring.mjs +61 -5
  79. package/dist/ledger-keyring.mjs.map +1 -1
  80. package/dist/ledger-mobile-bridge.cjs +3 -0
  81. package/dist/ledger-mobile-bridge.cjs.map +1 -1
  82. package/dist/ledger-mobile-bridge.d.cts +2 -1
  83. package/dist/ledger-mobile-bridge.d.cts.map +1 -1
  84. package/dist/ledger-mobile-bridge.d.mts +2 -1
  85. package/dist/ledger-mobile-bridge.d.mts.map +1 -1
  86. package/dist/ledger-mobile-bridge.mjs +3 -0
  87. package/dist/ledger-mobile-bridge.mjs.map +1 -1
  88. package/dist/v2/ledger-keyring.cjs +92 -1
  89. package/dist/v2/ledger-keyring.cjs.map +1 -1
  90. package/dist/v2/ledger-keyring.d.cts +65 -1
  91. package/dist/v2/ledger-keyring.d.cts.map +1 -1
  92. package/dist/v2/ledger-keyring.d.mts +65 -1
  93. package/dist/v2/ledger-keyring.d.mts.map +1 -1
  94. package/dist/v2/ledger-keyring.mjs +93 -2
  95. package/dist/v2/ledger-keyring.mjs.map +1 -1
  96. package/package.json +9 -5
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _LedgerDmkTransportMiddleware_instances, _LedgerDmkTransportMiddleware_sdk, _LedgerDmkTransportMiddleware_sessionId, _LedgerDmkTransportMiddleware_managedSessionId, _LedgerDmkTransportMiddleware_cachedSigner, _LedgerDmkTransportMiddleware_assignSession;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.LedgerDmkTransportMiddleware = void 0;
16
+ const device_signer_kit_ethereum_1 = require("@ledgerhq/device-signer-kit-ethereum");
17
+ /**
18
+ * LedgerDmkTransportMiddleware is a middleware to communicate with the
19
+ * Ledger device via DMK.
20
+ * It adapts the new DMK Signer ETH to the existing bridging architectural patterns.
21
+ */
22
+ class LedgerDmkTransportMiddleware {
23
+ constructor(sdk) {
24
+ _LedgerDmkTransportMiddleware_instances.add(this);
25
+ _LedgerDmkTransportMiddleware_sdk.set(this, void 0);
26
+ _LedgerDmkTransportMiddleware_sessionId.set(this, void 0);
27
+ /**
28
+ * Session created by this middleware's `connect()` call. Only managed
29
+ * sessions are disconnected automatically; IDs assigned via `setSessionId`
30
+ * are owned by the host.
31
+ */
32
+ _LedgerDmkTransportMiddleware_managedSessionId.set(this, void 0);
33
+ _LedgerDmkTransportMiddleware_cachedSigner.set(this, null);
34
+ __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_sdk, sdk, "f");
35
+ }
36
+ /**
37
+ * Starts device discovery using the configured DMK transport.
38
+ *
39
+ * @param args - Optional DMK discovery options.
40
+ * @returns An observable that emits discovered devices.
41
+ */
42
+ startDiscovering(...args) {
43
+ return __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sdk, "f").startDiscovering(...args);
44
+ }
45
+ /**
46
+ * Set the session ID used for subsequent DMK commands.
47
+ *
48
+ * Binds the middleware to an existing DMK session without disconnecting
49
+ * any prior session. If the current session was created by this middleware's
50
+ * `connect()`, that managed session is released when replaced.
51
+ *
52
+ * Invalidates any cached signer so the next `getEthSigner()` call builds a
53
+ * fresh signer bound to the new session.
54
+ *
55
+ * @param sessionId - The session ID for the connected Ledger device.
56
+ */
57
+ async setSessionId(sessionId) {
58
+ if (__classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sessionId, "f") === sessionId) {
59
+ return;
60
+ }
61
+ const managedToRelease = __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_managedSessionId, "f");
62
+ __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_instances, "m", _LedgerDmkTransportMiddleware_assignSession).call(this, sessionId);
63
+ if (managedToRelease && managedToRelease !== sessionId) {
64
+ await __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sdk, "f").disconnect({ sessionId: managedToRelease });
65
+ __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_managedSessionId, undefined, "f");
66
+ }
67
+ }
68
+ /**
69
+ * Method to retrieve the session ID.
70
+ *
71
+ * @returns The session ID.
72
+ * @throws {Error} If `setSessionId` or `connect` has not been called yet.
73
+ */
74
+ getSessionId() {
75
+ if (!__classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sessionId, "f")) {
76
+ throw new Error('Session ID not set. Call connect() or setSessionId() first.');
77
+ }
78
+ return __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sessionId, "f");
79
+ }
80
+ /**
81
+ * Connects to a discovered device and stores the resulting session ID.
82
+ *
83
+ * @param args - The DMK connection arguments.
84
+ * @returns The created session ID.
85
+ */
86
+ async connect(...args) {
87
+ const previousManagedSessionId = __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_managedSessionId, "f");
88
+ const sessionId = await __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sdk, "f").connect(...args);
89
+ __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_instances, "m", _LedgerDmkTransportMiddleware_assignSession).call(this, sessionId);
90
+ __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_managedSessionId, sessionId, "f");
91
+ if (previousManagedSessionId && previousManagedSessionId !== sessionId) {
92
+ await __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sdk, "f").disconnect({ sessionId: previousManagedSessionId });
93
+ }
94
+ return sessionId;
95
+ }
96
+ /**
97
+ * Build (or return the cached) Ethereum signer for the current session.
98
+ *
99
+ * The signer is memoized per session ID. `getEthSigner()` is called from
100
+ * five different operations on the bridge (`getPublicKey`,
101
+ * `deviceSignTransaction`, `deviceSignMessage`, `deviceSignTypedData`,
102
+ * `deviceSignDelegationAuthorization`); a single bridge session therefore
103
+ * needs at most one signer instance.
104
+ *
105
+ * Rebuilding is not free: `SignerEthBuilder.build()` allocates a fresh
106
+ * `DefaultContextModule` from `@ledgerhq/context-module`, which in its
107
+ * constructor builds a DI container and instantiates the default loaders
108
+ * (calldata, dynamic-network, external-plugin, gated-signing, nft, proxy,
109
+ * safe, token, trusted-name, typed-data, reporter, transaction-check, plus
110
+ * the field loaders, typed-data loader and blind-signing reporter). See
111
+ * `node_modules/@ledgerhq/context-module/lib/esm/src/DefaultContextModule.js`.
112
+ *
113
+ * Neither `DefaultSignerEth` nor `DefaultContextModule` expose a
114
+ * `dispose()` / `disconnect()` / `destroy()` method (verified against
115
+ * `node_modules/@ledgerhq/device-signer-kit-ethereum/lib/esm/internal/DefaultSignerEth.js`
116
+ * and the `DefaultContextModule` source above). If any of those loaders
117
+ * hold subscriptions to device-session state, re-creating the signer on
118
+ * every call would leak them. Caching is therefore defensive against
119
+ * subscription leaks, not just a micro-optimization.
120
+ *
121
+ * The cache is invalidated by `setSessionId()` when the session changes
122
+ * and by `dispose()` on shutdown.
123
+ *
124
+ * @returns An Ethereum signer instance bound to the current session.
125
+ */
126
+ getEthSigner() {
127
+ const sessionId = this.getSessionId();
128
+ if (__classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_cachedSigner, "f")?.sessionId === sessionId) {
129
+ return __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_cachedSigner, "f").signer;
130
+ }
131
+ const signer = new device_signer_kit_ethereum_1.SignerEthBuilder({
132
+ dmk: __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sdk, "f"),
133
+ sessionId,
134
+ }).build();
135
+ __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_cachedSigner, { sessionId, signer }, "f");
136
+ return signer;
137
+ }
138
+ /**
139
+ * Clears the stored session ID and signer cache without disconnecting from
140
+ * DMK.
141
+ *
142
+ * Use when the bridge does not own the DMK instance and the host manages
143
+ * session lifecycle externally.
144
+ */
145
+ clearSession() {
146
+ __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_sessionId, undefined, "f");
147
+ __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_managedSessionId, undefined, "f");
148
+ __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_cachedSigner, null, "f");
149
+ }
150
+ /**
151
+ * Disconnect the current session and clear cached state.
152
+ *
153
+ * Silently no-ops when no session has been established.
154
+ *
155
+ * @returns A promise that resolves when the session is closed.
156
+ */
157
+ async dispose() {
158
+ const sessionToDisconnect = __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_managedSessionId, "f") ?? __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sessionId, "f");
159
+ if (sessionToDisconnect) {
160
+ await __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sdk, "f").disconnect({ sessionId: sessionToDisconnect });
161
+ this.clearSession();
162
+ }
163
+ }
164
+ }
165
+ exports.LedgerDmkTransportMiddleware = LedgerDmkTransportMiddleware;
166
+ _LedgerDmkTransportMiddleware_sdk = new WeakMap(), _LedgerDmkTransportMiddleware_sessionId = new WeakMap(), _LedgerDmkTransportMiddleware_managedSessionId = new WeakMap(), _LedgerDmkTransportMiddleware_cachedSigner = new WeakMap(), _LedgerDmkTransportMiddleware_instances = new WeakSet(), _LedgerDmkTransportMiddleware_assignSession = function _LedgerDmkTransportMiddleware_assignSession(sessionId) {
167
+ __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_cachedSigner, null, "f");
168
+ __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_sessionId, sessionId, "f");
169
+ };
170
+ //# sourceMappingURL=ledger-dmk-transport-middleware.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger-dmk-transport-middleware.cjs","sourceRoot":"","sources":["../../src/dmk/ledger-dmk-transport-middleware.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DeviceManagementKit } from '@ledgerhq/device-management-kit';\nimport { SignerEthBuilder } from '@ledgerhq/device-signer-kit-ethereum';\n\ntype StartDiscoveringParameters = Parameters<\n DeviceManagementKit['startDiscovering']\n>;\n\ntype StartDiscoveringResult = ReturnType<\n DeviceManagementKit['startDiscovering']\n>;\n\ntype ConnectParameters = Parameters<DeviceManagementKit['connect']>;\n\ntype ConnectResult = ReturnType<DeviceManagementKit['connect']>;\n\ntype EthSigner = ReturnType<SignerEthBuilder['build']>;\n\ntype CachedSigner = { sessionId: string; signer: EthSigner };\n\n/**\n * LedgerDmkTransportMiddleware is a middleware to communicate with the\n * Ledger device via DMK.\n * It adapts the new DMK Signer ETH to the existing bridging architectural patterns.\n */\nexport class LedgerDmkTransportMiddleware {\n readonly #sdk: DeviceManagementKit;\n\n #sessionId?: string;\n\n /**\n * Session created by this middleware's `connect()` call. Only managed\n * sessions are disconnected automatically; IDs assigned via `setSessionId`\n * are owned by the host.\n */\n #managedSessionId?: string;\n\n #cachedSigner: CachedSigner | null = null;\n\n constructor(sdk: DeviceManagementKit) {\n this.#sdk = sdk;\n }\n\n /**\n * Starts device discovery using the configured DMK transport.\n *\n * @param args - Optional DMK discovery options.\n * @returns An observable that emits discovered devices.\n */\n startDiscovering(\n ...args: StartDiscoveringParameters\n ): StartDiscoveringResult {\n return this.#sdk.startDiscovering(...args);\n }\n\n /**\n * Set the session ID used for subsequent DMK commands.\n *\n * Binds the middleware to an existing DMK session without disconnecting\n * any prior session. If the current session was created by this middleware's\n * `connect()`, that managed session is released when replaced.\n *\n * Invalidates any cached signer so the next `getEthSigner()` call builds a\n * fresh signer bound to the new session.\n *\n * @param sessionId - The session ID for the connected Ledger device.\n */\n async setSessionId(sessionId: string): Promise<void> {\n if (this.#sessionId === sessionId) {\n return;\n }\n\n const managedToRelease = this.#managedSessionId;\n this.#assignSession(sessionId);\n\n if (managedToRelease && managedToRelease !== sessionId) {\n await this.#sdk.disconnect({ sessionId: managedToRelease });\n this.#managedSessionId = undefined;\n }\n }\n\n /**\n * Method to retrieve the session ID.\n *\n * @returns The session ID.\n * @throws {Error} If `setSessionId` or `connect` has not been called yet.\n */\n getSessionId(): string {\n if (!this.#sessionId) {\n throw new Error(\n 'Session ID not set. Call connect() or setSessionId() first.',\n );\n }\n return this.#sessionId;\n }\n\n /**\n * Connects to a discovered device and stores the resulting session ID.\n *\n * @param args - The DMK connection arguments.\n * @returns The created session ID.\n */\n async connect(...args: ConnectParameters): ConnectResult {\n const previousManagedSessionId = this.#managedSessionId;\n const sessionId = await this.#sdk.connect(...args);\n this.#assignSession(sessionId);\n this.#managedSessionId = sessionId;\n\n if (previousManagedSessionId && previousManagedSessionId !== sessionId) {\n await this.#sdk.disconnect({ sessionId: previousManagedSessionId });\n }\n\n return sessionId;\n }\n\n /**\n * Build (or return the cached) Ethereum signer for the current session.\n *\n * The signer is memoized per session ID. `getEthSigner()` is called from\n * five different operations on the bridge (`getPublicKey`,\n * `deviceSignTransaction`, `deviceSignMessage`, `deviceSignTypedData`,\n * `deviceSignDelegationAuthorization`); a single bridge session therefore\n * needs at most one signer instance.\n *\n * Rebuilding is not free: `SignerEthBuilder.build()` allocates a fresh\n * `DefaultContextModule` from `@ledgerhq/context-module`, which in its\n * constructor builds a DI container and instantiates the default loaders\n * (calldata, dynamic-network, external-plugin, gated-signing, nft, proxy,\n * safe, token, trusted-name, typed-data, reporter, transaction-check, plus\n * the field loaders, typed-data loader and blind-signing reporter). See\n * `node_modules/@ledgerhq/context-module/lib/esm/src/DefaultContextModule.js`.\n *\n * Neither `DefaultSignerEth` nor `DefaultContextModule` expose a\n * `dispose()` / `disconnect()` / `destroy()` method (verified against\n * `node_modules/@ledgerhq/device-signer-kit-ethereum/lib/esm/internal/DefaultSignerEth.js`\n * and the `DefaultContextModule` source above). If any of those loaders\n * hold subscriptions to device-session state, re-creating the signer on\n * every call would leak them. Caching is therefore defensive against\n * subscription leaks, not just a micro-optimization.\n *\n * The cache is invalidated by `setSessionId()` when the session changes\n * and by `dispose()` on shutdown.\n *\n * @returns An Ethereum signer instance bound to the current session.\n */\n getEthSigner(): EthSigner {\n const sessionId = this.getSessionId();\n if (this.#cachedSigner?.sessionId === sessionId) {\n return this.#cachedSigner.signer;\n }\n const signer = new SignerEthBuilder({\n dmk: this.#sdk,\n sessionId,\n }).build();\n this.#cachedSigner = { sessionId, signer };\n return signer;\n }\n\n /**\n * Clears the stored session ID and signer cache without disconnecting from\n * DMK.\n *\n * Use when the bridge does not own the DMK instance and the host manages\n * session lifecycle externally.\n */\n clearSession(): void {\n this.#sessionId = undefined;\n this.#managedSessionId = undefined;\n this.#cachedSigner = null;\n }\n\n /**\n * Disconnect the current session and clear cached state.\n *\n * Silently no-ops when no session has been established.\n *\n * @returns A promise that resolves when the session is closed.\n */\n async dispose(): Promise<void> {\n const sessionToDisconnect = this.#managedSessionId ?? this.#sessionId;\n if (sessionToDisconnect) {\n await this.#sdk.disconnect({ sessionId: sessionToDisconnect });\n this.clearSession();\n }\n }\n\n #assignSession(sessionId: string): void {\n this.#cachedSigner = null;\n this.#sessionId = sessionId;\n }\n}\n"]}
@@ -0,0 +1,99 @@
1
+ import type { DeviceManagementKit } from "@ledgerhq/device-management-kit";
2
+ import { SignerEthBuilder } from "@ledgerhq/device-signer-kit-ethereum";
3
+ type StartDiscoveringParameters = Parameters<DeviceManagementKit['startDiscovering']>;
4
+ type StartDiscoveringResult = ReturnType<DeviceManagementKit['startDiscovering']>;
5
+ type ConnectParameters = Parameters<DeviceManagementKit['connect']>;
6
+ type ConnectResult = ReturnType<DeviceManagementKit['connect']>;
7
+ type EthSigner = ReturnType<SignerEthBuilder['build']>;
8
+ /**
9
+ * LedgerDmkTransportMiddleware is a middleware to communicate with the
10
+ * Ledger device via DMK.
11
+ * It adapts the new DMK Signer ETH to the existing bridging architectural patterns.
12
+ */
13
+ export declare class LedgerDmkTransportMiddleware {
14
+ #private;
15
+ constructor(sdk: DeviceManagementKit);
16
+ /**
17
+ * Starts device discovery using the configured DMK transport.
18
+ *
19
+ * @param args - Optional DMK discovery options.
20
+ * @returns An observable that emits discovered devices.
21
+ */
22
+ startDiscovering(...args: StartDiscoveringParameters): StartDiscoveringResult;
23
+ /**
24
+ * Set the session ID used for subsequent DMK commands.
25
+ *
26
+ * Binds the middleware to an existing DMK session without disconnecting
27
+ * any prior session. If the current session was created by this middleware's
28
+ * `connect()`, that managed session is released when replaced.
29
+ *
30
+ * Invalidates any cached signer so the next `getEthSigner()` call builds a
31
+ * fresh signer bound to the new session.
32
+ *
33
+ * @param sessionId - The session ID for the connected Ledger device.
34
+ */
35
+ setSessionId(sessionId: string): Promise<void>;
36
+ /**
37
+ * Method to retrieve the session ID.
38
+ *
39
+ * @returns The session ID.
40
+ * @throws {Error} If `setSessionId` or `connect` has not been called yet.
41
+ */
42
+ getSessionId(): string;
43
+ /**
44
+ * Connects to a discovered device and stores the resulting session ID.
45
+ *
46
+ * @param args - The DMK connection arguments.
47
+ * @returns The created session ID.
48
+ */
49
+ connect(...args: ConnectParameters): ConnectResult;
50
+ /**
51
+ * Build (or return the cached) Ethereum signer for the current session.
52
+ *
53
+ * The signer is memoized per session ID. `getEthSigner()` is called from
54
+ * five different operations on the bridge (`getPublicKey`,
55
+ * `deviceSignTransaction`, `deviceSignMessage`, `deviceSignTypedData`,
56
+ * `deviceSignDelegationAuthorization`); a single bridge session therefore
57
+ * needs at most one signer instance.
58
+ *
59
+ * Rebuilding is not free: `SignerEthBuilder.build()` allocates a fresh
60
+ * `DefaultContextModule` from `@ledgerhq/context-module`, which in its
61
+ * constructor builds a DI container and instantiates the default loaders
62
+ * (calldata, dynamic-network, external-plugin, gated-signing, nft, proxy,
63
+ * safe, token, trusted-name, typed-data, reporter, transaction-check, plus
64
+ * the field loaders, typed-data loader and blind-signing reporter). See
65
+ * `node_modules/@ledgerhq/context-module/lib/esm/src/DefaultContextModule.js`.
66
+ *
67
+ * Neither `DefaultSignerEth` nor `DefaultContextModule` expose a
68
+ * `dispose()` / `disconnect()` / `destroy()` method (verified against
69
+ * `node_modules/@ledgerhq/device-signer-kit-ethereum/lib/esm/internal/DefaultSignerEth.js`
70
+ * and the `DefaultContextModule` source above). If any of those loaders
71
+ * hold subscriptions to device-session state, re-creating the signer on
72
+ * every call would leak them. Caching is therefore defensive against
73
+ * subscription leaks, not just a micro-optimization.
74
+ *
75
+ * The cache is invalidated by `setSessionId()` when the session changes
76
+ * and by `dispose()` on shutdown.
77
+ *
78
+ * @returns An Ethereum signer instance bound to the current session.
79
+ */
80
+ getEthSigner(): EthSigner;
81
+ /**
82
+ * Clears the stored session ID and signer cache without disconnecting from
83
+ * DMK.
84
+ *
85
+ * Use when the bridge does not own the DMK instance and the host manages
86
+ * session lifecycle externally.
87
+ */
88
+ clearSession(): void;
89
+ /**
90
+ * Disconnect the current session and clear cached state.
91
+ *
92
+ * Silently no-ops when no session has been established.
93
+ *
94
+ * @returns A promise that resolves when the session is closed.
95
+ */
96
+ dispose(): Promise<void>;
97
+ }
98
+ export {};
99
+ //# sourceMappingURL=ledger-dmk-transport-middleware.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger-dmk-transport-middleware.d.cts","sourceRoot":"","sources":["../../src/dmk/ledger-dmk-transport-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,wCAAwC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,6CAA6C;AAExE,KAAK,0BAA0B,GAAG,UAAU,CAC1C,mBAAmB,CAAC,kBAAkB,CAAC,CACxC,CAAC;AAEF,KAAK,sBAAsB,GAAG,UAAU,CACtC,mBAAmB,CAAC,kBAAkB,CAAC,CACxC,CAAC;AAEF,KAAK,iBAAiB,GAAG,UAAU,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,CAAC;AAEpE,KAAK,aAAa,GAAG,UAAU,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,CAAC;AAEhE,KAAK,SAAS,GAAG,UAAU,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;AAIvD;;;;GAIG;AACH,qBAAa,4BAA4B;;gBAc3B,GAAG,EAAE,mBAAmB;IAIpC;;;;;OAKG;IACH,gBAAgB,CACd,GAAG,IAAI,EAAE,0BAA0B,GAClC,sBAAsB;IAIzB;;;;;;;;;;;OAWG;IACG,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAcpD;;;;;OAKG;IACH,YAAY,IAAI,MAAM;IAStB;;;;;OAKG;IACG,OAAO,CAAC,GAAG,IAAI,EAAE,iBAAiB,GAAG,aAAa;IAaxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,YAAY,IAAI,SAAS;IAazB;;;;;;OAMG;IACH,YAAY,IAAI,IAAI;IAMpB;;;;;;OAMG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAY/B"}
@@ -0,0 +1,99 @@
1
+ import type { DeviceManagementKit } from "@ledgerhq/device-management-kit";
2
+ import { SignerEthBuilder } from "@ledgerhq/device-signer-kit-ethereum";
3
+ type StartDiscoveringParameters = Parameters<DeviceManagementKit['startDiscovering']>;
4
+ type StartDiscoveringResult = ReturnType<DeviceManagementKit['startDiscovering']>;
5
+ type ConnectParameters = Parameters<DeviceManagementKit['connect']>;
6
+ type ConnectResult = ReturnType<DeviceManagementKit['connect']>;
7
+ type EthSigner = ReturnType<SignerEthBuilder['build']>;
8
+ /**
9
+ * LedgerDmkTransportMiddleware is a middleware to communicate with the
10
+ * Ledger device via DMK.
11
+ * It adapts the new DMK Signer ETH to the existing bridging architectural patterns.
12
+ */
13
+ export declare class LedgerDmkTransportMiddleware {
14
+ #private;
15
+ constructor(sdk: DeviceManagementKit);
16
+ /**
17
+ * Starts device discovery using the configured DMK transport.
18
+ *
19
+ * @param args - Optional DMK discovery options.
20
+ * @returns An observable that emits discovered devices.
21
+ */
22
+ startDiscovering(...args: StartDiscoveringParameters): StartDiscoveringResult;
23
+ /**
24
+ * Set the session ID used for subsequent DMK commands.
25
+ *
26
+ * Binds the middleware to an existing DMK session without disconnecting
27
+ * any prior session. If the current session was created by this middleware's
28
+ * `connect()`, that managed session is released when replaced.
29
+ *
30
+ * Invalidates any cached signer so the next `getEthSigner()` call builds a
31
+ * fresh signer bound to the new session.
32
+ *
33
+ * @param sessionId - The session ID for the connected Ledger device.
34
+ */
35
+ setSessionId(sessionId: string): Promise<void>;
36
+ /**
37
+ * Method to retrieve the session ID.
38
+ *
39
+ * @returns The session ID.
40
+ * @throws {Error} If `setSessionId` or `connect` has not been called yet.
41
+ */
42
+ getSessionId(): string;
43
+ /**
44
+ * Connects to a discovered device and stores the resulting session ID.
45
+ *
46
+ * @param args - The DMK connection arguments.
47
+ * @returns The created session ID.
48
+ */
49
+ connect(...args: ConnectParameters): ConnectResult;
50
+ /**
51
+ * Build (or return the cached) Ethereum signer for the current session.
52
+ *
53
+ * The signer is memoized per session ID. `getEthSigner()` is called from
54
+ * five different operations on the bridge (`getPublicKey`,
55
+ * `deviceSignTransaction`, `deviceSignMessage`, `deviceSignTypedData`,
56
+ * `deviceSignDelegationAuthorization`); a single bridge session therefore
57
+ * needs at most one signer instance.
58
+ *
59
+ * Rebuilding is not free: `SignerEthBuilder.build()` allocates a fresh
60
+ * `DefaultContextModule` from `@ledgerhq/context-module`, which in its
61
+ * constructor builds a DI container and instantiates the default loaders
62
+ * (calldata, dynamic-network, external-plugin, gated-signing, nft, proxy,
63
+ * safe, token, trusted-name, typed-data, reporter, transaction-check, plus
64
+ * the field loaders, typed-data loader and blind-signing reporter). See
65
+ * `node_modules/@ledgerhq/context-module/lib/esm/src/DefaultContextModule.js`.
66
+ *
67
+ * Neither `DefaultSignerEth` nor `DefaultContextModule` expose a
68
+ * `dispose()` / `disconnect()` / `destroy()` method (verified against
69
+ * `node_modules/@ledgerhq/device-signer-kit-ethereum/lib/esm/internal/DefaultSignerEth.js`
70
+ * and the `DefaultContextModule` source above). If any of those loaders
71
+ * hold subscriptions to device-session state, re-creating the signer on
72
+ * every call would leak them. Caching is therefore defensive against
73
+ * subscription leaks, not just a micro-optimization.
74
+ *
75
+ * The cache is invalidated by `setSessionId()` when the session changes
76
+ * and by `dispose()` on shutdown.
77
+ *
78
+ * @returns An Ethereum signer instance bound to the current session.
79
+ */
80
+ getEthSigner(): EthSigner;
81
+ /**
82
+ * Clears the stored session ID and signer cache without disconnecting from
83
+ * DMK.
84
+ *
85
+ * Use when the bridge does not own the DMK instance and the host manages
86
+ * session lifecycle externally.
87
+ */
88
+ clearSession(): void;
89
+ /**
90
+ * Disconnect the current session and clear cached state.
91
+ *
92
+ * Silently no-ops when no session has been established.
93
+ *
94
+ * @returns A promise that resolves when the session is closed.
95
+ */
96
+ dispose(): Promise<void>;
97
+ }
98
+ export {};
99
+ //# sourceMappingURL=ledger-dmk-transport-middleware.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger-dmk-transport-middleware.d.mts","sourceRoot":"","sources":["../../src/dmk/ledger-dmk-transport-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,wCAAwC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,6CAA6C;AAExE,KAAK,0BAA0B,GAAG,UAAU,CAC1C,mBAAmB,CAAC,kBAAkB,CAAC,CACxC,CAAC;AAEF,KAAK,sBAAsB,GAAG,UAAU,CACtC,mBAAmB,CAAC,kBAAkB,CAAC,CACxC,CAAC;AAEF,KAAK,iBAAiB,GAAG,UAAU,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,CAAC;AAEpE,KAAK,aAAa,GAAG,UAAU,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,CAAC;AAEhE,KAAK,SAAS,GAAG,UAAU,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;AAIvD;;;;GAIG;AACH,qBAAa,4BAA4B;;gBAc3B,GAAG,EAAE,mBAAmB;IAIpC;;;;;OAKG;IACH,gBAAgB,CACd,GAAG,IAAI,EAAE,0BAA0B,GAClC,sBAAsB;IAIzB;;;;;;;;;;;OAWG;IACG,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAcpD;;;;;OAKG;IACH,YAAY,IAAI,MAAM;IAStB;;;;;OAKG;IACG,OAAO,CAAC,GAAG,IAAI,EAAE,iBAAiB,GAAG,aAAa;IAaxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,YAAY,IAAI,SAAS;IAazB;;;;;;OAMG;IACH,YAAY,IAAI,IAAI;IAMpB;;;;;;OAMG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAY/B"}
@@ -0,0 +1,166 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _LedgerDmkTransportMiddleware_instances, _LedgerDmkTransportMiddleware_sdk, _LedgerDmkTransportMiddleware_sessionId, _LedgerDmkTransportMiddleware_managedSessionId, _LedgerDmkTransportMiddleware_cachedSigner, _LedgerDmkTransportMiddleware_assignSession;
13
+ import { SignerEthBuilder } from "@ledgerhq/device-signer-kit-ethereum";
14
+ /**
15
+ * LedgerDmkTransportMiddleware is a middleware to communicate with the
16
+ * Ledger device via DMK.
17
+ * It adapts the new DMK Signer ETH to the existing bridging architectural patterns.
18
+ */
19
+ export class LedgerDmkTransportMiddleware {
20
+ constructor(sdk) {
21
+ _LedgerDmkTransportMiddleware_instances.add(this);
22
+ _LedgerDmkTransportMiddleware_sdk.set(this, void 0);
23
+ _LedgerDmkTransportMiddleware_sessionId.set(this, void 0);
24
+ /**
25
+ * Session created by this middleware's `connect()` call. Only managed
26
+ * sessions are disconnected automatically; IDs assigned via `setSessionId`
27
+ * are owned by the host.
28
+ */
29
+ _LedgerDmkTransportMiddleware_managedSessionId.set(this, void 0);
30
+ _LedgerDmkTransportMiddleware_cachedSigner.set(this, null);
31
+ __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_sdk, sdk, "f");
32
+ }
33
+ /**
34
+ * Starts device discovery using the configured DMK transport.
35
+ *
36
+ * @param args - Optional DMK discovery options.
37
+ * @returns An observable that emits discovered devices.
38
+ */
39
+ startDiscovering(...args) {
40
+ return __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sdk, "f").startDiscovering(...args);
41
+ }
42
+ /**
43
+ * Set the session ID used for subsequent DMK commands.
44
+ *
45
+ * Binds the middleware to an existing DMK session without disconnecting
46
+ * any prior session. If the current session was created by this middleware's
47
+ * `connect()`, that managed session is released when replaced.
48
+ *
49
+ * Invalidates any cached signer so the next `getEthSigner()` call builds a
50
+ * fresh signer bound to the new session.
51
+ *
52
+ * @param sessionId - The session ID for the connected Ledger device.
53
+ */
54
+ async setSessionId(sessionId) {
55
+ if (__classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sessionId, "f") === sessionId) {
56
+ return;
57
+ }
58
+ const managedToRelease = __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_managedSessionId, "f");
59
+ __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_instances, "m", _LedgerDmkTransportMiddleware_assignSession).call(this, sessionId);
60
+ if (managedToRelease && managedToRelease !== sessionId) {
61
+ await __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sdk, "f").disconnect({ sessionId: managedToRelease });
62
+ __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_managedSessionId, undefined, "f");
63
+ }
64
+ }
65
+ /**
66
+ * Method to retrieve the session ID.
67
+ *
68
+ * @returns The session ID.
69
+ * @throws {Error} If `setSessionId` or `connect` has not been called yet.
70
+ */
71
+ getSessionId() {
72
+ if (!__classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sessionId, "f")) {
73
+ throw new Error('Session ID not set. Call connect() or setSessionId() first.');
74
+ }
75
+ return __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sessionId, "f");
76
+ }
77
+ /**
78
+ * Connects to a discovered device and stores the resulting session ID.
79
+ *
80
+ * @param args - The DMK connection arguments.
81
+ * @returns The created session ID.
82
+ */
83
+ async connect(...args) {
84
+ const previousManagedSessionId = __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_managedSessionId, "f");
85
+ const sessionId = await __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sdk, "f").connect(...args);
86
+ __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_instances, "m", _LedgerDmkTransportMiddleware_assignSession).call(this, sessionId);
87
+ __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_managedSessionId, sessionId, "f");
88
+ if (previousManagedSessionId && previousManagedSessionId !== sessionId) {
89
+ await __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sdk, "f").disconnect({ sessionId: previousManagedSessionId });
90
+ }
91
+ return sessionId;
92
+ }
93
+ /**
94
+ * Build (or return the cached) Ethereum signer for the current session.
95
+ *
96
+ * The signer is memoized per session ID. `getEthSigner()` is called from
97
+ * five different operations on the bridge (`getPublicKey`,
98
+ * `deviceSignTransaction`, `deviceSignMessage`, `deviceSignTypedData`,
99
+ * `deviceSignDelegationAuthorization`); a single bridge session therefore
100
+ * needs at most one signer instance.
101
+ *
102
+ * Rebuilding is not free: `SignerEthBuilder.build()` allocates a fresh
103
+ * `DefaultContextModule` from `@ledgerhq/context-module`, which in its
104
+ * constructor builds a DI container and instantiates the default loaders
105
+ * (calldata, dynamic-network, external-plugin, gated-signing, nft, proxy,
106
+ * safe, token, trusted-name, typed-data, reporter, transaction-check, plus
107
+ * the field loaders, typed-data loader and blind-signing reporter). See
108
+ * `node_modules/@ledgerhq/context-module/lib/esm/src/DefaultContextModule.js`.
109
+ *
110
+ * Neither `DefaultSignerEth` nor `DefaultContextModule` expose a
111
+ * `dispose()` / `disconnect()` / `destroy()` method (verified against
112
+ * `node_modules/@ledgerhq/device-signer-kit-ethereum/lib/esm/internal/DefaultSignerEth.js`
113
+ * and the `DefaultContextModule` source above). If any of those loaders
114
+ * hold subscriptions to device-session state, re-creating the signer on
115
+ * every call would leak them. Caching is therefore defensive against
116
+ * subscription leaks, not just a micro-optimization.
117
+ *
118
+ * The cache is invalidated by `setSessionId()` when the session changes
119
+ * and by `dispose()` on shutdown.
120
+ *
121
+ * @returns An Ethereum signer instance bound to the current session.
122
+ */
123
+ getEthSigner() {
124
+ const sessionId = this.getSessionId();
125
+ if (__classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_cachedSigner, "f")?.sessionId === sessionId) {
126
+ return __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_cachedSigner, "f").signer;
127
+ }
128
+ const signer = new SignerEthBuilder({
129
+ dmk: __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sdk, "f"),
130
+ sessionId,
131
+ }).build();
132
+ __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_cachedSigner, { sessionId, signer }, "f");
133
+ return signer;
134
+ }
135
+ /**
136
+ * Clears the stored session ID and signer cache without disconnecting from
137
+ * DMK.
138
+ *
139
+ * Use when the bridge does not own the DMK instance and the host manages
140
+ * session lifecycle externally.
141
+ */
142
+ clearSession() {
143
+ __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_sessionId, undefined, "f");
144
+ __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_managedSessionId, undefined, "f");
145
+ __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_cachedSigner, null, "f");
146
+ }
147
+ /**
148
+ * Disconnect the current session and clear cached state.
149
+ *
150
+ * Silently no-ops when no session has been established.
151
+ *
152
+ * @returns A promise that resolves when the session is closed.
153
+ */
154
+ async dispose() {
155
+ const sessionToDisconnect = __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_managedSessionId, "f") ?? __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sessionId, "f");
156
+ if (sessionToDisconnect) {
157
+ await __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sdk, "f").disconnect({ sessionId: sessionToDisconnect });
158
+ this.clearSession();
159
+ }
160
+ }
161
+ }
162
+ _LedgerDmkTransportMiddleware_sdk = new WeakMap(), _LedgerDmkTransportMiddleware_sessionId = new WeakMap(), _LedgerDmkTransportMiddleware_managedSessionId = new WeakMap(), _LedgerDmkTransportMiddleware_cachedSigner = new WeakMap(), _LedgerDmkTransportMiddleware_instances = new WeakSet(), _LedgerDmkTransportMiddleware_assignSession = function _LedgerDmkTransportMiddleware_assignSession(sessionId) {
163
+ __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_cachedSigner, null, "f");
164
+ __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_sessionId, sessionId, "f");
165
+ };
166
+ //# sourceMappingURL=ledger-dmk-transport-middleware.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger-dmk-transport-middleware.mjs","sourceRoot":"","sources":["../../src/dmk/ledger-dmk-transport-middleware.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DeviceManagementKit } from '@ledgerhq/device-management-kit';\nimport { SignerEthBuilder } from '@ledgerhq/device-signer-kit-ethereum';\n\ntype StartDiscoveringParameters = Parameters<\n DeviceManagementKit['startDiscovering']\n>;\n\ntype StartDiscoveringResult = ReturnType<\n DeviceManagementKit['startDiscovering']\n>;\n\ntype ConnectParameters = Parameters<DeviceManagementKit['connect']>;\n\ntype ConnectResult = ReturnType<DeviceManagementKit['connect']>;\n\ntype EthSigner = ReturnType<SignerEthBuilder['build']>;\n\ntype CachedSigner = { sessionId: string; signer: EthSigner };\n\n/**\n * LedgerDmkTransportMiddleware is a middleware to communicate with the\n * Ledger device via DMK.\n * It adapts the new DMK Signer ETH to the existing bridging architectural patterns.\n */\nexport class LedgerDmkTransportMiddleware {\n readonly #sdk: DeviceManagementKit;\n\n #sessionId?: string;\n\n /**\n * Session created by this middleware's `connect()` call. Only managed\n * sessions are disconnected automatically; IDs assigned via `setSessionId`\n * are owned by the host.\n */\n #managedSessionId?: string;\n\n #cachedSigner: CachedSigner | null = null;\n\n constructor(sdk: DeviceManagementKit) {\n this.#sdk = sdk;\n }\n\n /**\n * Starts device discovery using the configured DMK transport.\n *\n * @param args - Optional DMK discovery options.\n * @returns An observable that emits discovered devices.\n */\n startDiscovering(\n ...args: StartDiscoveringParameters\n ): StartDiscoveringResult {\n return this.#sdk.startDiscovering(...args);\n }\n\n /**\n * Set the session ID used for subsequent DMK commands.\n *\n * Binds the middleware to an existing DMK session without disconnecting\n * any prior session. If the current session was created by this middleware's\n * `connect()`, that managed session is released when replaced.\n *\n * Invalidates any cached signer so the next `getEthSigner()` call builds a\n * fresh signer bound to the new session.\n *\n * @param sessionId - The session ID for the connected Ledger device.\n */\n async setSessionId(sessionId: string): Promise<void> {\n if (this.#sessionId === sessionId) {\n return;\n }\n\n const managedToRelease = this.#managedSessionId;\n this.#assignSession(sessionId);\n\n if (managedToRelease && managedToRelease !== sessionId) {\n await this.#sdk.disconnect({ sessionId: managedToRelease });\n this.#managedSessionId = undefined;\n }\n }\n\n /**\n * Method to retrieve the session ID.\n *\n * @returns The session ID.\n * @throws {Error} If `setSessionId` or `connect` has not been called yet.\n */\n getSessionId(): string {\n if (!this.#sessionId) {\n throw new Error(\n 'Session ID not set. Call connect() or setSessionId() first.',\n );\n }\n return this.#sessionId;\n }\n\n /**\n * Connects to a discovered device and stores the resulting session ID.\n *\n * @param args - The DMK connection arguments.\n * @returns The created session ID.\n */\n async connect(...args: ConnectParameters): ConnectResult {\n const previousManagedSessionId = this.#managedSessionId;\n const sessionId = await this.#sdk.connect(...args);\n this.#assignSession(sessionId);\n this.#managedSessionId = sessionId;\n\n if (previousManagedSessionId && previousManagedSessionId !== sessionId) {\n await this.#sdk.disconnect({ sessionId: previousManagedSessionId });\n }\n\n return sessionId;\n }\n\n /**\n * Build (or return the cached) Ethereum signer for the current session.\n *\n * The signer is memoized per session ID. `getEthSigner()` is called from\n * five different operations on the bridge (`getPublicKey`,\n * `deviceSignTransaction`, `deviceSignMessage`, `deviceSignTypedData`,\n * `deviceSignDelegationAuthorization`); a single bridge session therefore\n * needs at most one signer instance.\n *\n * Rebuilding is not free: `SignerEthBuilder.build()` allocates a fresh\n * `DefaultContextModule` from `@ledgerhq/context-module`, which in its\n * constructor builds a DI container and instantiates the default loaders\n * (calldata, dynamic-network, external-plugin, gated-signing, nft, proxy,\n * safe, token, trusted-name, typed-data, reporter, transaction-check, plus\n * the field loaders, typed-data loader and blind-signing reporter). See\n * `node_modules/@ledgerhq/context-module/lib/esm/src/DefaultContextModule.js`.\n *\n * Neither `DefaultSignerEth` nor `DefaultContextModule` expose a\n * `dispose()` / `disconnect()` / `destroy()` method (verified against\n * `node_modules/@ledgerhq/device-signer-kit-ethereum/lib/esm/internal/DefaultSignerEth.js`\n * and the `DefaultContextModule` source above). If any of those loaders\n * hold subscriptions to device-session state, re-creating the signer on\n * every call would leak them. Caching is therefore defensive against\n * subscription leaks, not just a micro-optimization.\n *\n * The cache is invalidated by `setSessionId()` when the session changes\n * and by `dispose()` on shutdown.\n *\n * @returns An Ethereum signer instance bound to the current session.\n */\n getEthSigner(): EthSigner {\n const sessionId = this.getSessionId();\n if (this.#cachedSigner?.sessionId === sessionId) {\n return this.#cachedSigner.signer;\n }\n const signer = new SignerEthBuilder({\n dmk: this.#sdk,\n sessionId,\n }).build();\n this.#cachedSigner = { sessionId, signer };\n return signer;\n }\n\n /**\n * Clears the stored session ID and signer cache without disconnecting from\n * DMK.\n *\n * Use when the bridge does not own the DMK instance and the host manages\n * session lifecycle externally.\n */\n clearSession(): void {\n this.#sessionId = undefined;\n this.#managedSessionId = undefined;\n this.#cachedSigner = null;\n }\n\n /**\n * Disconnect the current session and clear cached state.\n *\n * Silently no-ops when no session has been established.\n *\n * @returns A promise that resolves when the session is closed.\n */\n async dispose(): Promise<void> {\n const sessionToDisconnect = this.#managedSessionId ?? this.#sessionId;\n if (sessionToDisconnect) {\n await this.#sdk.disconnect({ sessionId: sessionToDisconnect });\n this.clearSession();\n }\n }\n\n #assignSession(sessionId: string): void {\n this.#cachedSigner = null;\n this.#sessionId = sessionId;\n }\n}\n"]}
package/dist/index.cjs CHANGED
@@ -17,10 +17,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./ledger-keyring.cjs"), exports);
18
18
  __exportStar(require("./ledger-iframe-bridge.cjs"), exports);
19
19
  __exportStar(require("./ledger-mobile-bridge.cjs"), exports);
20
+ __exportStar(require("./dmk/ledger-dmk-bridge.cjs"), exports);
21
+ __exportStar(require("./dmk/ledger-dmk-transport-middleware.cjs"), exports);
20
22
  __exportStar(require("./ledger-transport-middleware.cjs"), exports);
21
23
  __exportStar(require("./ledger-hw-app.cjs"), exports);
22
24
  __exportStar(require("./errors.cjs"), exports);
23
25
  __exportStar(require("./ledger-error-handler.cjs"), exports);
24
26
  __exportStar(require("./constants.cjs"), exports);
25
27
  __exportStar(require("./utils.cjs"), exports);
28
+ __exportStar(require("./dmk/dmk-error-translator.cjs"), exports);
26
29
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,uDAAiC;AACjC,6DAAuC;AACvC,6DAAuC;AAEvC,oEAA8C;AAE9C,sDAAgC;AAChC,+CAAyB;AACzB,6DAAuC;AACvC,kDAA4B;AAC5B,8CAAwB","sourcesContent":["export * from './ledger-keyring';\nexport * from './ledger-iframe-bridge';\nexport * from './ledger-mobile-bridge';\nexport type * from './ledger-bridge';\nexport * from './ledger-transport-middleware';\nexport type * from './type';\nexport * from './ledger-hw-app';\nexport * from './errors';\nexport * from './ledger-error-handler';\nexport * from './constants';\nexport * from './utils';\n"]}
1
+ {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,uDAAiC;AACjC,6DAAuC;AACvC,6DAAuC;AACvC,8DAAwC;AACxC,4EAAsD;AAEtD,oEAA8C;AAE9C,sDAAgC;AAChC,+CAAyB;AACzB,6DAAuC;AACvC,kDAA4B;AAC5B,8CAAwB;AACxB,iEAA2C","sourcesContent":["export * from './ledger-keyring';\nexport * from './ledger-iframe-bridge';\nexport * from './ledger-mobile-bridge';\nexport * from './dmk/ledger-dmk-bridge';\nexport * from './dmk/ledger-dmk-transport-middleware';\nexport type * from './ledger-bridge';\nexport * from './ledger-transport-middleware';\nexport type * from './type';\nexport * from './ledger-hw-app';\nexport * from './errors';\nexport * from './ledger-error-handler';\nexport * from './constants';\nexport * from './utils';\nexport * from './dmk/dmk-error-translator';\n"]}
package/dist/index.d.cts CHANGED
@@ -1,6 +1,8 @@
1
1
  export * from "./ledger-keyring.cjs";
2
2
  export * from "./ledger-iframe-bridge.cjs";
3
3
  export * from "./ledger-mobile-bridge.cjs";
4
+ export * from "./dmk/ledger-dmk-bridge.cjs";
5
+ export * from "./dmk/ledger-dmk-transport-middleware.cjs";
4
6
  export type * from "./ledger-bridge.cjs";
5
7
  export * from "./ledger-transport-middleware.cjs";
6
8
  export type * from "./type.cjs";
@@ -9,4 +11,5 @@ export * from "./errors.cjs";
9
11
  export * from "./ledger-error-handler.cjs";
10
12
  export * from "./constants.cjs";
11
13
  export * from "./utils.cjs";
14
+ export * from "./dmk/dmk-error-translator.cjs";
12
15
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qCAAiC;AACjC,2CAAuC;AACvC,2CAAuC;AACvC,yCAAqC;AACrC,kDAA8C;AAC9C,gCAA4B;AAC5B,oCAAgC;AAChC,6BAAyB;AACzB,2CAAuC;AACvC,gCAA4B;AAC5B,4BAAwB"}
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qCAAiC;AACjC,2CAAuC;AACvC,2CAAuC;AACvC,4CAAwC;AACxC,0DAAsD;AACtD,yCAAqC;AACrC,kDAA8C;AAC9C,gCAA4B;AAC5B,oCAAgC;AAChC,6BAAyB;AACzB,2CAAuC;AACvC,gCAA4B;AAC5B,4BAAwB;AACxB,+CAA2C"}