@metamask/keyring-sdk 4.0.0 → 4.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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [4.2.0]
11
+
12
+ ### Added
13
+
14
+ - Add `AtomicKeyring` support ([#640](https://github.com/MetaMask/accounts/pull/640))
15
+ - Atomic keyrings run long-running operations inside their own read-write lock, entirely outside the controller mutex (only the `update` method will be guarded by the `KeyringController`'s vault mutex).
16
+ - Add `AtomicUpdater` type, callback forwarded by the `KeyringController` during register phase.
17
+ - Add `isAtomicKeyring` helper to identify atomic keyrings.
18
+ - Add `ReadWriteLock` primitive with parallel readers, exclusive writers ([#640](https://github.com/MetaMask/accounts/pull/640))
19
+ - Also support priority mode: `'read'` or `'write'`.
20
+
21
+ ## [4.1.0]
22
+
23
+ ### Added
24
+
25
+ - Add `isKeyringV1Adapter` type guard ([#685](https://github.com/MetaMask/accounts/pull/685))
26
+ - Use this instead of doing type checks with `instanceof`.
27
+
28
+ ### Changed
29
+
30
+ - Bump `@metamask/keyring-api` from `^24.2.0` to `^24.3.0` ([#674](https://github.com/MetaMask/accounts/pull/674))
31
+
10
32
  ## [4.0.0]
11
33
 
12
34
  ### Changed
@@ -130,7 +152,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
130
152
 
131
153
  - Initial release, extracted from `@metamask/keyring-api` ([#478](https://github.com/MetaMask/accounts/pull/478), [#482](https://github.com/MetaMask/accounts/pull/482))
132
154
 
133
- [Unreleased]: https://github.com/MetaMask/accounts/compare/@metamask/keyring-sdk@4.0.0...HEAD
155
+ [Unreleased]: https://github.com/MetaMask/accounts/compare/@metamask/keyring-sdk@4.2.0...HEAD
156
+ [4.2.0]: https://github.com/MetaMask/accounts/compare/@metamask/keyring-sdk@4.1.0...@metamask/keyring-sdk@4.2.0
157
+ [4.1.0]: https://github.com/MetaMask/accounts/compare/@metamask/keyring-sdk@4.0.0...@metamask/keyring-sdk@4.1.0
134
158
  [4.0.0]: https://github.com/MetaMask/accounts/compare/@metamask/keyring-sdk@3.1.0...@metamask/keyring-sdk@4.0.0
135
159
  [3.1.0]: https://github.com/MetaMask/accounts/compare/@metamask/keyring-sdk@3.0.0...@metamask/keyring-sdk@3.1.0
136
160
  [3.0.0]: https://github.com/MetaMask/accounts/compare/@metamask/keyring-sdk@2.3.0...@metamask/keyring-sdk@3.0.0
package/README.md CHANGED
@@ -42,6 +42,97 @@ export class MyKeyringV2 extends EthKeyringWrapper<MyLegacyKeyring> {
42
42
  }
43
43
  ```
44
44
 
45
+ ## Atomic keyrings
46
+
47
+ Most keyrings are in-memory: their operations complete in microseconds, so the
48
+ `KeyringController` can safely wrap every operation in a controller-wide mutex.
49
+ That model breaks down for keyrings whose operations are long-running network
50
+ protocols (for example, MPC ceremonies): holding the mutex for the ceremony
51
+ freezes every other controller operation, and multi-step remote/local
52
+ transitions cannot be made crash-safe.
53
+
54
+ `AtomicKeyring` inverts the lock ownership for such keyrings. The keyring
55
+ serializes its own operations with an internal read-write lock, runs long work
56
+ entirely outside the controller mutex, and calls back into the controller —
57
+ through the injected `update` callback — only when it has new state to persist.
58
+ The controller holds its mutex just for the sub-millisecond commit window.
59
+
60
+ ```ts
61
+ import {
62
+ AtomicKeyring,
63
+ isAtomicKeyring,
64
+ type AtomicUpdater,
65
+ } from '@metamask/keyring-sdk';
66
+
67
+ class ExampleAtomicKeyring extends AtomicKeyring {
68
+ #state: 'uninitialized' | 'staged' | 'ready' = 'uninitialized';
69
+
70
+ constructor(updater: AtomicUpdater) {
71
+ super({ updater });
72
+ }
73
+
74
+ // Snapshot reads stay lock-free: the controller calls them while holding
75
+ // its own mutex.
76
+ async serialize(): Promise<Json> {
77
+ return { state: this.#state };
78
+ }
79
+
80
+ async createAccount(): Promise<void> {
81
+ // Long-running work runs inside the write lock, outside the controller
82
+ // mutex. Readers and other writers queue behind it.
83
+ await this.withWriteLock(async () => {
84
+ const next = await this.#runCeremony(); // compute and validate — may fail freely
85
+
86
+ // Checkpoint 1: stage the pre-computed state.
87
+ await this.update(() => {
88
+ this.#state = 'staged';
89
+ });
90
+
91
+ await this.#activateRemote(); // remote commit
92
+
93
+ // Checkpoint 2: promote to ready.
94
+ await this.update(() => {
95
+ this.#state = 'ready';
96
+ });
97
+ });
98
+ }
99
+ }
100
+
101
+ // Brand-based detection — never `instanceof`, which fails across duplicate
102
+ // copies of the package.
103
+ if (isAtomicKeyring(value)) {
104
+ /* dispatch without holding the controller mutex */
105
+ }
106
+ ```
107
+
108
+ The contract, enforced by design rather than caller discipline:
109
+
110
+ - **Actions are pure assignments.** Compute and validate the next state
111
+ _before_ calling `update`. A throwing action is a keyring bug.
112
+ - **No nested `update` calls.** A nested call is rejected with an explicit
113
+ error, never a deadlock.
114
+ - **Snapshot reads stay lock-free.** `getAccounts` and `serialize` are called
115
+ by the controller while it holds its mutex (and by `update` while the keyring
116
+ holds its write lock), so they must return immediately from the current
117
+ state.
118
+ - **`init` stays cheap.** The controller invokes `init` under its lock, at
119
+ construction and vault-restore time. Long work belongs in write operations.
120
+ - **No rollback.** On the atomic dispatch path a failed operation keeps every
121
+ committed checkpoint, so the next write operation advances the state machine
122
+ instead of re-running the ceremony.
123
+
124
+ A crash between the checkpoints leaves a persisted, recoverable intermediate
125
+ state; the next write operation advances the state machine (`staged` →
126
+ `ready`) without re-running the ceremony.
127
+
128
+ `ReadWriteLock` is also exported on its own, for keyrings that want the
129
+ read-write lock without the base class: parallel readers, exclusive writers,
130
+ first-in-first-out grant order, and a new reader always queues behind a
131
+ pending writer — so a steady stream of reads cannot starve a long write. For
132
+ read-heavy workloads, `{ priority: 'read' }` lets new readers join active
133
+ readers even while a writer is merely queued, at the cost that continuous
134
+ reads can starve a queued writer.
135
+
45
136
  ## Contributing
46
137
 
47
138
  This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/accounts#readme).
@@ -0,0 +1,126 @@
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 _AtomicKeyring_updater, _AtomicKeyring_rwLock, _AtomicKeyring_updating;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.isAtomicKeyring = exports.AtomicKeyring = void 0;
16
+ const read_write_lock_1 = require("./read-write-lock.cjs");
17
+ /**
18
+ * Brand used to mark `AtomicKeyring` instances. Registered on the global
19
+ * symbol registry, so the brand survives duplicate copies of this package —
20
+ * where `instanceof` would fail — and `isAtomicKeyring` keeps working.
21
+ */
22
+ const ATOMIC_KEYRING_BRAND = Symbol.for('@metamask/keyring-sdk/atomic-keyring');
23
+ /**
24
+ * Base class for keyrings that serialize their own operations and call back
25
+ * into the controller only to persist state.
26
+ *
27
+ * Long-running work runs inside the lock helpers, entirely outside the
28
+ * controller mutex; state is committed through `update`.
29
+ *
30
+ * Subclasses implementing an `init` lifecycle hook must keep it cheap —
31
+ * in-memory work only. The controller invokes `init` under its lock, at
32
+ * construction and vault-restore time; an `init` that performs long work,
33
+ * or calls `update` (which needs that same, non-reentrant lock), would
34
+ * block or deadlock the controller. Long work belongs in write operations,
35
+ * which the controller dispatches without holding its lock.
36
+ *
37
+ * Snapshot reads the controller performs itself — `getAccounts` and
38
+ * `serialize` — must stay lock-free in subclasses: the controller calls
39
+ * them while holding its own mutex, and the updater calls `serialize` while
40
+ * the keyring is inside a write operation. They must return immediately
41
+ * from the current state.
42
+ */
43
+ class AtomicKeyring {
44
+ /**
45
+ * The updater is required at construction — no unbound state, ever.
46
+ *
47
+ * @param options - Constructor options.
48
+ * @param options.updater - The controller's vault-write callback, which
49
+ * this keyring invokes through `update` to commit state.
50
+ */
51
+ constructor(options) {
52
+ _AtomicKeyring_updater.set(this, void 0);
53
+ _AtomicKeyring_rwLock.set(this, new read_write_lock_1.ReadWriteLock());
54
+ _AtomicKeyring_updating.set(this, false);
55
+ __classPrivateFieldSet(this, _AtomicKeyring_updater, options.updater, "f");
56
+ this[ATOMIC_KEYRING_BRAND] = true;
57
+ }
58
+ /**
59
+ * Commit a state mutation on behalf of this keyring. Injects `this` into
60
+ * the updater, so the controller can verify registration.
61
+ *
62
+ * Actions must be pure assignments of pre-computed next state: compute
63
+ * and validate before calling `update`. A throwing action is a keyring
64
+ * bug, and is rethrown as such. Nested `update` calls are rejected with
65
+ * an error, never a deadlock.
66
+ *
67
+ * Call `update` only from within a write-locked operation, so commits
68
+ * cannot interleave with other operations on this keyring.
69
+ *
70
+ * @param action - The state mutation to commit. Must be a pure assignment
71
+ * of pre-computed next state.
72
+ * @throws If called while another `update` call is in progress, or if the
73
+ * action or updater throws.
74
+ */
75
+ async update(action) {
76
+ if (__classPrivateFieldGet(this, _AtomicKeyring_updating, "f")) {
77
+ throw new Error('AtomicKeyring - Nested update() calls are not allowed. The update() action must not call update() again.');
78
+ }
79
+ __classPrivateFieldSet(this, _AtomicKeyring_updating, true, "f");
80
+ try {
81
+ await __classPrivateFieldGet(this, _AtomicKeyring_updater, "f").call(this, this, action);
82
+ }
83
+ finally {
84
+ __classPrivateFieldSet(this, _AtomicKeyring_updating, false, "f");
85
+ }
86
+ }
87
+ /**
88
+ * Run a read operation — parallel with other readers, queued behind
89
+ * writers. Only for operations that can tolerate waiting behind a write
90
+ * section.
91
+ *
92
+ * @param fn - The operation to run while holding the read lock.
93
+ * @returns Whatever `fn` resolves to.
94
+ */
95
+ async withReadLock(fn) {
96
+ return __classPrivateFieldGet(this, _AtomicKeyring_rwLock, "f").withReadLock(fn);
97
+ }
98
+ /**
99
+ * Run a write operation — exclusive with readers and other writers.
100
+ * Long-running network work belongs here, outside the controller mutex.
101
+ *
102
+ * @param fn - The operation to run while holding the write lock.
103
+ * @returns Whatever `fn` resolves to.
104
+ */
105
+ async withWriteLock(fn) {
106
+ return __classPrivateFieldGet(this, _AtomicKeyring_rwLock, "f").withWriteLock(fn);
107
+ }
108
+ }
109
+ exports.AtomicKeyring = AtomicKeyring;
110
+ _AtomicKeyring_updater = new WeakMap(), _AtomicKeyring_rwLock = new WeakMap(), _AtomicKeyring_updating = new WeakMap();
111
+ /**
112
+ * Whether the value is an `AtomicKeyring`.
113
+ *
114
+ * Brand-based — a global-registry symbol set by the constructor — so it
115
+ * works across duplicate copies of the package, where `instanceof` fails.
116
+ *
117
+ * @param value - The value to check.
118
+ * @returns Whether the value is an `AtomicKeyring` instance.
119
+ */
120
+ function isAtomicKeyring(value) {
121
+ return (typeof value === 'object' &&
122
+ value !== null &&
123
+ value[ATOMIC_KEYRING_BRAND] === true);
124
+ }
125
+ exports.isAtomicKeyring = isAtomicKeyring;
126
+ //# sourceMappingURL=atomic-keyring.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-keyring.cjs","sourceRoot":"","sources":["../src/atomic-keyring.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2DAAkD;AAiBlD;;;;GAIG;AACH,MAAM,oBAAoB,GAAG,MAAM,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;AAEhF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAsB,aAAa;IAOjC;;;;;;OAMG;IACH,YAAY,OAAmC;QAbtC,yCAAwB;QAExB,gCAAyB,IAAI,+BAAa,EAAE,EAAC;QAEtD,kCAAqB,KAAK,EAAC;QAUzB,uBAAA,IAAI,0BAAY,OAAO,CAAC,OAAO,MAAA,CAAC;QAC/B,IAAqC,CAAC,oBAAoB,CAAC,GAAG,IAAI,CAAC;IACtE,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACO,KAAK,CAAC,MAAM,CAAC,MAAkC;QACvD,IAAI,uBAAA,IAAI,+BAAU,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CACb,0GAA0G,CAC3G,CAAC;QACJ,CAAC;QAED,uBAAA,IAAI,2BAAa,IAAI,MAAA,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,uBAAA,IAAI,8BAAS,MAAb,IAAI,EAAU,IAAI,EAAE,MAAM,CAAC,CAAC;QACpC,CAAC;gBAAS,CAAC;YACT,uBAAA,IAAI,2BAAa,KAAK,MAAA,CAAC;QACzB,CAAC;IACH,CAAC;IAED;;;;;;;OAOG;IACO,KAAK,CAAC,YAAY,CAC1B,EAAyB;QAEzB,OAAO,uBAAA,IAAI,6BAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IACvC,CAAC;IAED;;;;;;OAMG;IACO,KAAK,CAAC,aAAa,CAC3B,EAAyB;QAEzB,OAAO,uBAAA,IAAI,6BAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IACxC,CAAC;CACF;AA7ED,sCA6EC;;AAED;;;;;;;;GAQG;AACH,SAAgB,eAAe,CAAC,KAAc;IAC5C,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACb,KAAsC,CAAC,oBAAoB,CAAC,KAAK,IAAI,CACvE,CAAC;AACJ,CAAC;AAND,0CAMC","sourcesContent":["import { ReadWriteLock } from './read-write-lock';\n\n/**\n * A vault-write callback injected into an `AtomicKeyring` at construction,\n * via the keyring builder context.\n *\n * Stateless: the calling keyring is the first parameter, so a single\n * function serves every keyring. Acquires the controller lock, verifies the\n * keyring is still registered, runs `action()`, writes the vault if and only\n * if the serialized state changed, then releases — a sub-millisecond commit\n * window.\n */\nexport type AtomicUpdater = (\n keyring: AtomicKeyring,\n action: () => void | Promise<void>,\n) => Promise<void>;\n\n/**\n * Brand used to mark `AtomicKeyring` instances. Registered on the global\n * symbol registry, so the brand survives duplicate copies of this package —\n * where `instanceof` would fail — and `isAtomicKeyring` keeps working.\n */\nconst ATOMIC_KEYRING_BRAND = Symbol.for('@metamask/keyring-sdk/atomic-keyring');\n\n/**\n * Base class for keyrings that serialize their own operations and call back\n * into the controller only to persist state.\n *\n * Long-running work runs inside the lock helpers, entirely outside the\n * controller mutex; state is committed through `update`.\n *\n * Subclasses implementing an `init` lifecycle hook must keep it cheap —\n * in-memory work only. The controller invokes `init` under its lock, at\n * construction and vault-restore time; an `init` that performs long work,\n * or calls `update` (which needs that same, non-reentrant lock), would\n * block or deadlock the controller. Long work belongs in write operations,\n * which the controller dispatches without holding its lock.\n *\n * Snapshot reads the controller performs itself — `getAccounts` and\n * `serialize` — must stay lock-free in subclasses: the controller calls\n * them while holding its own mutex, and the updater calls `serialize` while\n * the keyring is inside a write operation. They must return immediately\n * from the current state.\n */\nexport abstract class AtomicKeyring {\n readonly #updater: AtomicUpdater;\n\n readonly #rwLock: ReadWriteLock = new ReadWriteLock();\n\n #updating: boolean = false;\n\n /**\n * The updater is required at construction — no unbound state, ever.\n *\n * @param options - Constructor options.\n * @param options.updater - The controller's vault-write callback, which\n * this keyring invokes through `update` to commit state.\n */\n constructor(options: { updater: AtomicUpdater }) {\n this.#updater = options.updater;\n (this as Record<PropertyKey, unknown>)[ATOMIC_KEYRING_BRAND] = true;\n }\n\n /**\n * Commit a state mutation on behalf of this keyring. Injects `this` into\n * the updater, so the controller can verify registration.\n *\n * Actions must be pure assignments of pre-computed next state: compute\n * and validate before calling `update`. A throwing action is a keyring\n * bug, and is rethrown as such. Nested `update` calls are rejected with\n * an error, never a deadlock.\n *\n * Call `update` only from within a write-locked operation, so commits\n * cannot interleave with other operations on this keyring.\n *\n * @param action - The state mutation to commit. Must be a pure assignment\n * of pre-computed next state.\n * @throws If called while another `update` call is in progress, or if the\n * action or updater throws.\n */\n protected async update(action: () => void | Promise<void>): Promise<void> {\n if (this.#updating) {\n throw new Error(\n 'AtomicKeyring - Nested update() calls are not allowed. The update() action must not call update() again.',\n );\n }\n\n this.#updating = true;\n try {\n await this.#updater(this, action);\n } finally {\n this.#updating = false;\n }\n }\n\n /**\n * Run a read operation — parallel with other readers, queued behind\n * writers. Only for operations that can tolerate waiting behind a write\n * section.\n *\n * @param fn - The operation to run while holding the read lock.\n * @returns Whatever `fn` resolves to.\n */\n protected async withReadLock<Result>(\n fn: () => Promise<Result>,\n ): Promise<Result> {\n return this.#rwLock.withReadLock(fn);\n }\n\n /**\n * Run a write operation — exclusive with readers and other writers.\n * Long-running network work belongs here, outside the controller mutex.\n *\n * @param fn - The operation to run while holding the write lock.\n * @returns Whatever `fn` resolves to.\n */\n protected async withWriteLock<Result>(\n fn: () => Promise<Result>,\n ): Promise<Result> {\n return this.#rwLock.withWriteLock(fn);\n }\n}\n\n/**\n * Whether the value is an `AtomicKeyring`.\n *\n * Brand-based — a global-registry symbol set by the constructor — so it\n * works across duplicate copies of the package, where `instanceof` fails.\n *\n * @param value - The value to check.\n * @returns Whether the value is an `AtomicKeyring` instance.\n */\nexport function isAtomicKeyring(value: unknown): value is AtomicKeyring {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as Record<PropertyKey, unknown>)[ATOMIC_KEYRING_BRAND] === true\n );\n}\n"]}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * A vault-write callback injected into an `AtomicKeyring` at construction,
3
+ * via the keyring builder context.
4
+ *
5
+ * Stateless: the calling keyring is the first parameter, so a single
6
+ * function serves every keyring. Acquires the controller lock, verifies the
7
+ * keyring is still registered, runs `action()`, writes the vault if and only
8
+ * if the serialized state changed, then releases — a sub-millisecond commit
9
+ * window.
10
+ */
11
+ export type AtomicUpdater = (keyring: AtomicKeyring, action: () => void | Promise<void>) => Promise<void>;
12
+ /**
13
+ * Base class for keyrings that serialize their own operations and call back
14
+ * into the controller only to persist state.
15
+ *
16
+ * Long-running work runs inside the lock helpers, entirely outside the
17
+ * controller mutex; state is committed through `update`.
18
+ *
19
+ * Subclasses implementing an `init` lifecycle hook must keep it cheap —
20
+ * in-memory work only. The controller invokes `init` under its lock, at
21
+ * construction and vault-restore time; an `init` that performs long work,
22
+ * or calls `update` (which needs that same, non-reentrant lock), would
23
+ * block or deadlock the controller. Long work belongs in write operations,
24
+ * which the controller dispatches without holding its lock.
25
+ *
26
+ * Snapshot reads the controller performs itself — `getAccounts` and
27
+ * `serialize` — must stay lock-free in subclasses: the controller calls
28
+ * them while holding its own mutex, and the updater calls `serialize` while
29
+ * the keyring is inside a write operation. They must return immediately
30
+ * from the current state.
31
+ */
32
+ export declare abstract class AtomicKeyring {
33
+ #private;
34
+ /**
35
+ * The updater is required at construction — no unbound state, ever.
36
+ *
37
+ * @param options - Constructor options.
38
+ * @param options.updater - The controller's vault-write callback, which
39
+ * this keyring invokes through `update` to commit state.
40
+ */
41
+ constructor(options: {
42
+ updater: AtomicUpdater;
43
+ });
44
+ /**
45
+ * Commit a state mutation on behalf of this keyring. Injects `this` into
46
+ * the updater, so the controller can verify registration.
47
+ *
48
+ * Actions must be pure assignments of pre-computed next state: compute
49
+ * and validate before calling `update`. A throwing action is a keyring
50
+ * bug, and is rethrown as such. Nested `update` calls are rejected with
51
+ * an error, never a deadlock.
52
+ *
53
+ * Call `update` only from within a write-locked operation, so commits
54
+ * cannot interleave with other operations on this keyring.
55
+ *
56
+ * @param action - The state mutation to commit. Must be a pure assignment
57
+ * of pre-computed next state.
58
+ * @throws If called while another `update` call is in progress, or if the
59
+ * action or updater throws.
60
+ */
61
+ protected update(action: () => void | Promise<void>): Promise<void>;
62
+ /**
63
+ * Run a read operation — parallel with other readers, queued behind
64
+ * writers. Only for operations that can tolerate waiting behind a write
65
+ * section.
66
+ *
67
+ * @param fn - The operation to run while holding the read lock.
68
+ * @returns Whatever `fn` resolves to.
69
+ */
70
+ protected withReadLock<Result>(fn: () => Promise<Result>): Promise<Result>;
71
+ /**
72
+ * Run a write operation — exclusive with readers and other writers.
73
+ * Long-running network work belongs here, outside the controller mutex.
74
+ *
75
+ * @param fn - The operation to run while holding the write lock.
76
+ * @returns Whatever `fn` resolves to.
77
+ */
78
+ protected withWriteLock<Result>(fn: () => Promise<Result>): Promise<Result>;
79
+ }
80
+ /**
81
+ * Whether the value is an `AtomicKeyring`.
82
+ *
83
+ * Brand-based — a global-registry symbol set by the constructor — so it
84
+ * works across duplicate copies of the package, where `instanceof` fails.
85
+ *
86
+ * @param value - The value to check.
87
+ * @returns Whether the value is an `AtomicKeyring` instance.
88
+ */
89
+ export declare function isAtomicKeyring(value: unknown): value is AtomicKeyring;
90
+ //# sourceMappingURL=atomic-keyring.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-keyring.d.cts","sourceRoot":"","sources":["../src/atomic-keyring.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,MAAM,aAAa,GAAG,CAC1B,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAC/B,OAAO,CAAC,IAAI,CAAC,CAAC;AASnB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,8BAAsB,aAAa;;IAOjC;;;;;;OAMG;gBACS,OAAO,EAAE;QAAE,OAAO,EAAE,aAAa,CAAA;KAAE;IAK/C;;;;;;;;;;;;;;;;OAgBG;cACa,MAAM,CAAC,MAAM,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAezE;;;;;;;OAOG;cACa,YAAY,CAAC,MAAM,EACjC,EAAE,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GACxB,OAAO,CAAC,MAAM,CAAC;IAIlB;;;;;;OAMG;cACa,aAAa,CAAC,MAAM,EAClC,EAAE,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GACxB,OAAO,CAAC,MAAM,CAAC;CAGnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAMtE"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * A vault-write callback injected into an `AtomicKeyring` at construction,
3
+ * via the keyring builder context.
4
+ *
5
+ * Stateless: the calling keyring is the first parameter, so a single
6
+ * function serves every keyring. Acquires the controller lock, verifies the
7
+ * keyring is still registered, runs `action()`, writes the vault if and only
8
+ * if the serialized state changed, then releases — a sub-millisecond commit
9
+ * window.
10
+ */
11
+ export type AtomicUpdater = (keyring: AtomicKeyring, action: () => void | Promise<void>) => Promise<void>;
12
+ /**
13
+ * Base class for keyrings that serialize their own operations and call back
14
+ * into the controller only to persist state.
15
+ *
16
+ * Long-running work runs inside the lock helpers, entirely outside the
17
+ * controller mutex; state is committed through `update`.
18
+ *
19
+ * Subclasses implementing an `init` lifecycle hook must keep it cheap —
20
+ * in-memory work only. The controller invokes `init` under its lock, at
21
+ * construction and vault-restore time; an `init` that performs long work,
22
+ * or calls `update` (which needs that same, non-reentrant lock), would
23
+ * block or deadlock the controller. Long work belongs in write operations,
24
+ * which the controller dispatches without holding its lock.
25
+ *
26
+ * Snapshot reads the controller performs itself — `getAccounts` and
27
+ * `serialize` — must stay lock-free in subclasses: the controller calls
28
+ * them while holding its own mutex, and the updater calls `serialize` while
29
+ * the keyring is inside a write operation. They must return immediately
30
+ * from the current state.
31
+ */
32
+ export declare abstract class AtomicKeyring {
33
+ #private;
34
+ /**
35
+ * The updater is required at construction — no unbound state, ever.
36
+ *
37
+ * @param options - Constructor options.
38
+ * @param options.updater - The controller's vault-write callback, which
39
+ * this keyring invokes through `update` to commit state.
40
+ */
41
+ constructor(options: {
42
+ updater: AtomicUpdater;
43
+ });
44
+ /**
45
+ * Commit a state mutation on behalf of this keyring. Injects `this` into
46
+ * the updater, so the controller can verify registration.
47
+ *
48
+ * Actions must be pure assignments of pre-computed next state: compute
49
+ * and validate before calling `update`. A throwing action is a keyring
50
+ * bug, and is rethrown as such. Nested `update` calls are rejected with
51
+ * an error, never a deadlock.
52
+ *
53
+ * Call `update` only from within a write-locked operation, so commits
54
+ * cannot interleave with other operations on this keyring.
55
+ *
56
+ * @param action - The state mutation to commit. Must be a pure assignment
57
+ * of pre-computed next state.
58
+ * @throws If called while another `update` call is in progress, or if the
59
+ * action or updater throws.
60
+ */
61
+ protected update(action: () => void | Promise<void>): Promise<void>;
62
+ /**
63
+ * Run a read operation — parallel with other readers, queued behind
64
+ * writers. Only for operations that can tolerate waiting behind a write
65
+ * section.
66
+ *
67
+ * @param fn - The operation to run while holding the read lock.
68
+ * @returns Whatever `fn` resolves to.
69
+ */
70
+ protected withReadLock<Result>(fn: () => Promise<Result>): Promise<Result>;
71
+ /**
72
+ * Run a write operation — exclusive with readers and other writers.
73
+ * Long-running network work belongs here, outside the controller mutex.
74
+ *
75
+ * @param fn - The operation to run while holding the write lock.
76
+ * @returns Whatever `fn` resolves to.
77
+ */
78
+ protected withWriteLock<Result>(fn: () => Promise<Result>): Promise<Result>;
79
+ }
80
+ /**
81
+ * Whether the value is an `AtomicKeyring`.
82
+ *
83
+ * Brand-based — a global-registry symbol set by the constructor — so it
84
+ * works across duplicate copies of the package, where `instanceof` fails.
85
+ *
86
+ * @param value - The value to check.
87
+ * @returns Whether the value is an `AtomicKeyring` instance.
88
+ */
89
+ export declare function isAtomicKeyring(value: unknown): value is AtomicKeyring;
90
+ //# sourceMappingURL=atomic-keyring.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-keyring.d.mts","sourceRoot":"","sources":["../src/atomic-keyring.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,MAAM,aAAa,GAAG,CAC1B,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAC/B,OAAO,CAAC,IAAI,CAAC,CAAC;AASnB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,8BAAsB,aAAa;;IAOjC;;;;;;OAMG;gBACS,OAAO,EAAE;QAAE,OAAO,EAAE,aAAa,CAAA;KAAE;IAK/C;;;;;;;;;;;;;;;;OAgBG;cACa,MAAM,CAAC,MAAM,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAezE;;;;;;;OAOG;cACa,YAAY,CAAC,MAAM,EACjC,EAAE,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GACxB,OAAO,CAAC,MAAM,CAAC;IAIlB;;;;;;OAMG;cACa,aAAa,CAAC,MAAM,EAClC,EAAE,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GACxB,OAAO,CAAC,MAAM,CAAC;CAGnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAMtE"}
@@ -0,0 +1,121 @@
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 _AtomicKeyring_updater, _AtomicKeyring_rwLock, _AtomicKeyring_updating;
13
+ import { ReadWriteLock } from "./read-write-lock.mjs";
14
+ /**
15
+ * Brand used to mark `AtomicKeyring` instances. Registered on the global
16
+ * symbol registry, so the brand survives duplicate copies of this package —
17
+ * where `instanceof` would fail — and `isAtomicKeyring` keeps working.
18
+ */
19
+ const ATOMIC_KEYRING_BRAND = Symbol.for('@metamask/keyring-sdk/atomic-keyring');
20
+ /**
21
+ * Base class for keyrings that serialize their own operations and call back
22
+ * into the controller only to persist state.
23
+ *
24
+ * Long-running work runs inside the lock helpers, entirely outside the
25
+ * controller mutex; state is committed through `update`.
26
+ *
27
+ * Subclasses implementing an `init` lifecycle hook must keep it cheap —
28
+ * in-memory work only. The controller invokes `init` under its lock, at
29
+ * construction and vault-restore time; an `init` that performs long work,
30
+ * or calls `update` (which needs that same, non-reentrant lock), would
31
+ * block or deadlock the controller. Long work belongs in write operations,
32
+ * which the controller dispatches without holding its lock.
33
+ *
34
+ * Snapshot reads the controller performs itself — `getAccounts` and
35
+ * `serialize` — must stay lock-free in subclasses: the controller calls
36
+ * them while holding its own mutex, and the updater calls `serialize` while
37
+ * the keyring is inside a write operation. They must return immediately
38
+ * from the current state.
39
+ */
40
+ export class AtomicKeyring {
41
+ /**
42
+ * The updater is required at construction — no unbound state, ever.
43
+ *
44
+ * @param options - Constructor options.
45
+ * @param options.updater - The controller's vault-write callback, which
46
+ * this keyring invokes through `update` to commit state.
47
+ */
48
+ constructor(options) {
49
+ _AtomicKeyring_updater.set(this, void 0);
50
+ _AtomicKeyring_rwLock.set(this, new ReadWriteLock());
51
+ _AtomicKeyring_updating.set(this, false);
52
+ __classPrivateFieldSet(this, _AtomicKeyring_updater, options.updater, "f");
53
+ this[ATOMIC_KEYRING_BRAND] = true;
54
+ }
55
+ /**
56
+ * Commit a state mutation on behalf of this keyring. Injects `this` into
57
+ * the updater, so the controller can verify registration.
58
+ *
59
+ * Actions must be pure assignments of pre-computed next state: compute
60
+ * and validate before calling `update`. A throwing action is a keyring
61
+ * bug, and is rethrown as such. Nested `update` calls are rejected with
62
+ * an error, never a deadlock.
63
+ *
64
+ * Call `update` only from within a write-locked operation, so commits
65
+ * cannot interleave with other operations on this keyring.
66
+ *
67
+ * @param action - The state mutation to commit. Must be a pure assignment
68
+ * of pre-computed next state.
69
+ * @throws If called while another `update` call is in progress, or if the
70
+ * action or updater throws.
71
+ */
72
+ async update(action) {
73
+ if (__classPrivateFieldGet(this, _AtomicKeyring_updating, "f")) {
74
+ throw new Error('AtomicKeyring - Nested update() calls are not allowed. The update() action must not call update() again.');
75
+ }
76
+ __classPrivateFieldSet(this, _AtomicKeyring_updating, true, "f");
77
+ try {
78
+ await __classPrivateFieldGet(this, _AtomicKeyring_updater, "f").call(this, this, action);
79
+ }
80
+ finally {
81
+ __classPrivateFieldSet(this, _AtomicKeyring_updating, false, "f");
82
+ }
83
+ }
84
+ /**
85
+ * Run a read operation — parallel with other readers, queued behind
86
+ * writers. Only for operations that can tolerate waiting behind a write
87
+ * section.
88
+ *
89
+ * @param fn - The operation to run while holding the read lock.
90
+ * @returns Whatever `fn` resolves to.
91
+ */
92
+ async withReadLock(fn) {
93
+ return __classPrivateFieldGet(this, _AtomicKeyring_rwLock, "f").withReadLock(fn);
94
+ }
95
+ /**
96
+ * Run a write operation — exclusive with readers and other writers.
97
+ * Long-running network work belongs here, outside the controller mutex.
98
+ *
99
+ * @param fn - The operation to run while holding the write lock.
100
+ * @returns Whatever `fn` resolves to.
101
+ */
102
+ async withWriteLock(fn) {
103
+ return __classPrivateFieldGet(this, _AtomicKeyring_rwLock, "f").withWriteLock(fn);
104
+ }
105
+ }
106
+ _AtomicKeyring_updater = new WeakMap(), _AtomicKeyring_rwLock = new WeakMap(), _AtomicKeyring_updating = new WeakMap();
107
+ /**
108
+ * Whether the value is an `AtomicKeyring`.
109
+ *
110
+ * Brand-based — a global-registry symbol set by the constructor — so it
111
+ * works across duplicate copies of the package, where `instanceof` fails.
112
+ *
113
+ * @param value - The value to check.
114
+ * @returns Whether the value is an `AtomicKeyring` instance.
115
+ */
116
+ export function isAtomicKeyring(value) {
117
+ return (typeof value === 'object' &&
118
+ value !== null &&
119
+ value[ATOMIC_KEYRING_BRAND] === true);
120
+ }
121
+ //# sourceMappingURL=atomic-keyring.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-keyring.mjs","sourceRoot":"","sources":["../src/atomic-keyring.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAAE,aAAa,EAAE,8BAA0B;AAiBlD;;;;GAIG;AACH,MAAM,oBAAoB,GAAG,MAAM,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;AAEhF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAgB,aAAa;IAOjC;;;;;;OAMG;IACH,YAAY,OAAmC;QAbtC,yCAAwB;QAExB,gCAAyB,IAAI,aAAa,EAAE,EAAC;QAEtD,kCAAqB,KAAK,EAAC;QAUzB,uBAAA,IAAI,0BAAY,OAAO,CAAC,OAAO,MAAA,CAAC;QAC/B,IAAqC,CAAC,oBAAoB,CAAC,GAAG,IAAI,CAAC;IACtE,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACO,KAAK,CAAC,MAAM,CAAC,MAAkC;QACvD,IAAI,uBAAA,IAAI,+BAAU,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CACb,0GAA0G,CAC3G,CAAC;QACJ,CAAC;QAED,uBAAA,IAAI,2BAAa,IAAI,MAAA,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,uBAAA,IAAI,8BAAS,MAAb,IAAI,EAAU,IAAI,EAAE,MAAM,CAAC,CAAC;QACpC,CAAC;gBAAS,CAAC;YACT,uBAAA,IAAI,2BAAa,KAAK,MAAA,CAAC;QACzB,CAAC;IACH,CAAC;IAED;;;;;;;OAOG;IACO,KAAK,CAAC,YAAY,CAC1B,EAAyB;QAEzB,OAAO,uBAAA,IAAI,6BAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IACvC,CAAC;IAED;;;;;;OAMG;IACO,KAAK,CAAC,aAAa,CAC3B,EAAyB;QAEzB,OAAO,uBAAA,IAAI,6BAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IACxC,CAAC;CACF;;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACb,KAAsC,CAAC,oBAAoB,CAAC,KAAK,IAAI,CACvE,CAAC;AACJ,CAAC","sourcesContent":["import { ReadWriteLock } from './read-write-lock';\n\n/**\n * A vault-write callback injected into an `AtomicKeyring` at construction,\n * via the keyring builder context.\n *\n * Stateless: the calling keyring is the first parameter, so a single\n * function serves every keyring. Acquires the controller lock, verifies the\n * keyring is still registered, runs `action()`, writes the vault if and only\n * if the serialized state changed, then releases — a sub-millisecond commit\n * window.\n */\nexport type AtomicUpdater = (\n keyring: AtomicKeyring,\n action: () => void | Promise<void>,\n) => Promise<void>;\n\n/**\n * Brand used to mark `AtomicKeyring` instances. Registered on the global\n * symbol registry, so the brand survives duplicate copies of this package —\n * where `instanceof` would fail — and `isAtomicKeyring` keeps working.\n */\nconst ATOMIC_KEYRING_BRAND = Symbol.for('@metamask/keyring-sdk/atomic-keyring');\n\n/**\n * Base class for keyrings that serialize their own operations and call back\n * into the controller only to persist state.\n *\n * Long-running work runs inside the lock helpers, entirely outside the\n * controller mutex; state is committed through `update`.\n *\n * Subclasses implementing an `init` lifecycle hook must keep it cheap —\n * in-memory work only. The controller invokes `init` under its lock, at\n * construction and vault-restore time; an `init` that performs long work,\n * or calls `update` (which needs that same, non-reentrant lock), would\n * block or deadlock the controller. Long work belongs in write operations,\n * which the controller dispatches without holding its lock.\n *\n * Snapshot reads the controller performs itself — `getAccounts` and\n * `serialize` — must stay lock-free in subclasses: the controller calls\n * them while holding its own mutex, and the updater calls `serialize` while\n * the keyring is inside a write operation. They must return immediately\n * from the current state.\n */\nexport abstract class AtomicKeyring {\n readonly #updater: AtomicUpdater;\n\n readonly #rwLock: ReadWriteLock = new ReadWriteLock();\n\n #updating: boolean = false;\n\n /**\n * The updater is required at construction — no unbound state, ever.\n *\n * @param options - Constructor options.\n * @param options.updater - The controller's vault-write callback, which\n * this keyring invokes through `update` to commit state.\n */\n constructor(options: { updater: AtomicUpdater }) {\n this.#updater = options.updater;\n (this as Record<PropertyKey, unknown>)[ATOMIC_KEYRING_BRAND] = true;\n }\n\n /**\n * Commit a state mutation on behalf of this keyring. Injects `this` into\n * the updater, so the controller can verify registration.\n *\n * Actions must be pure assignments of pre-computed next state: compute\n * and validate before calling `update`. A throwing action is a keyring\n * bug, and is rethrown as such. Nested `update` calls are rejected with\n * an error, never a deadlock.\n *\n * Call `update` only from within a write-locked operation, so commits\n * cannot interleave with other operations on this keyring.\n *\n * @param action - The state mutation to commit. Must be a pure assignment\n * of pre-computed next state.\n * @throws If called while another `update` call is in progress, or if the\n * action or updater throws.\n */\n protected async update(action: () => void | Promise<void>): Promise<void> {\n if (this.#updating) {\n throw new Error(\n 'AtomicKeyring - Nested update() calls are not allowed. The update() action must not call update() again.',\n );\n }\n\n this.#updating = true;\n try {\n await this.#updater(this, action);\n } finally {\n this.#updating = false;\n }\n }\n\n /**\n * Run a read operation — parallel with other readers, queued behind\n * writers. Only for operations that can tolerate waiting behind a write\n * section.\n *\n * @param fn - The operation to run while holding the read lock.\n * @returns Whatever `fn` resolves to.\n */\n protected async withReadLock<Result>(\n fn: () => Promise<Result>,\n ): Promise<Result> {\n return this.#rwLock.withReadLock(fn);\n }\n\n /**\n * Run a write operation — exclusive with readers and other writers.\n * Long-running network work belongs here, outside the controller mutex.\n *\n * @param fn - The operation to run while holding the write lock.\n * @returns Whatever `fn` resolves to.\n */\n protected async withWriteLock<Result>(\n fn: () => Promise<Result>,\n ): Promise<Result> {\n return this.#rwLock.withWriteLock(fn);\n }\n}\n\n/**\n * Whether the value is an `AtomicKeyring`.\n *\n * Brand-based — a global-registry symbol set by the constructor — so it\n * works across duplicate copies of the package, where `instanceof` fails.\n *\n * @param value - The value to check.\n * @returns Whether the value is an `AtomicKeyring` instance.\n */\nexport function isAtomicKeyring(value: unknown): value is AtomicKeyring {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as Record<PropertyKey, unknown>)[ATOMIC_KEYRING_BRAND] === true\n );\n}\n"]}
package/dist/index.cjs CHANGED
@@ -14,9 +14,11 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./atomic-keyring.cjs"), exports);
17
18
  __exportStar(require("./keyring-account-registry.cjs"), exports);
18
19
  __exportStar(require("./migration.cjs"), exports);
19
20
  __exportStar(require("./mnemonic.cjs"), exports);
20
21
  __exportStar(require("./entropy.cjs"), exports);
22
+ __exportStar(require("./read-write-lock.cjs"), exports);
21
23
  __exportStar(require("./eth/index.cjs"), exports);
22
24
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,iEAA2C;AAC3C,kDAA4B;AAC5B,iDAA2B;AAC3B,gDAA0B;AAC1B,kDAAsB","sourcesContent":["export * from './keyring-account-registry';\nexport * from './migration';\nexport * from './mnemonic';\nexport * from './entropy';\nexport * from './eth';\n"]}
1
+ {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,uDAAiC;AACjC,iEAA2C;AAC3C,kDAA4B;AAC5B,iDAA2B;AAC3B,gDAA0B;AAC1B,wDAAkC;AAClC,kDAAsB","sourcesContent":["export * from './atomic-keyring';\nexport * from './keyring-account-registry';\nexport * from './migration';\nexport * from './mnemonic';\nexport * from './entropy';\nexport * from './read-write-lock';\nexport * from './eth';\n"]}
package/dist/index.d.cts CHANGED
@@ -1,6 +1,8 @@
1
+ export * from "./atomic-keyring.cjs";
1
2
  export * from "./keyring-account-registry.cjs";
2
3
  export * from "./migration.cjs";
3
4
  export * from "./mnemonic.cjs";
4
5
  export * from "./entropy.cjs";
6
+ export * from "./read-write-lock.cjs";
5
7
  export * from "./eth/index.cjs";
6
8
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+CAA2C;AAC3C,gCAA4B;AAC5B,+BAA2B;AAC3B,8BAA0B;AAC1B,gCAAsB"}
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qCAAiC;AACjC,+CAA2C;AAC3C,gCAA4B;AAC5B,+BAA2B;AAC3B,8BAA0B;AAC1B,sCAAkC;AAClC,gCAAsB"}
package/dist/index.d.mts CHANGED
@@ -1,6 +1,8 @@
1
+ export * from "./atomic-keyring.mjs";
1
2
  export * from "./keyring-account-registry.mjs";
2
3
  export * from "./migration.mjs";
3
4
  export * from "./mnemonic.mjs";
4
5
  export * from "./entropy.mjs";
6
+ export * from "./read-write-lock.mjs";
5
7
  export * from "./eth/index.mjs";
6
8
  //# sourceMappingURL=index.d.mts.map