@mysten-incubation/dev-wallet 0.3.0 → 0.4.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 (147) hide show
  1. package/CHANGELOG.md +55 -2
  2. package/dist/_virtual/{_@oxc-project_runtime@0.127.0/helpers → _@oxc-project_runtime@0.135.0/helpers/esm}/decorate.mjs +1 -1
  3. package/dist/adapters/base-adapter.d.mts.map +1 -1
  4. package/dist/adapters/base-adapter.mjs.map +1 -1
  5. package/dist/adapters/build-managed-account.mjs.map +1 -1
  6. package/dist/adapters/devstack-adapter.d.mts.map +1 -1
  7. package/dist/adapters/devstack-adapter.mjs.map +1 -1
  8. package/dist/adapters/devstack-paths.d.mts.map +1 -1
  9. package/dist/adapters/devstack-paths.mjs.map +1 -1
  10. package/dist/adapters/fork-relay.d.mts.map +1 -1
  11. package/dist/adapters/fork-relay.mjs.map +1 -1
  12. package/dist/adapters/idb-store.mjs.map +1 -1
  13. package/dist/adapters/in-memory-adapter.d.mts.map +1 -1
  14. package/dist/adapters/in-memory-adapter.mjs.map +1 -1
  15. package/dist/adapters/passkey-adapter.d.mts.map +1 -1
  16. package/dist/adapters/passkey-adapter.mjs.map +1 -1
  17. package/dist/adapters/remote-cli-adapter.d.mts.map +1 -1
  18. package/dist/adapters/remote-cli-adapter.mjs.map +1 -1
  19. package/dist/adapters/webcrypto-adapter.d.mts.map +1 -1
  20. package/dist/adapters/webcrypto-adapter.mjs.map +1 -1
  21. package/dist/bin/cli.mjs.map +1 -1
  22. package/dist/client/dev-wallet-client.d.mts.map +1 -1
  23. package/dist/client/dev-wallet-client.mjs.map +1 -1
  24. package/dist/client/request-handler.d.mts.map +1 -1
  25. package/dist/client/request-handler.mjs.map +1 -1
  26. package/dist/index.d.mts +2 -1
  27. package/dist/index.mjs +2 -1
  28. package/dist/inject/index.d.mts +61 -0
  29. package/dist/inject/index.d.mts.map +1 -0
  30. package/dist/inject/index.mjs +61 -0
  31. package/dist/inject/index.mjs.map +1 -0
  32. package/dist/react/components.d.mts +6 -8
  33. package/dist/react/components.d.mts.map +1 -1
  34. package/dist/react/components.mjs.map +1 -1
  35. package/dist/react/context.d.mts +2 -3
  36. package/dist/react/context.d.mts.map +1 -1
  37. package/dist/react/context.mjs.map +1 -1
  38. package/dist/react/useDevWallet.d.mts +3 -11
  39. package/dist/react/useDevWallet.d.mts.map +1 -1
  40. package/dist/react/useDevWallet.mjs +14 -36
  41. package/dist/react/useDevWallet.mjs.map +1 -1
  42. package/dist/server/cli-signing-middleware.d.mts.map +1 -1
  43. package/dist/server/cli-signing-middleware.mjs.map +1 -1
  44. package/dist/standalone/assets/base-adapter-DET6CP7z.js +1 -0
  45. package/dist/standalone/assets/in-memory-adapter-CX7xym4e.js +1 -0
  46. package/dist/standalone/assets/main-CuOnBmaa.js +7 -0
  47. package/dist/standalone/assets/webcrypto-adapter-CUQmbHDy.js +1 -0
  48. package/dist/standalone/bookmarklet.js +5 -5
  49. package/dist/standalone/index.html +2 -1
  50. package/dist/types.d.mts.map +1 -1
  51. package/dist/ui/copy-controller.mjs.map +1 -1
  52. package/dist/ui/dev-wallet-account-selector.d.mts +2 -3
  53. package/dist/ui/dev-wallet-account-selector.d.mts.map +1 -1
  54. package/dist/ui/dev-wallet-account-selector.mjs +1 -1
  55. package/dist/ui/dev-wallet-account-selector.mjs.map +1 -1
  56. package/dist/ui/dev-wallet-accounts.d.mts +2 -3
  57. package/dist/ui/dev-wallet-accounts.d.mts.map +1 -1
  58. package/dist/ui/dev-wallet-accounts.mjs +1 -1
  59. package/dist/ui/dev-wallet-accounts.mjs.map +1 -1
  60. package/dist/ui/dev-wallet-balances.d.mts +2 -3
  61. package/dist/ui/dev-wallet-balances.d.mts.map +1 -1
  62. package/dist/ui/dev-wallet-balances.mjs +1 -1
  63. package/dist/ui/dev-wallet-balances.mjs.map +1 -1
  64. package/dist/ui/dev-wallet-connect.d.mts +2 -3
  65. package/dist/ui/dev-wallet-connect.d.mts.map +1 -1
  66. package/dist/ui/dev-wallet-connect.mjs +1 -1
  67. package/dist/ui/dev-wallet-connect.mjs.map +1 -1
  68. package/dist/ui/dev-wallet-dropdown.d.mts +2 -3
  69. package/dist/ui/dev-wallet-dropdown.d.mts.map +1 -1
  70. package/dist/ui/dev-wallet-dropdown.mjs +1 -1
  71. package/dist/ui/dev-wallet-dropdown.mjs.map +1 -1
  72. package/dist/ui/dev-wallet-fork-panel.d.mts +2 -3
  73. package/dist/ui/dev-wallet-fork-panel.d.mts.map +1 -1
  74. package/dist/ui/dev-wallet-fork-panel.mjs +1 -1
  75. package/dist/ui/dev-wallet-fork-panel.mjs.map +1 -1
  76. package/dist/ui/dev-wallet-network-badge.d.mts +2 -3
  77. package/dist/ui/dev-wallet-network-badge.d.mts.map +1 -1
  78. package/dist/ui/dev-wallet-network-badge.mjs +1 -1
  79. package/dist/ui/dev-wallet-network-badge.mjs.map +1 -1
  80. package/dist/ui/dev-wallet-new-account.d.mts +2 -3
  81. package/dist/ui/dev-wallet-new-account.d.mts.map +1 -1
  82. package/dist/ui/dev-wallet-new-account.mjs +1 -1
  83. package/dist/ui/dev-wallet-new-account.mjs.map +1 -1
  84. package/dist/ui/dev-wallet-objects.d.mts +2 -3
  85. package/dist/ui/dev-wallet-objects.d.mts.map +1 -1
  86. package/dist/ui/dev-wallet-objects.mjs +1 -1
  87. package/dist/ui/dev-wallet-objects.mjs.map +1 -1
  88. package/dist/ui/dev-wallet-panel.d.mts +2 -3
  89. package/dist/ui/dev-wallet-panel.d.mts.map +1 -1
  90. package/dist/ui/dev-wallet-panel.mjs +1 -1
  91. package/dist/ui/dev-wallet-panel.mjs.map +1 -1
  92. package/dist/ui/dev-wallet-popup.d.mts +2 -3
  93. package/dist/ui/dev-wallet-popup.d.mts.map +1 -1
  94. package/dist/ui/dev-wallet-popup.mjs +1 -1
  95. package/dist/ui/dev-wallet-popup.mjs.map +1 -1
  96. package/dist/ui/dev-wallet-settings.d.mts +2 -3
  97. package/dist/ui/dev-wallet-settings.d.mts.map +1 -1
  98. package/dist/ui/dev-wallet-settings.mjs +1 -1
  99. package/dist/ui/dev-wallet-settings.mjs.map +1 -1
  100. package/dist/ui/dev-wallet-signing-modal.d.mts +2 -3
  101. package/dist/ui/dev-wallet-signing-modal.d.mts.map +1 -1
  102. package/dist/ui/dev-wallet-signing-modal.mjs +1 -1
  103. package/dist/ui/dev-wallet-signing-modal.mjs.map +1 -1
  104. package/dist/ui/dev-wallet-signing.d.mts +2 -3
  105. package/dist/ui/dev-wallet-signing.d.mts.map +1 -1
  106. package/dist/ui/dev-wallet-signing.mjs +2 -1
  107. package/dist/ui/dev-wallet-signing.mjs.map +1 -1
  108. package/dist/ui/dev-wallet-standalone.d.mts +2 -3
  109. package/dist/ui/dev-wallet-standalone.d.mts.map +1 -1
  110. package/dist/ui/dev-wallet-standalone.mjs +1 -1
  111. package/dist/ui/dev-wallet-standalone.mjs.map +1 -1
  112. package/dist/ui/dev-wallet-tab-bar.d.mts +1 -2
  113. package/dist/ui/dev-wallet-tab-bar.d.mts.map +1 -1
  114. package/dist/ui/dev-wallet-tab-bar.mjs +1 -1
  115. package/dist/ui/dev-wallet-tab-bar.mjs.map +1 -1
  116. package/dist/ui/mount.mjs.map +1 -1
  117. package/dist/ui/styles.mjs.map +1 -1
  118. package/dist/ui/transaction-analyzer.mjs.map +1 -1
  119. package/dist/ui/utils.d.mts.map +1 -1
  120. package/dist/ui/utils.mjs.map +1 -1
  121. package/dist/ui/wallet-controller.mjs.map +1 -1
  122. package/dist/wallet/constants.d.mts.map +1 -1
  123. package/dist/wallet/constants.mjs.map +1 -1
  124. package/dist/wallet/dev-wallet.d.mts +19 -0
  125. package/dist/wallet/dev-wallet.d.mts.map +1 -1
  126. package/dist/wallet/dev-wallet.mjs +58 -2
  127. package/dist/wallet/dev-wallet.mjs.map +1 -1
  128. package/dist/wallet/initializer.d.mts.map +1 -1
  129. package/dist/wallet/initializer.mjs +10 -26
  130. package/dist/wallet/initializer.mjs.map +1 -1
  131. package/dist/wallet/mount-and-register.d.mts +44 -0
  132. package/dist/wallet/mount-and-register.d.mts.map +1 -0
  133. package/dist/wallet/mount-and-register.mjs +46 -0
  134. package/dist/wallet/mount-and-register.mjs.map +1 -0
  135. package/dist/wallet/signing.d.mts.map +1 -1
  136. package/dist/wallet/signing.mjs.map +1 -1
  137. package/package.json +26 -22
  138. package/src/index.ts +5 -0
  139. package/src/inject/index.ts +183 -0
  140. package/src/react/useDevWallet.ts +26 -71
  141. package/src/ui/dev-wallet-signing.ts +2 -0
  142. package/src/wallet/dev-wallet.ts +75 -2
  143. package/src/wallet/initializer.ts +16 -42
  144. package/src/wallet/mount-and-register.ts +93 -0
  145. package/dist/standalone/assets/in-memory-adapter-qRkNfj9O.js +0 -1
  146. package/dist/standalone/assets/main-DDltQnzR.js +0 -3954
  147. package/dist/standalone/assets/webcrypto-adapter-FejcrL9x.js +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,59 @@
1
1
  # @mysten-incubation/dev-wallet
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 9e1e1be: Dev wallet: explicit test-only connect, no pre-connect or storage seeding.
8
+
9
+ The injected dev wallet no longer seeds dApp Kit's localStorage to fake an auto-connect to a
10
+ specific account on page load. A fresh page now loads disconnected, and dApp Kit's own
11
+ `autoConnect` does only what it's meant to — re-connect a genuine prior session.
12
+
13
+ A new devstack `/dapp-kit` entrypoint exports `registerDAppKitForTesting(dAppKit)`, which the app
14
+ wires DEV-only after `createDAppKit(...)`. It publishes the `connectAs` slot that drives a REAL
15
+ connection through dApp Kit's public API (`connectWallet` / `switchAccount`, resolving accounts by
16
+ label) instead of narrowing/widening the wallet's exposed accounts to exploit reconciliation. The
17
+ dev wallet auto-approves `standard:connect` only when signing is auto-approved (the headless-e2e
18
+ `DEVSTACK_AUTO_APPROVE` signal); in normal dev a human approves the connect. This fixes wallet
19
+ connection under `@mysten/dapp-kit-core` ≥1.6, whose rewritten auto-connect state machine broke
20
+ the old storage-seeding approach.
21
+
22
+ - 11c258a: Auto-inject the devstack dev wallet via the Vite plugin.
23
+
24
+ `@mysten-incubation/dev-wallet` adds a `/inject` entry (`registerDevstackDevWallet`) that
25
+ constructs the dev wallet from a devstack stack's config and registers it on the page via the
26
+ wallet-standard window protocol (plus the Playwright `connectAs` slot). The devstack Vite plugin
27
+ uses it to inject + register the dev wallet in DEV only, so dapp-kit apps discover it through
28
+ wallet-standard with no app-side wiring — apps no longer need a `dapp-kit.dev.ts` or any
29
+ `@devstack-dev` import, and production builds carry no dev-wallet code. The dev wallet exposes all
30
+ of its accounts to the dApp while `connectAs` still drives the active account.
31
+
32
+ - 9e1e1be: The injected devstack dev wallet now bundles a `WebCryptoSignerAdapter` alongside the
33
+ stack's server-resolved accounts, so users can create their own accounts from the wallet UI.
34
+ Created accounts persist across reloads in IndexedDB via non-extractable WebCrypto keys (not
35
+ in-memory), and the stack's `alice`/`bob`/`carol` accounts remain available.
36
+ - 9e1e1be: Disambiguate the conflated `chain` concept into three precise ones: `network` (the
37
+ network name — `localnet`/`testnet`/…), `chainId` (the genesis-digest chain identifier, unique per
38
+ spun-up network), and the wallet-standard `sui:<network>` chain name (derived only at the
39
+ dev-wallet wallet-standard boundary — `sui:` never appears in devstack internals).
40
+
41
+ **Breaking.** The substrate `Identity`/manifest field `chain` is now `network` and holds the bare
42
+ network name (previously a `sui:`-prefixed string). The network parser accepts only canonical
43
+ names — the `local` shorthand and the `sui:`-prefixed alias table are removed (use `localnet`);
44
+ `network ⇄ chain id` is now just a `sui:` prefix, not a lookup table. The sui plugin's resolved
45
+ value, on-disk cache-dir keys, and `chain-probe:`/`faucet:request:` capability keys now key on the
46
+ genesis-digest `chainId`. The generated `config.ts` active-network key is `localnet` (was
47
+ `local`), and `config.networks.<net>` / `byNetwork.<net>` are keyed by network name. The
48
+ dev-wallet `registerDevstackDevWallet` config and `DevWalletConfig` take `network` instead of
49
+ `chain`. The dashboard GraphQL surfaces `chainId` (sui) and `network` (deepbook) instead of
50
+ `chain`. Known walrus/deepbook deployments and the deepbook DEEP-funding gate now key on the
51
+ network name — fixing a latent bug where the gate compared a genesis digest against the
52
+ `'sui:testnet'` literal and was dead for every non-literal value.
53
+
54
+ On-disk state keyed by the old `sui:local` chain brand is invalidated; run `devstack wipe` on
55
+ existing local stacks after upgrading.
56
+
3
57
  ## 0.3.0
4
58
 
5
59
  ### Minor Changes
@@ -81,8 +135,7 @@
81
135
  - **Postgres password format changed.** `derivePassword(app, stack, stackRoot)` now incorporates
82
136
  the stack's on-disk runtime root and an sha256 short hash. Existing dev databases created
83
137
  against the previous `pg-${app+stack}` format will fail to authenticate on first `pg_isready`
84
- probe. Either delete the existing container (`docker rm -f`) and let devstack recreate it, OR
85
- set `password` explicitly on `PostgresServiceOptions` for stable credentials across the upgrade.
138
+ probe. Delete the existing container (`docker rm -f`) and let devstack recreate it.
86
139
  Multi-checkout shells of the same `(app, stack)` now derive distinct passwords by design.
87
140
  - **CLI argv-parse failures now exit with code 64 (`USAGE`), not 1 (`GENERIC`).** Tests / CI
88
141
  scripts that pattern-match exit codes for "user error vs internal error" should treat 64 the
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.127.0/helpers/decorate.js
1
+ //#region \0@oxc-project+runtime@0.135.0/helpers/esm/decorate.js
2
2
  function __decorate(decorators, target, key, desc) {
3
3
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
4
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1 +1 @@
1
- {"version":3,"file":"base-adapter.d.mts","names":[],"sources":["../../src/adapters/base-adapter.ts"],"mappings":";;;;;AAYA;;;;uBAAsB,iBAAA,YAA6B,aAAA;EAAA;oBAChC,EAAA;EAAA,kBACA,IAAA;EAAA,SACT,aAAA;EAAA,SAKA,UAAA,CAAA,GAAc,OAAA;EAEvB,WAAA,CAAA,GAAe,cAAA;EAIf,UAAA,CAAW,OAAA,WAAkB,cAAA;EAI7B,iBAAA,CAAkB,QAAA,GAAW,QAAA,EAAU,cAAA;EAOvC,OAAA,CAAA;EAAA,cAOc,SAAA,CAAA,YAAsB,cAAA;EAAA,UAI1B,kBAAA,CAAmB,QAAA,EAAU,cAAA;EAAA,UAK7B,UAAA,CAAW,OAAA,EAAS,cAAA;EAAA,UAKpB,sBAAA,CAAuB,OAAA;EA3CxB;EAAA,UAoDC,cAAA,CAAe,OAAA,UAAiB,OAAA,EAAS,cAAA;EA/C5B;EAAA,UAwDb,cAAA,CAAe,OAAA,UAAiB,KAAA;EAAA,UAMhC,eAAA,CAAA;EAAA,UAIA,eAAA,CAAA;AAAA"}
1
+ {"version":3,"file":"base-adapter.d.mts","names":[],"sources":["../../src/adapters/base-adapter.ts"],"mappings":";;;;;AAYA;;;;uBAAsB,iBAAA,YAA6B,aAAA;EAAA;oBAChC,EAAA;EAAA,kBACA,IAAA;EAAA,SACT,aAAA;EAAA,SAKA,UAAA,IAAc,OAAA;EAEvB,WAAA,IAAe,cAAA;EAIf,UAAA,CAAW,OAAA,WAAkB,cAAA;EAI7B,iBAAA,CAAkB,QAAA,GAAW,QAAA,EAAU,cAAA;EAOvC,OAAA;EAAA,cAOc,SAAA,aAAsB,cAAA;EAAA,UAI1B,kBAAA,CAAmB,QAAA,EAAU,cAAA;EAAA,UAK7B,UAAA,CAAW,OAAA,EAAS,cAAA;EAAA,UAKpB,sBAAA,CAAuB,OAAA;EA3CxB;EAAA,UAoDC,cAAA,CAAe,OAAA,UAAiB,OAAA,EAAS,cAAA;EA/C5B;EAAA,UAwDb,cAAA,CAAe,OAAA,UAAiB,KAAA;EAAA,UAMhC,eAAA;EAAA,UAIA,eAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"base-adapter.mjs","names":["#accounts","#listeners"],"sources":["../../src/adapters/base-adapter.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { ManagedAccount, SignerAdapter } from '../types.js';\nimport { buildManagedAccount } from './build-managed-account.js';\n\n/**\n * Abstract base class implementing the shared boilerplate of {@link SignerAdapter}.\n *\n * Subclasses only need to implement `initialize()`, `id`, and `name`.\n * All account list management, listener notification, and cleanup are handled here.\n */\nexport abstract class BaseSignerAdapter implements SignerAdapter {\n\tabstract readonly id: string;\n\tabstract readonly name: string;\n\treadonly allowAutoSign: boolean = true;\n\n\t#accounts: ManagedAccount[] = [];\n\t#listeners = new Set<(accounts: ManagedAccount[]) => void>();\n\n\tabstract initialize(): Promise<void>;\n\n\tgetAccounts(): ManagedAccount[] {\n\t\treturn [...this.#accounts];\n\t}\n\n\tgetAccount(address: string): ManagedAccount | undefined {\n\t\treturn this.#accounts.find((account) => account.address === address);\n\t}\n\n\tonAccountsChanged(callback: (accounts: ManagedAccount[]) => void): () => void {\n\t\tthis.#listeners.add(callback);\n\t\treturn () => {\n\t\t\tthis.#listeners.delete(callback);\n\t\t};\n\t}\n\n\tdestroy(): void {\n\t\tthis.#accounts = [];\n\t\tthis.#listeners.clear();\n\t}\n\n\t// ── Protected helpers for subclasses ─────────────────────────────────────\n\n\tprotected get _accounts(): readonly ManagedAccount[] {\n\t\treturn this.#accounts;\n\t}\n\n\tprotected setInitialAccounts(accounts: ManagedAccount[]): void {\n\t\tthis.#accounts = accounts;\n\t\tthis.notifyListeners();\n\t}\n\n\tprotected addAccount(account: ManagedAccount): void {\n\t\tthis.#accounts.push(account);\n\t\tthis.notifyListeners();\n\t}\n\n\tprotected removeAccountByAddress(address: string): boolean {\n\t\tconst index = this.#accounts.findIndex((a) => a.address === address);\n\t\tif (index === -1) return false;\n\t\tthis.#accounts.splice(index, 1);\n\t\tthis.notifyListeners();\n\t\treturn true;\n\t}\n\n\t/** Replace an account by address (e.g. after rename). */\n\tprotected replaceAccount(address: string, account: ManagedAccount): boolean {\n\t\tconst index = this.#accounts.findIndex((a) => a.address === address);\n\t\tif (index === -1) return false;\n\t\tthis.#accounts[index] = account;\n\t\tthis.notifyListeners();\n\t\treturn true;\n\t}\n\n\t/** Rename an account by rebuilding its ManagedAccount with a new label. */\n\tprotected _performRename(address: string, label: string): boolean {\n\t\tconst account = this.getAccount(address);\n\t\tif (!account) return false;\n\t\treturn this.replaceAccount(address, buildManagedAccount(account.signer, address, label));\n\t}\n\n\tprotected getDefaultLabel(): string {\n\t\treturn `Account ${this.getAccounts().length + 1}`;\n\t}\n\n\tprotected notifyListeners(): void {\n\t\tconst accounts = this.getAccounts();\n\t\tfor (const listener of this.#listeners) {\n\t\t\tlistener(accounts);\n\t\t}\n\t}\n}\n"],"mappings":";;;;;;;;AAYA,IAAsB,oBAAtB,MAAiE;;uBAG9B;mBAEJ,EAAE;oCACnB,IAAI,KAA2C;;CAD5D;CACA;CAIA,cAAgC;AAC/B,SAAO,CAAC,GAAG,MAAA,SAAe;;CAG3B,WAAW,SAA6C;AACvD,SAAO,MAAA,SAAe,MAAM,YAAY,QAAQ,YAAY,QAAQ;;CAGrE,kBAAkB,UAA4D;AAC7E,QAAA,UAAgB,IAAI,SAAS;AAC7B,eAAa;AACZ,SAAA,UAAgB,OAAO,SAAS;;;CAIlC,UAAgB;AACf,QAAA,WAAiB,EAAE;AACnB,QAAA,UAAgB,OAAO;;CAKxB,IAAc,YAAuC;AACpD,SAAO,MAAA;;CAGR,mBAA6B,UAAkC;AAC9D,QAAA,WAAiB;AACjB,OAAK,iBAAiB;;CAGvB,WAAqB,SAA+B;AACnD,QAAA,SAAe,KAAK,QAAQ;AAC5B,OAAK,iBAAiB;;CAGvB,uBAAiC,SAA0B;EAC1D,MAAM,QAAQ,MAAA,SAAe,WAAW,MAAM,EAAE,YAAY,QAAQ;AACpE,MAAI,UAAU,GAAI,QAAO;AACzB,QAAA,SAAe,OAAO,OAAO,EAAE;AAC/B,OAAK,iBAAiB;AACtB,SAAO;;;CAIR,eAAyB,SAAiB,SAAkC;EAC3E,MAAM,QAAQ,MAAA,SAAe,WAAW,MAAM,EAAE,YAAY,QAAQ;AACpE,MAAI,UAAU,GAAI,QAAO;AACzB,QAAA,SAAe,SAAS;AACxB,OAAK,iBAAiB;AACtB,SAAO;;;CAIR,eAAyB,SAAiB,OAAwB;EACjE,MAAM,UAAU,KAAK,WAAW,QAAQ;AACxC,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,KAAK,eAAe,SAAS,oBAAoB,QAAQ,QAAQ,SAAS,MAAM,CAAC;;CAGzF,kBAAoC;AACnC,SAAO,WAAW,KAAK,aAAa,CAAC,SAAS;;CAG/C,kBAAkC;EACjC,MAAM,WAAW,KAAK,aAAa;AACnC,OAAK,MAAM,YAAY,MAAA,UACtB,UAAS,SAAS"}
1
+ {"version":3,"file":"base-adapter.mjs","names":["#accounts","#listeners"],"sources":["../../src/adapters/base-adapter.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { ManagedAccount, SignerAdapter } from '../types.js';\nimport { buildManagedAccount } from './build-managed-account.js';\n\n/**\n * Abstract base class implementing the shared boilerplate of {@link SignerAdapter}.\n *\n * Subclasses only need to implement `initialize()`, `id`, and `name`.\n * All account list management, listener notification, and cleanup are handled here.\n */\nexport abstract class BaseSignerAdapter implements SignerAdapter {\n\tabstract readonly id: string;\n\tabstract readonly name: string;\n\treadonly allowAutoSign: boolean = true;\n\n\t#accounts: ManagedAccount[] = [];\n\t#listeners = new Set<(accounts: ManagedAccount[]) => void>();\n\n\tabstract initialize(): Promise<void>;\n\n\tgetAccounts(): ManagedAccount[] {\n\t\treturn [...this.#accounts];\n\t}\n\n\tgetAccount(address: string): ManagedAccount | undefined {\n\t\treturn this.#accounts.find((account) => account.address === address);\n\t}\n\n\tonAccountsChanged(callback: (accounts: ManagedAccount[]) => void): () => void {\n\t\tthis.#listeners.add(callback);\n\t\treturn () => {\n\t\t\tthis.#listeners.delete(callback);\n\t\t};\n\t}\n\n\tdestroy(): void {\n\t\tthis.#accounts = [];\n\t\tthis.#listeners.clear();\n\t}\n\n\t// ── Protected helpers for subclasses ─────────────────────────────────────\n\n\tprotected get _accounts(): readonly ManagedAccount[] {\n\t\treturn this.#accounts;\n\t}\n\n\tprotected setInitialAccounts(accounts: ManagedAccount[]): void {\n\t\tthis.#accounts = accounts;\n\t\tthis.notifyListeners();\n\t}\n\n\tprotected addAccount(account: ManagedAccount): void {\n\t\tthis.#accounts.push(account);\n\t\tthis.notifyListeners();\n\t}\n\n\tprotected removeAccountByAddress(address: string): boolean {\n\t\tconst index = this.#accounts.findIndex((a) => a.address === address);\n\t\tif (index === -1) return false;\n\t\tthis.#accounts.splice(index, 1);\n\t\tthis.notifyListeners();\n\t\treturn true;\n\t}\n\n\t/** Replace an account by address (e.g. after rename). */\n\tprotected replaceAccount(address: string, account: ManagedAccount): boolean {\n\t\tconst index = this.#accounts.findIndex((a) => a.address === address);\n\t\tif (index === -1) return false;\n\t\tthis.#accounts[index] = account;\n\t\tthis.notifyListeners();\n\t\treturn true;\n\t}\n\n\t/** Rename an account by rebuilding its ManagedAccount with a new label. */\n\tprotected _performRename(address: string, label: string): boolean {\n\t\tconst account = this.getAccount(address);\n\t\tif (!account) return false;\n\t\treturn this.replaceAccount(address, buildManagedAccount(account.signer, address, label));\n\t}\n\n\tprotected getDefaultLabel(): string {\n\t\treturn `Account ${this.getAccounts().length + 1}`;\n\t}\n\n\tprotected notifyListeners(): void {\n\t\tconst accounts = this.getAccounts();\n\t\tfor (const listener of this.#listeners) {\n\t\t\tlistener(accounts);\n\t\t}\n\t}\n}\n"],"mappings":";;;;;;;;AAYA,IAAsB,oBAAtB,MAAiE;;uBAG9B;mBAEJ,CAAC;oCAClB,IAAI,IAA0C;;CAD3D;CACA;CAIA,cAAgC;EAC/B,OAAO,CAAC,GAAG,KAAKA,SAAS;CAC1B;CAEA,WAAW,SAA6C;EACvD,OAAO,KAAKA,UAAU,MAAM,YAAY,QAAQ,YAAY,OAAO;CACpE;CAEA,kBAAkB,UAA4D;EAC7E,KAAKC,WAAW,IAAI,QAAQ;EAC5B,aAAa;GACZ,KAAKA,WAAW,OAAO,QAAQ;EAChC;CACD;CAEA,UAAgB;EACf,KAAKD,YAAY,CAAC;EAClB,KAAKC,WAAW,MAAM;CACvB;CAIA,IAAc,YAAuC;EACpD,OAAO,KAAKD;CACb;CAEA,mBAA6B,UAAkC;EAC9D,KAAKA,YAAY;EACjB,KAAK,gBAAgB;CACtB;CAEA,WAAqB,SAA+B;EACnD,KAAKA,UAAU,KAAK,OAAO;EAC3B,KAAK,gBAAgB;CACtB;CAEA,uBAAiC,SAA0B;EAC1D,MAAM,QAAQ,KAAKA,UAAU,WAAW,MAAM,EAAE,YAAY,OAAO;EACnE,IAAI,UAAU,IAAI,OAAO;EACzB,KAAKA,UAAU,OAAO,OAAO,CAAC;EAC9B,KAAK,gBAAgB;EACrB,OAAO;CACR;;CAGA,eAAyB,SAAiB,SAAkC;EAC3E,MAAM,QAAQ,KAAKA,UAAU,WAAW,MAAM,EAAE,YAAY,OAAO;EACnE,IAAI,UAAU,IAAI,OAAO;EACzB,KAAKA,UAAU,SAAS;EACxB,KAAK,gBAAgB;EACrB,OAAO;CACR;;CAGA,eAAyB,SAAiB,OAAwB;EACjE,MAAM,UAAU,KAAK,WAAW,OAAO;EACvC,IAAI,CAAC,SAAS,OAAO;EACrB,OAAO,KAAK,eAAe,SAAS,oBAAoB,QAAQ,QAAQ,SAAS,KAAK,CAAC;CACxF;CAEA,kBAAoC;EACnC,OAAO,WAAW,KAAK,YAAY,CAAC,CAAC,SAAS;CAC/C;CAEA,kBAAkC;EACjC,MAAM,WAAW,KAAK,YAAY;EAClC,KAAK,MAAM,YAAY,KAAKC,YAC3B,SAAS,QAAQ;CAEnB;AACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"build-managed-account.mjs","names":[],"sources":["../../src/adapters/build-managed-account.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Signer } from '@mysten/sui/cryptography';\nimport type { IdentifierString } from '@mysten/wallet-standard';\nimport { ReadonlyWalletAccount, SUI_CHAINS } from '@mysten/wallet-standard';\n\nimport type { ManagedAccount } from '../types.js';\n\nconst DEFAULT_FEATURES: IdentifierString[] = [\n\t'sui:signTransaction',\n\t'sui:signAndExecuteTransaction',\n\t'sui:signPersonalMessage',\n];\n\n/**\n * Build a {@link ManagedAccount} from a signer, address, label, and optional feature list.\n */\nexport function buildManagedAccount(\n\tsigner: Signer,\n\taddress: string,\n\tlabel: string,\n\tfeatures?: readonly IdentifierString[],\n): ManagedAccount {\n\tconst walletAccount = new ReadonlyWalletAccount({\n\t\taddress,\n\t\tlabel,\n\t\tpublicKey: signer.getPublicKey().toSuiBytes(),\n\t\tchains: [...SUI_CHAINS],\n\t\tfeatures: [...(features ?? DEFAULT_FEATURES)],\n\t});\n\treturn { address, label, signer, walletAccount };\n}\n"],"mappings":";;AASA,MAAM,mBAAuC;CAC5C;CACA;CACA;CACA;;;;AAKD,SAAgB,oBACf,QACA,SACA,OACA,UACiB;AAQjB,QAAO;EAAE;EAAS;EAAO;EAAQ,mBAPP,sBAAsB;GAC/C;GACA;GACA,WAAW,OAAO,cAAc,CAAC,YAAY;GAC7C,QAAQ,CAAC,GAAG,WAAW;GACvB,UAAU,CAAC,GAAI,YAAY,iBAAkB;GAC7C,CACgC;EAAe"}
1
+ {"version":3,"file":"build-managed-account.mjs","names":[],"sources":["../../src/adapters/build-managed-account.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Signer } from '@mysten/sui/cryptography';\nimport type { IdentifierString } from '@mysten/wallet-standard';\nimport { ReadonlyWalletAccount, SUI_CHAINS } from '@mysten/wallet-standard';\n\nimport type { ManagedAccount } from '../types.js';\n\nconst DEFAULT_FEATURES: IdentifierString[] = [\n\t'sui:signTransaction',\n\t'sui:signAndExecuteTransaction',\n\t'sui:signPersonalMessage',\n];\n\n/**\n * Build a {@link ManagedAccount} from a signer, address, label, and optional feature list.\n */\nexport function buildManagedAccount(\n\tsigner: Signer,\n\taddress: string,\n\tlabel: string,\n\tfeatures?: readonly IdentifierString[],\n): ManagedAccount {\n\tconst walletAccount = new ReadonlyWalletAccount({\n\t\taddress,\n\t\tlabel,\n\t\tpublicKey: signer.getPublicKey().toSuiBytes(),\n\t\tchains: [...SUI_CHAINS],\n\t\tfeatures: [...(features ?? DEFAULT_FEATURES)],\n\t});\n\treturn { address, label, signer, walletAccount };\n}\n"],"mappings":";;AASA,MAAM,mBAAuC;CAC5C;CACA;CACA;AACD;;;;AAKA,SAAgB,oBACf,QACA,SACA,OACA,UACiB;CAQjB,OAAO;EAAE;EAAS;EAAO;EAAQ,mBAPP,sBAAsB;GAC/C;GACA;GACA,WAAW,OAAO,aAAa,CAAC,CAAC,WAAW;GAC5C,QAAQ,CAAC,GAAG,UAAU;GACtB,UAAU,CAAC,GAAI,YAAY,gBAAiB;EAC7C,CACiC;CAAc;AAChD"}
@@ -1 +1 @@
1
- {"version":3,"file":"devstack-adapter.d.mts","names":[],"sources":["../../src/adapters/devstack-adapter.ts"],"mappings":";;;;;AAiEA;;;;cAAa,mBAAA,SAA4B,MAAA;EAAA;cAO5B,OAAA;IACX,OAAA;IACA,SAAA,EAAW,SAAA;IACX,MAAA,EAAQ,eAAA;IACR,YAAA;IACA,SAAA;EAAA;EAUK,IAAA,CAAK,MAAA,EAAQ,UAAA,GAAa,OAAA,CAAQ,UAAA,CAAW,WAAA;EAOnD,YAAA,CAAA,GAAgB,eAAA;EAIhB,YAAA,CAAA,GAAgB,SAAA;EAIP,YAAA,CAAA;EAIM,eAAA,CAAgB,KAAA,EAAO,UAAA,GAAU,OAAA;;;;EAqCjC,mBAAA,CAAoB,KAAA,EAAO,UAAA,GAAU,OAAA;;;;;UAyCpC,4BAAA;EA3Gf;;EA8GD,YAAA;EApGmB;;EAuGnB,KAAA;EAvGwC;EAyGxC,IAAA;AAAA;;;;;;;;;;;;;;;;;;;AARD;;cA+Ba,qBAAA,SAA8B,iBAAA;EAAA;WACjC,EAAA;EAAA,SACA,IAAA;EAAA,SACA,aAAA;cAKG,OAAA,EAAS,4BAAA;EAYf,UAAA,CAAA,GAAc,OAAA;EAaX,OAAA,CAAA;AAAA;;;;;;;;;;iBAyCM,kBAAA,CAAmB,SAAA;;;;;;;;UAsBlB,uBAAA;EAChB,GAAA;IACC,MAAA;MACC,GAAA;MACA,OAAA;IAAA;EAAA;AAAA;;;;;;;;;;AAiBH;;;iBAAgB,iCAAA,CACf,QAAA,EAAU,uBAAA,GACR,qBAAA"}
1
+ {"version":3,"file":"devstack-adapter.d.mts","names":[],"sources":["../../src/adapters/devstack-adapter.ts"],"mappings":";;;;;AAiEA;;;;cAAa,mBAAA,SAA4B,MAAA;EAAA;cAO5B,OAAA;IACX,OAAA;IACA,SAAA,EAAW,SAAA;IACX,MAAA,EAAQ,eAAA;IACR,YAAA;IACA,SAAA;EAAA;EAUK,IAAA,CAAK,MAAA,EAAQ,UAAA,GAAa,OAAA,CAAQ,UAAA,CAAW,WAAA;EAOnD,YAAA,IAAgB,eAAA;EAIhB,YAAA,IAAgB,SAAA;EAIP,YAAA;EAIM,eAAA,CAAgB,KAAA,EAAO,UAAA,GAAU,OAAA;;;;EAqCjC,mBAAA,CAAoB,KAAA,EAAO,UAAA,GAAU,OAAA;;;;;UAyCpC,4BAAA;EA3Gf;;EA8GD,YAAA;EApGmB;;EAuGnB,KAAA;EAvGwC;EAyGxC,IAAA;AAAA;;;;;;;;;;;;;;;;;;;AARD;;cA+Ba,qBAAA,SAA8B,iBAAA;EAAA;WACjC,EAAA;EAAA,SACA,IAAA;EAAA,SACA,aAAA;cAKG,OAAA,EAAS,4BAAA;EAYf,UAAA,IAAc,OAAA;EAaX,OAAA;AAAA;;;;;;;;;;iBAyCM,kBAAA,CAAmB,SAA6B;;;;;;;;UAsB/C,uBAAA;EAChB,GAAA;IACC,MAAA;MACC,GAAA;MACA,OAAA;IAAA;EAAA;AAAA;;;;;;;;;AAAO;AAiBV;;;iBAAgB,iCAAA,CACf,QAAA,EAAU,uBAAA,GACR,qBAAqB"}
@@ -1 +1 @@
1
- {"version":3,"file":"devstack-adapter.mjs","names":["#address","#publicKey","#scheme","#serverOrigin","#authToken","#fetchAccounts"],"sources":["../../src/adapters/devstack-adapter.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n//\n// Devstack signer adapter — exposes accounts resolved by `devstack up`'s\n// `wallet()` plugin to dApp Kit, signing transactions over HTTP so\n// private keys never enter the frontend bundle. Mirrors RemoteCliAdapter's\n// out-of-process model; the difference is the source of accounts (devstack\n// resolved signers vs `sui keytool list`) and the endpoint paths.\n\nimport { Signer } from '@mysten/sui/cryptography';\nimport type { PublicKey, SignatureScheme } from '@mysten/sui/cryptography';\nimport { Ed25519PublicKey } from '@mysten/sui/keypairs/ed25519';\nimport { Secp256k1PublicKey } from '@mysten/sui/keypairs/secp256k1';\nimport { Secp256r1PublicKey } from '@mysten/sui/keypairs/secp256r1';\nimport { fromBase64, toBase64 } from '@mysten/sui/utils';\n\nimport type { ManagedAccount } from '../types.js';\nimport { BaseSignerAdapter } from './base-adapter.js';\nimport { buildManagedAccount } from './build-managed-account.js';\nimport { DEVSTACK_WALLET_HTTP_PATH } from './devstack-paths.js';\n\nconst DEVSTACK_WALLET_FEATURES = [\n\t'sui:signTransaction',\n\t'sui:signAndExecuteTransaction',\n\t'sui:signPersonalMessage',\n] as const;\n\ninterface ServerAccountInfo {\n\tname: string;\n\taddress: string;\n\tscheme: SignatureScheme;\n\tpublicKey: string;\n}\n\nclass AuthError extends Error {\n\tconstructor() {\n\t\tsuper('Unauthorized');\n\t\tthis.name = 'AuthError';\n\t}\n}\n\nfunction publicKeyForScheme(base64: string, scheme: SignatureScheme | string): PublicKey {\n\tconst bytes = fromBase64(base64);\n\t// The devstack server's wallet-app endpoint surfaces the scheme in\n\t// the lowercase form it standardised at the Account boundary\n\t// (`'ed25519'`, `'secp256k1'`, `'secp256r1'`). The Sui SDK's\n\t// `SignatureScheme` union is mixed-case (`'ED25519'`, `'Secp256k1'`,\n\t// …). Normalise to lowercase before matching so either casing parses.\n\tswitch (String(scheme).toLowerCase()) {\n\t\tcase 'ed25519':\n\t\t\treturn new Ed25519PublicKey(bytes);\n\t\tcase 'secp256k1':\n\t\t\treturn new Secp256k1PublicKey(bytes);\n\t\tcase 'secp256r1':\n\t\t\treturn new Secp256r1PublicKey(bytes);\n\t\tdefault:\n\t\t\tthrow new Error(`Unsupported key scheme: ${scheme}`);\n\t}\n}\n\n/**\n * Signer that delegates transaction signing to the devstack wallet-app\n * over HTTP. Mirrors {@link CliProxySigner} but talks to the\n * `wallet()` plugin's endpoints under `/api/v1/devstack/*`.\n */\nexport class DevstackProxySigner extends Signer {\n\t#address: string;\n\t#publicKey: PublicKey;\n\t#scheme: SignatureScheme;\n\t#serverOrigin: string;\n\t#authToken: string | null;\n\n\tconstructor(options: {\n\t\taddress: string;\n\t\tpublicKey: PublicKey;\n\t\tscheme: SignatureScheme;\n\t\tserverOrigin: string;\n\t\tauthToken?: string | null;\n\t}) {\n\t\tsuper();\n\t\tthis.#address = options.address;\n\t\tthis.#publicKey = options.publicKey;\n\t\tthis.#scheme = options.scheme;\n\t\tthis.#serverOrigin = options.serverOrigin;\n\t\tthis.#authToken = options.authToken ?? null;\n\t}\n\n\tasync sign(_bytes: Uint8Array): Promise<Uint8Array<ArrayBuffer>> {\n\t\tthrow new Error(\n\t\t\t'DevstackProxySigner does not support raw digest signing. ' +\n\t\t\t\t'Use signTransaction (the wallet-app signs BCS-serialized TransactionData).',\n\t\t);\n\t}\n\n\tgetKeyScheme(): SignatureScheme {\n\t\treturn this.#scheme;\n\t}\n\n\tgetPublicKey(): PublicKey {\n\t\treturn this.#publicKey;\n\t}\n\n\toverride toSuiAddress(): string {\n\t\treturn this.#address;\n\t}\n\n\toverride async signTransaction(bytes: Uint8Array) {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\t\tif (this.#authToken !== null) {\n\t\t\theaders['Authorization'] = `Bearer ${this.#authToken}`;\n\t\t}\n\t\tconst requestBytes = toBase64(bytes);\n\t\tconst res = await fetch(`${this.#serverOrigin}${DEVSTACK_WALLET_HTTP_PATH.SIGN_TRANSACTION}`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders,\n\t\t\tbody: JSON.stringify({\n\t\t\t\taddress: this.#address,\n\t\t\t\tbytes: requestBytes,\n\t\t\t}),\n\t\t});\n\t\tif (!res.ok) {\n\t\t\tconst body = await res.json().catch(() => ({}));\n\t\t\tconst message = (body as { error?: unknown }).error;\n\t\t\tthrow new Error(\n\t\t\t\t`Devstack signing failed: ${typeof message === 'string' ? message : res.statusText}`,\n\t\t\t);\n\t\t}\n\t\tconst { bytes: responseBytes, signature } = (await res.json()) as {\n\t\t\tbytes?: string;\n\t\t\tsignature?: string;\n\t\t};\n\t\tif (typeof responseBytes !== 'string' || responseBytes.length === 0) {\n\t\t\tthrow new Error('Devstack signing failed: server returned invalid signed bytes');\n\t\t}\n\t\tif (typeof signature !== 'string' || signature.length === 0) {\n\t\t\tthrow new Error('Devstack signing failed: server returned invalid signature');\n\t\t}\n\t\treturn {\n\t\t\tbytes: responseBytes,\n\t\t\tsignature,\n\t\t};\n\t}\n\n\toverride async signPersonalMessage(bytes: Uint8Array) {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\t\tif (this.#authToken !== null) {\n\t\t\theaders['Authorization'] = `Bearer ${this.#authToken}`;\n\t\t}\n\t\tconst res = await fetch(\n\t\t\t`${this.#serverOrigin}${DEVSTACK_WALLET_HTTP_PATH.SIGN_PERSONAL_MESSAGE}`,\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders,\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\taddress: this.#address,\n\t\t\t\t\tbytes: toBase64(bytes),\n\t\t\t\t}),\n\t\t\t},\n\t\t);\n\t\tif (!res.ok) {\n\t\t\tconst body = await res.json().catch(() => ({}));\n\t\t\tconst message = (body as { error?: unknown }).error;\n\t\t\tthrow new Error(\n\t\t\t\t`Devstack personal-message signing failed: ${\n\t\t\t\t\ttypeof message === 'string' ? message : res.statusText\n\t\t\t\t}`,\n\t\t\t);\n\t\t}\n\t\tconst { bytes: responseBytes, signature } = (await res.json()) as {\n\t\t\tbytes?: string;\n\t\t\tsignature?: string;\n\t\t};\n\t\tif (typeof responseBytes !== 'string' || responseBytes.length === 0) {\n\t\t\tthrow new Error(\n\t\t\t\t'Devstack personal-message signing failed: server returned invalid signed bytes',\n\t\t\t);\n\t\t}\n\t\tif (typeof signature !== 'string' || signature.length === 0) {\n\t\t\tthrow new Error('Devstack personal-message signing failed: server returned no signature');\n\t\t}\n\t\treturn { bytes: responseBytes, signature };\n\t}\n}\n\nexport interface DevstackSignerAdapterOptions {\n\t/** Wallet-app origin (e.g. `http://localhost:9420`). Read from the\n\t * matching endpoint entry on the active manifest. */\n\tserverOrigin: string;\n\t/** Bearer token. Parse `?token=<hex>` from the manifest entry's\n\t * `pairUrl`. Omit if the wallet-app runs without auth. */\n\ttoken?: string | null;\n\t/** Override the adapter's display name. Defaults to `'Devstack'`. */\n\tname?: string;\n}\n\n/**\n * {@link SignerAdapter} that surfaces every account resolved by `devstack up`\n * (including `cliSigner`/`envSigner`/`generatedKeypair` slots) without\n * shipping their private keys into the frontend bundle. All signing happens\n * server-side via the `wallet()` plugin.\n *\n * The simplest construction uses devstack's generated `dapp-kit-config.ts`,\n * which bakes the wallet adapter wiring at codegen time — apps spread\n * `devstackDappKitConfig` into `createDAppKit(...)` and never touch the\n * adapter directly.\n *\n * To construct one manually (e.g. from a non-manifest source):\n *\n * ```ts\n * const adapter = new DevstackSignerAdapter({\n * serverOrigin: 'http://localhost:9420',\n * token: '<bearer hex>',\n * });\n * ```\n */\nexport class DevstackSignerAdapter extends BaseSignerAdapter {\n\treadonly id = 'devstack';\n\treadonly name: string;\n\treadonly allowAutoSign = true;\n\n\t#serverOrigin: string;\n\t#authToken: string | null;\n\n\tconstructor(options: DevstackSignerAdapterOptions) {\n\t\tsuper();\n\t\ttry {\n\t\t\tnew URL(options.serverOrigin);\n\t\t} catch {\n\t\t\tthrow new Error(`DevstackSignerAdapter: invalid serverOrigin \"${options.serverOrigin}\"`);\n\t\t}\n\t\tthis.#serverOrigin = options.serverOrigin;\n\t\tthis.#authToken = options.token ?? null;\n\t\tthis.name = options.name ?? 'Devstack';\n\t}\n\n\tasync initialize(): Promise<void> {\n\t\ttry {\n\t\t\tconst accounts = await this.#fetchAccounts();\n\t\t\tthis.setInitialAccounts(accounts);\n\t\t} catch (error) {\n\t\t\tif (error instanceof AuthError) {\n\t\t\t\tconsole.warn('[dev-wallet] devstack adapter: unauthorized — skipping account import');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconsole.warn('[dev-wallet] devstack adapter: initialization failed:', error);\n\t\t}\n\t}\n\n\toverride destroy(): void {\n\t\tthis.#authToken = null;\n\t\tsuper.destroy();\n\t}\n\n\tasync #fetchAccounts(): Promise<ManagedAccount[]> {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\t\tif (this.#authToken !== null) {\n\t\t\theaders['Authorization'] = `Bearer ${this.#authToken}`;\n\t\t}\n\t\tconst res = await fetch(`${this.#serverOrigin}${DEVSTACK_WALLET_HTTP_PATH.ACCOUNTS}`, {\n\t\t\theaders,\n\t\t});\n\t\tif (res.status === 401 || res.status === 403) throw new AuthError();\n\t\tif (!res.ok) {\n\t\t\tthrow new Error(`Failed to fetch devstack accounts: ${res.statusText}`);\n\t\t}\n\t\tconst { accounts } = (await res.json()) as { accounts: ServerAccountInfo[] };\n\t\treturn accounts.map((info) => {\n\t\t\tconst publicKey = publicKeyForScheme(info.publicKey, info.scheme);\n\t\t\tconst signer = new DevstackProxySigner({\n\t\t\t\taddress: info.address,\n\t\t\t\tpublicKey,\n\t\t\t\tscheme: info.scheme,\n\t\t\t\tserverOrigin: this.#serverOrigin,\n\t\t\t\tauthToken: this.#authToken,\n\t\t\t});\n\t\t\treturn buildManagedAccount(signer, info.address, info.name, DEVSTACK_WALLET_FEATURES);\n\t\t});\n\t}\n}\n\n/**\n * Pull the bearer token off a paired URL produced by the `wallet()`\n * plugin. Returns `null` if the input is undefined, malformed, or\n * carries a `<redacted>` placeholder (the manifest's pairUrl carries\n * the redacted form; the real token lives in a sibling 0o600 file the\n * consumer reads separately).\n *\n * The token rides in the URL fragment as `#token=<hex>`.\n */\nexport function parseDevstackToken(pairedUrl: string | undefined): string | null {\n\tif (pairedUrl === undefined) return null;\n\ttry {\n\t\tconst url = new URL(pairedUrl);\n\t\tconst hash = url.hash; // e.g. \"#token=abcd\"\n\t\tif (hash.length <= 1) return null;\n\t\tconst params = new URLSearchParams(hash.slice(1));\n\t\tconst token = params.get('token');\n\t\tif (token === null || token === '<redacted>') return null;\n\t\treturn token;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Narrow input — the only field the adapter consumes is\n * `app.wallet.{url, pairUrl}`. Codegen emits exactly this shape so\n * generated `dapp-kit-config.ts` doesn't have to fabricate placeholder\n * manifest fields (`stack.app`, `coins`, etc.) just to satisfy the\n * full `Manifest` type. Mirrors a slice of `AppManifest` from\n * `@mysten-incubation/devstack` without importing it (dev-wallet\n * doesn't depend on devstack — circular). */\nexport interface DevstackAdapterManifest {\n\tapp?: {\n\t\twallet?: {\n\t\t\turl: string;\n\t\t\tpairUrl?: string;\n\t\t};\n\t};\n}\n\n/**\n * Convenience: read the wallet-app endpoint off a devstack manifest and\n * build a configured adapter, or return `null` when the entry isn't\n * present (no `wallet(...)` in the stack, or it hasn't come up yet).\n *\n * The wallet entry's `pairUrl` carries the `#token=…` fragment used to\n * extract the bearer token. The full `Manifest` shape from\n * `@mysten-incubation/devstack` is structurally compatible with\n * {@link DevstackAdapterManifest}, so\n * `createDevstackAdapterFromManifest(devstackManifest)` typechecks\n * without a cast.\n */\nexport function createDevstackAdapterFromManifest(\n\tmanifest: DevstackAdapterManifest,\n): DevstackSignerAdapter | null {\n\tconst wallet = manifest.app?.wallet;\n\tif (wallet === undefined) return null;\n\treturn new DevstackSignerAdapter({\n\t\tserverOrigin: wallet.url,\n\t\ttoken: parseDevstackToken(wallet.pairUrl),\n\t});\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"devstack-adapter.mjs","names":["#address","#publicKey","#scheme","#serverOrigin","#authToken","#fetchAccounts"],"sources":["../../src/adapters/devstack-adapter.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n//\n// Devstack signer adapter — exposes accounts resolved by `devstack up`'s\n// `wallet()` plugin to dApp Kit, signing transactions over HTTP so\n// private keys never enter the frontend bundle. Mirrors RemoteCliAdapter's\n// out-of-process model; the difference is the source of accounts (devstack\n// resolved signers vs `sui keytool list`) and the endpoint paths.\n\nimport { Signer } from '@mysten/sui/cryptography';\nimport type { PublicKey, SignatureScheme } from '@mysten/sui/cryptography';\nimport { Ed25519PublicKey } from '@mysten/sui/keypairs/ed25519';\nimport { Secp256k1PublicKey } from '@mysten/sui/keypairs/secp256k1';\nimport { Secp256r1PublicKey } from '@mysten/sui/keypairs/secp256r1';\nimport { fromBase64, toBase64 } from '@mysten/sui/utils';\n\nimport type { ManagedAccount } from '../types.js';\nimport { BaseSignerAdapter } from './base-adapter.js';\nimport { buildManagedAccount } from './build-managed-account.js';\nimport { DEVSTACK_WALLET_HTTP_PATH } from './devstack-paths.js';\n\nconst DEVSTACK_WALLET_FEATURES = [\n\t'sui:signTransaction',\n\t'sui:signAndExecuteTransaction',\n\t'sui:signPersonalMessage',\n] as const;\n\ninterface ServerAccountInfo {\n\tname: string;\n\taddress: string;\n\tscheme: SignatureScheme;\n\tpublicKey: string;\n}\n\nclass AuthError extends Error {\n\tconstructor() {\n\t\tsuper('Unauthorized');\n\t\tthis.name = 'AuthError';\n\t}\n}\n\nfunction publicKeyForScheme(base64: string, scheme: SignatureScheme | string): PublicKey {\n\tconst bytes = fromBase64(base64);\n\t// The devstack server's wallet-app endpoint surfaces the scheme in\n\t// the lowercase form it standardised at the Account boundary\n\t// (`'ed25519'`, `'secp256k1'`, `'secp256r1'`). The Sui SDK's\n\t// `SignatureScheme` union is mixed-case (`'ED25519'`, `'Secp256k1'`,\n\t// …). Normalise to lowercase before matching so either casing parses.\n\tswitch (String(scheme).toLowerCase()) {\n\t\tcase 'ed25519':\n\t\t\treturn new Ed25519PublicKey(bytes);\n\t\tcase 'secp256k1':\n\t\t\treturn new Secp256k1PublicKey(bytes);\n\t\tcase 'secp256r1':\n\t\t\treturn new Secp256r1PublicKey(bytes);\n\t\tdefault:\n\t\t\tthrow new Error(`Unsupported key scheme: ${scheme}`);\n\t}\n}\n\n/**\n * Signer that delegates transaction signing to the devstack wallet-app\n * over HTTP. Mirrors {@link CliProxySigner} but talks to the\n * `wallet()` plugin's endpoints under `/api/v1/devstack/*`.\n */\nexport class DevstackProxySigner extends Signer {\n\t#address: string;\n\t#publicKey: PublicKey;\n\t#scheme: SignatureScheme;\n\t#serverOrigin: string;\n\t#authToken: string | null;\n\n\tconstructor(options: {\n\t\taddress: string;\n\t\tpublicKey: PublicKey;\n\t\tscheme: SignatureScheme;\n\t\tserverOrigin: string;\n\t\tauthToken?: string | null;\n\t}) {\n\t\tsuper();\n\t\tthis.#address = options.address;\n\t\tthis.#publicKey = options.publicKey;\n\t\tthis.#scheme = options.scheme;\n\t\tthis.#serverOrigin = options.serverOrigin;\n\t\tthis.#authToken = options.authToken ?? null;\n\t}\n\n\tasync sign(_bytes: Uint8Array): Promise<Uint8Array<ArrayBuffer>> {\n\t\tthrow new Error(\n\t\t\t'DevstackProxySigner does not support raw digest signing. ' +\n\t\t\t\t'Use signTransaction (the wallet-app signs BCS-serialized TransactionData).',\n\t\t);\n\t}\n\n\tgetKeyScheme(): SignatureScheme {\n\t\treturn this.#scheme;\n\t}\n\n\tgetPublicKey(): PublicKey {\n\t\treturn this.#publicKey;\n\t}\n\n\toverride toSuiAddress(): string {\n\t\treturn this.#address;\n\t}\n\n\toverride async signTransaction(bytes: Uint8Array) {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\t\tif (this.#authToken !== null) {\n\t\t\theaders['Authorization'] = `Bearer ${this.#authToken}`;\n\t\t}\n\t\tconst requestBytes = toBase64(bytes);\n\t\tconst res = await fetch(`${this.#serverOrigin}${DEVSTACK_WALLET_HTTP_PATH.SIGN_TRANSACTION}`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders,\n\t\t\tbody: JSON.stringify({\n\t\t\t\taddress: this.#address,\n\t\t\t\tbytes: requestBytes,\n\t\t\t}),\n\t\t});\n\t\tif (!res.ok) {\n\t\t\tconst body = await res.json().catch(() => ({}));\n\t\t\tconst message = (body as { error?: unknown }).error;\n\t\t\tthrow new Error(\n\t\t\t\t`Devstack signing failed: ${typeof message === 'string' ? message : res.statusText}`,\n\t\t\t);\n\t\t}\n\t\tconst { bytes: responseBytes, signature } = (await res.json()) as {\n\t\t\tbytes?: string;\n\t\t\tsignature?: string;\n\t\t};\n\t\tif (typeof responseBytes !== 'string' || responseBytes.length === 0) {\n\t\t\tthrow new Error('Devstack signing failed: server returned invalid signed bytes');\n\t\t}\n\t\tif (typeof signature !== 'string' || signature.length === 0) {\n\t\t\tthrow new Error('Devstack signing failed: server returned invalid signature');\n\t\t}\n\t\treturn {\n\t\t\tbytes: responseBytes,\n\t\t\tsignature,\n\t\t};\n\t}\n\n\toverride async signPersonalMessage(bytes: Uint8Array) {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\t\tif (this.#authToken !== null) {\n\t\t\theaders['Authorization'] = `Bearer ${this.#authToken}`;\n\t\t}\n\t\tconst res = await fetch(\n\t\t\t`${this.#serverOrigin}${DEVSTACK_WALLET_HTTP_PATH.SIGN_PERSONAL_MESSAGE}`,\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders,\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\taddress: this.#address,\n\t\t\t\t\tbytes: toBase64(bytes),\n\t\t\t\t}),\n\t\t\t},\n\t\t);\n\t\tif (!res.ok) {\n\t\t\tconst body = await res.json().catch(() => ({}));\n\t\t\tconst message = (body as { error?: unknown }).error;\n\t\t\tthrow new Error(\n\t\t\t\t`Devstack personal-message signing failed: ${\n\t\t\t\t\ttypeof message === 'string' ? message : res.statusText\n\t\t\t\t}`,\n\t\t\t);\n\t\t}\n\t\tconst { bytes: responseBytes, signature } = (await res.json()) as {\n\t\t\tbytes?: string;\n\t\t\tsignature?: string;\n\t\t};\n\t\tif (typeof responseBytes !== 'string' || responseBytes.length === 0) {\n\t\t\tthrow new Error(\n\t\t\t\t'Devstack personal-message signing failed: server returned invalid signed bytes',\n\t\t\t);\n\t\t}\n\t\tif (typeof signature !== 'string' || signature.length === 0) {\n\t\t\tthrow new Error('Devstack personal-message signing failed: server returned no signature');\n\t\t}\n\t\treturn { bytes: responseBytes, signature };\n\t}\n}\n\nexport interface DevstackSignerAdapterOptions {\n\t/** Wallet-app origin (e.g. `http://localhost:9420`). Read from the\n\t * matching endpoint entry on the active manifest. */\n\tserverOrigin: string;\n\t/** Bearer token. Parse `?token=<hex>` from the manifest entry's\n\t * `pairUrl`. Omit if the wallet-app runs without auth. */\n\ttoken?: string | null;\n\t/** Override the adapter's display name. Defaults to `'Devstack'`. */\n\tname?: string;\n}\n\n/**\n * {@link SignerAdapter} that surfaces every account resolved by `devstack up`\n * (including `cliSigner`/`envSigner`/`generatedKeypair` slots) without\n * shipping their private keys into the frontend bundle. All signing happens\n * server-side via the `wallet()` plugin.\n *\n * The simplest construction uses devstack's generated `dapp-kit-config.ts`,\n * which bakes the wallet adapter wiring at codegen time — apps spread\n * `devstackDappKitConfig` into `createDAppKit(...)` and never touch the\n * adapter directly.\n *\n * To construct one manually (e.g. from a non-manifest source):\n *\n * ```ts\n * const adapter = new DevstackSignerAdapter({\n * serverOrigin: 'http://localhost:9420',\n * token: '<bearer hex>',\n * });\n * ```\n */\nexport class DevstackSignerAdapter extends BaseSignerAdapter {\n\treadonly id = 'devstack';\n\treadonly name: string;\n\treadonly allowAutoSign = true;\n\n\t#serverOrigin: string;\n\t#authToken: string | null;\n\n\tconstructor(options: DevstackSignerAdapterOptions) {\n\t\tsuper();\n\t\ttry {\n\t\t\tnew URL(options.serverOrigin);\n\t\t} catch {\n\t\t\tthrow new Error(`DevstackSignerAdapter: invalid serverOrigin \"${options.serverOrigin}\"`);\n\t\t}\n\t\tthis.#serverOrigin = options.serverOrigin;\n\t\tthis.#authToken = options.token ?? null;\n\t\tthis.name = options.name ?? 'Devstack';\n\t}\n\n\tasync initialize(): Promise<void> {\n\t\ttry {\n\t\t\tconst accounts = await this.#fetchAccounts();\n\t\t\tthis.setInitialAccounts(accounts);\n\t\t} catch (error) {\n\t\t\tif (error instanceof AuthError) {\n\t\t\t\tconsole.warn('[dev-wallet] devstack adapter: unauthorized — skipping account import');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconsole.warn('[dev-wallet] devstack adapter: initialization failed:', error);\n\t\t}\n\t}\n\n\toverride destroy(): void {\n\t\tthis.#authToken = null;\n\t\tsuper.destroy();\n\t}\n\n\tasync #fetchAccounts(): Promise<ManagedAccount[]> {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\t\tif (this.#authToken !== null) {\n\t\t\theaders['Authorization'] = `Bearer ${this.#authToken}`;\n\t\t}\n\t\tconst res = await fetch(`${this.#serverOrigin}${DEVSTACK_WALLET_HTTP_PATH.ACCOUNTS}`, {\n\t\t\theaders,\n\t\t});\n\t\tif (res.status === 401 || res.status === 403) throw new AuthError();\n\t\tif (!res.ok) {\n\t\t\tthrow new Error(`Failed to fetch devstack accounts: ${res.statusText}`);\n\t\t}\n\t\tconst { accounts } = (await res.json()) as { accounts: ServerAccountInfo[] };\n\t\treturn accounts.map((info) => {\n\t\t\tconst publicKey = publicKeyForScheme(info.publicKey, info.scheme);\n\t\t\tconst signer = new DevstackProxySigner({\n\t\t\t\taddress: info.address,\n\t\t\t\tpublicKey,\n\t\t\t\tscheme: info.scheme,\n\t\t\t\tserverOrigin: this.#serverOrigin,\n\t\t\t\tauthToken: this.#authToken,\n\t\t\t});\n\t\t\treturn buildManagedAccount(signer, info.address, info.name, DEVSTACK_WALLET_FEATURES);\n\t\t});\n\t}\n}\n\n/**\n * Pull the bearer token off a paired URL produced by the `wallet()`\n * plugin. Returns `null` if the input is undefined, malformed, or\n * carries a `<redacted>` placeholder (the manifest's pairUrl carries\n * the redacted form; the real token lives in a sibling 0o600 file the\n * consumer reads separately).\n *\n * The token rides in the URL fragment as `#token=<hex>`.\n */\nexport function parseDevstackToken(pairedUrl: string | undefined): string | null {\n\tif (pairedUrl === undefined) return null;\n\ttry {\n\t\tconst url = new URL(pairedUrl);\n\t\tconst hash = url.hash; // e.g. \"#token=abcd\"\n\t\tif (hash.length <= 1) return null;\n\t\tconst params = new URLSearchParams(hash.slice(1));\n\t\tconst token = params.get('token');\n\t\tif (token === null || token === '<redacted>') return null;\n\t\treturn token;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Narrow input — the only field the adapter consumes is\n * `app.wallet.{url, pairUrl}`. Codegen emits exactly this shape so\n * generated `dapp-kit-config.ts` doesn't have to fabricate placeholder\n * manifest fields (`stack.app`, `coins`, etc.) just to satisfy the\n * full `Manifest` type. Mirrors a slice of `AppManifest` from\n * `@mysten-incubation/devstack` without importing it (dev-wallet\n * doesn't depend on devstack — circular). */\nexport interface DevstackAdapterManifest {\n\tapp?: {\n\t\twallet?: {\n\t\t\turl: string;\n\t\t\tpairUrl?: string;\n\t\t};\n\t};\n}\n\n/**\n * Convenience: read the wallet-app endpoint off a devstack manifest and\n * build a configured adapter, or return `null` when the entry isn't\n * present (no `wallet(...)` in the stack, or it hasn't come up yet).\n *\n * The wallet entry's `pairUrl` carries the `#token=…` fragment used to\n * extract the bearer token. The full `Manifest` shape from\n * `@mysten-incubation/devstack` is structurally compatible with\n * {@link DevstackAdapterManifest}, so\n * `createDevstackAdapterFromManifest(devstackManifest)` typechecks\n * without a cast.\n */\nexport function createDevstackAdapterFromManifest(\n\tmanifest: DevstackAdapterManifest,\n): DevstackSignerAdapter | null {\n\tconst wallet = manifest.app?.wallet;\n\tif (wallet === undefined) return null;\n\treturn new DevstackSignerAdapter({\n\t\tserverOrigin: wallet.url,\n\t\ttoken: parseDevstackToken(wallet.pairUrl),\n\t});\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"devstack-paths.d.mts","names":[],"sources":["../../src/adapters/devstack-paths.ts"],"mappings":";cAMa,yBAAA;EAAA;;;;;KAOD,2BAAA,WACH,yBAAA,eAAwC,yBAAA"}
1
+ {"version":3,"file":"devstack-paths.d.mts","names":[],"sources":["../../src/adapters/devstack-paths.ts"],"mappings":";cAMa,yBAAA;EAAA;;;;;KAOD,2BAAA,WACH,yBAAA,eAAwC,yBAAyB"}
@@ -1 +1 @@
1
- {"version":3,"file":"devstack-paths.mjs","names":[],"sources":["../../src/adapters/devstack-paths.ts"],"sourcesContent":["// Mirror of `packages/devstack/src/plugins/wallet/protocol.ts` `WalletHttpPath`.\n// Duplicated here rather than imported because devstack already\n// (peer-)depends on `@mysten-incubation/dev-wallet` for codegen-emitted\n// wallet glue, and adding the reverse edge would close the workspace\n// cycle and break turbo's build ordering.\n\nexport const DEVSTACK_WALLET_HTTP_PATH = {\n\tHEALTH: '/api/v1/devstack/health',\n\tACCOUNTS: '/api/v1/devstack/accounts',\n\tSIGN_TRANSACTION: '/api/v1/devstack/sign-transaction',\n\tSIGN_PERSONAL_MESSAGE: '/api/v1/devstack/sign-personal-message',\n} as const;\n\nexport type DevstackWalletHttpPathValue =\n\t(typeof DEVSTACK_WALLET_HTTP_PATH)[keyof typeof DEVSTACK_WALLET_HTTP_PATH];\n"],"mappings":";AAMA,MAAa,4BAA4B;CACxC,QAAQ;CACR,UAAU;CACV,kBAAkB;CAClB,uBAAuB;CACvB"}
1
+ {"version":3,"file":"devstack-paths.mjs","names":[],"sources":["../../src/adapters/devstack-paths.ts"],"sourcesContent":["// Mirror of `packages/devstack/src/plugins/wallet/protocol.ts` `WalletHttpPath`.\n// Duplicated here rather than imported because devstack already\n// (peer-)depends on `@mysten-incubation/dev-wallet` for codegen-emitted\n// wallet glue, and adding the reverse edge would close the workspace\n// cycle and break turbo's build ordering.\n\nexport const DEVSTACK_WALLET_HTTP_PATH = {\n\tHEALTH: '/api/v1/devstack/health',\n\tACCOUNTS: '/api/v1/devstack/accounts',\n\tSIGN_TRANSACTION: '/api/v1/devstack/sign-transaction',\n\tSIGN_PERSONAL_MESSAGE: '/api/v1/devstack/sign-personal-message',\n} as const;\n\nexport type DevstackWalletHttpPathValue =\n\t(typeof DEVSTACK_WALLET_HTTP_PATH)[keyof typeof DEVSTACK_WALLET_HTTP_PATH];\n"],"mappings":";AAMA,MAAa,4BAA4B;CACxC,QAAQ;CACR,UAAU;CACV,kBAAkB;CAClB,uBAAuB;AACxB"}
@@ -1 +1 @@
1
- {"version":3,"file":"fork-relay.d.mts","names":[],"sources":["../../src/adapters/fork-relay.ts"],"mappings":";cASa,oBAAA;EAAA;;;;;;;;;;AAcb;;UAAiB,UAAA;EAAA,SACP,UAAA;EAAA,SACA,OAAA;EAAA,SACA,UAAA;EAAA;;EAAA,SAGA,QAAA;AAAA;AAKV;;AAAA,UAAiB,qBAAA;EAAA,SACP,OAAA;EAAA,SACA,KAAA;EAAA;;;EAAA,SAIA,MAAA;AAAA;AAAA,UAGO,gBAAA;;;;WAIP,YAAA;EAKe;EAAA,SAHf,KAAA;AAAA;AAAA,cAGJ,kBAAA,SAA2B,KAAA;EAAA,SAEf,MAAA;cAAA,MAAA,UAChB,OAAA;AAAA;;KAQU,eAAA;EAAA,SACE,EAAA;EAAA,SAAmB,KAAA,EAAO,CAAA;AAAA;EAAA,SAC1B,EAAA;EAAA,SAAoB,KAAA;EAAA,SAAwB,MAAA;AAAA;;;;;;;;AA6B1D;;;;cAAa,SAAA;EAAA;cAIA,OAAA,EAAS,gBAAA;EAAA,IAUjB,YAAA,CAAA;EAIE,SAAA,CAAA,GAAa,OAAA,CAAQ,eAAA,CAAgB,UAAA;EAcrC,YAAA,CAAa,UAAA,WAAqB,OAAA,CAAQ,eAAA,CAAgB,UAAA;EAoB1D,iBAAA,CAAkB,KAAA,WAAgB,OAAA,CAAQ,eAAA,CAAgB,UAAA;EAoB1D,kBAAA,CAAA,GAAsB,OAAA,CAAQ,eAAA,CAAgB,qBAAA;EApBZ;;;EAoClC,gBAAA,CACL,OAAA,UACA,MAAA,YACE,OAAA,CAAQ,eAAA,CAAgB,qBAAA;AAAA;;;;;UAyEX,iBAAA;EAAA,SACP,GAAA;IAAA,SACC,MAAA;MAAA,SACC,GAAA;MAAA,SACA,OAAA;IAAA;EAAA;EAAA,SAGF,IAAA;IA3IH;;IAAA,SA8II,OAAA;IA9IsC;;IAAA,SAiJtC,QAAA;EAAA;AAAA;;;;;;;;;iBAkCK,2BAAA,CAA4B,QAAA,EAAU,iBAAA,GAAoB,SAAA"}
1
+ {"version":3,"file":"fork-relay.d.mts","names":[],"sources":["../../src/adapters/fork-relay.ts"],"mappings":";cASa,oBAAA;EAAA;;;;;;;;;;AAcb;;UAAiB,UAAA;EAAA,SACP,UAAA;EAAA,SACA,OAAA;EAAA,SACA,UAAA;EAAA;;EAAA,SAGA,QAAA;AAAA;AAKV;;AAAA,UAAiB,qBAAA;EAAA,SACP,OAAA;EAAA,SACA,KAAA;EAAA;;;EAAA,SAIA,MAAA;AAAA;AAAA,UAGO,gBAAA;;;AAMF;WAFL,YAAA;EAKe;EAAA,SAHf,KAAK;AAAA;AAAA,cAGT,kBAAA,SAA2B,KAAK;EAAA,SAEpB,MAAA;cAAA,MAAA,UAChB,OAAA;AAAA;;KAQU,eAAA;EAAA,SACE,EAAA;EAAA,SAAmB,KAAA,EAAO,CAAC;AAAA;EAAA,SAC3B,EAAA;EAAA,SAAoB,KAAA;EAAA,SAAwB,MAAA;AAAA;;;;;;;AAAM;AA6BhE;;;;cAAa,SAAA;EAAA;cAIA,OAAA,EAAS,gBAAA;EAAA,IAUjB,YAAA;EAIE,SAAA,IAAa,OAAA,CAAQ,eAAA,CAAgB,UAAA;EAcrC,YAAA,CAAa,UAAA,WAAqB,OAAA,CAAQ,eAAA,CAAgB,UAAA;EAoB1D,iBAAA,CAAkB,KAAA,WAAgB,OAAA,CAAQ,eAAA,CAAgB,UAAA;EAoB1D,kBAAA,IAAsB,OAAA,CAAQ,eAAA,CAAgB,qBAAA;EApBZ;;;EAoClC,gBAAA,CACL,OAAA,UACA,MAAA,YACE,OAAA,CAAQ,eAAA,CAAgB,qBAAA;AAAA;;;;;UAyEX,iBAAA;EAAA,SACP,GAAA;IAAA,SACC,MAAA;MAAA,SACC,GAAA;MAAA,SACA,OAAA;IAAA;EAAA;EAAA,SAGF,IAAA;IA3IH;;IAAA,SA8II,OAAA;IA9IsC;;IAAA,SAiJtC,QAAA;EAAA;AAAA;;;;;;;;;iBAkCK,2BAAA,CAA4B,QAAA,EAAU,iBAAA,GAAoB,SAAS"}
@@ -1 +1 @@
1
- {"version":3,"file":"fork-relay.mjs","names":["#serverOrigin","#authToken","#request"],"sources":["../../src/adapters/fork-relay.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n//\n// Browser-side relay for sui-fork admin RPCs. A compatible devstack\n// wallet endpoint can proxy a curated subset of the fork-control surface\n// (status read, advance-clock, advance-checkpoint, impersonation slot\n// list) over HTTP so the fork panel does not have to ship a gRPC\n// client into the browser bundle.\n\nexport const FORK_RELAY_HTTP_PATH = {\n\tSTATUS: '/api/v1/devstack/fork/status',\n\tADVANCE_CLOCK: '/api/v1/devstack/fork/advance-clock',\n\tADVANCE_CHECKPOINT: '/api/v1/devstack/fork/advance-checkpoint',\n\tIMPERSONATIONS: '/api/v1/devstack/fork/impersonations',\n} as const;\n\n/** Snapshot of the fork's current head state.\n *\n * Mirrors the body of `ForkControl.getStatus()` minus the unused\n * heartbeat fields. `clockMs` is a Unix-ms timestamp (the value\n * `clock::timestamp_ms()` returns on chain); `checkpoint` is the\n * latest sealed sequence number.\n */\nexport interface ForkStatus {\n\treadonly checkpoint: bigint;\n\treadonly clockMs: bigint;\n\treadonly autoTickMs?: number;\n\t/** Best-effort label of the upstream the fork is pointed at, e.g.\n\t * `'mainnet'` or `'testnet'`. Sourced from the devstack manifest. */\n\treadonly upstream?: string;\n}\n\n/** A single impersonation slot resolved by devstack — typically every\n * address that was passed to `Sui({fork:{impersonate:[…]}})`. */\nexport interface ForkImpersonationSlot {\n\treadonly address: string;\n\treadonly label?: string;\n\t/** Whether the wallet currently routes user-driven tx signing through\n\t * the `executeImpersonated` path for this address. The dev-wallet\n\t * server stores the toggle so it persists across page reloads. */\n\treadonly active: boolean;\n}\n\nexport interface ForkRelayOptions {\n\t/** Wallet-app origin (e.g. `http://localhost:9420`). Same value\n\t * `DevstackSignerAdapter` consumes — read from the manifest entry's\n\t * `app.wallet.url`. */\n\treadonly serverOrigin: string;\n\t/** Bearer token. Same parsing rules as `DevstackSignerAdapter`. */\n\treadonly token?: string | null;\n}\n\nclass ForkRelayHttpError extends Error {\n\tconstructor(\n\t\tpublic readonly status: number,\n\t\tmessage: string,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = 'ForkRelayHttpError';\n\t}\n}\n\n/** Tagged result so callers can render an inline error without throwing. */\nexport type ForkRelayResult<T> =\n\t| { readonly ok: true; readonly value: T }\n\t| { readonly ok: false; readonly error: string; readonly status?: number };\n\nfunction isForkStatusBody(\n\tvalue: unknown,\n): value is { checkpoint: string; clockMs: string; autoTickMs?: number; upstream?: string } {\n\tif (value === null || typeof value !== 'object') return false;\n\tconst v = value as Record<string, unknown>;\n\treturn typeof v.checkpoint === 'string' && typeof v.clockMs === 'string';\n}\n\nfunction isImpersonationsBody(\n\tvalue: unknown,\n): value is { slots: { address: string; label?: string; active: boolean }[] } {\n\tif (value === null || typeof value !== 'object') return false;\n\tconst slots = (value as { slots?: unknown }).slots;\n\treturn Array.isArray(slots);\n}\n\n/**\n * Thin HTTP client for the fork admin relay. Stateless — each call\n * issues a fresh `fetch`. The UI layer (`dev-wallet-fork-panel`)\n * polls `getStatus` on a low cadence and only refreshes after action\n * verbs land, so the lack of caching here is intentional.\n *\n * @example\n * const relay = new ForkRelay({ serverOrigin, token });\n * const status = await relay.getStatus();\n * if (status.ok) console.log(`clock=${status.value.clockMs}ms`);\n */\nexport class ForkRelay {\n\t#serverOrigin: string;\n\t#authToken: string | null;\n\n\tconstructor(options: ForkRelayOptions) {\n\t\ttry {\n\t\t\tnew URL(options.serverOrigin);\n\t\t} catch {\n\t\t\tthrow new Error(`ForkRelay: invalid serverOrigin \"${options.serverOrigin}\"`);\n\t\t}\n\t\tthis.#serverOrigin = options.serverOrigin;\n\t\tthis.#authToken = options.token ?? null;\n\t}\n\n\tget serverOrigin(): string {\n\t\treturn this.#serverOrigin;\n\t}\n\n\tasync getStatus(): Promise<ForkRelayResult<ForkStatus>> {\n\t\treturn this.#request('GET', FORK_RELAY_HTTP_PATH.STATUS, undefined, (body) => {\n\t\t\tif (!isForkStatusBody(body)) {\n\t\t\t\tthrow new Error('Malformed fork status response');\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tcheckpoint: BigInt(body.checkpoint),\n\t\t\t\tclockMs: BigInt(body.clockMs),\n\t\t\t\tautoTickMs: body.autoTickMs,\n\t\t\t\tupstream: body.upstream,\n\t\t\t};\n\t\t});\n\t}\n\n\tasync advanceClock(durationMs: number): Promise<ForkRelayResult<ForkStatus>> {\n\t\tif (!Number.isFinite(durationMs) || durationMs <= 0 || !Number.isInteger(durationMs)) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `advanceClock: durationMs must be a positive integer (got ${durationMs})`,\n\t\t\t};\n\t\t}\n\t\treturn this.#request('POST', FORK_RELAY_HTTP_PATH.ADVANCE_CLOCK, { durationMs }, (body) => {\n\t\t\tif (!isForkStatusBody(body)) {\n\t\t\t\tthrow new Error('Malformed advance-clock response');\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tcheckpoint: BigInt(body.checkpoint),\n\t\t\t\tclockMs: BigInt(body.clockMs),\n\t\t\t\tautoTickMs: body.autoTickMs,\n\t\t\t\tupstream: body.upstream,\n\t\t\t};\n\t\t});\n\t}\n\n\tasync advanceCheckpoint(count: number): Promise<ForkRelayResult<ForkStatus>> {\n\t\tif (!Number.isFinite(count) || count <= 0 || !Number.isInteger(count)) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `advanceCheckpoint: count must be a positive integer (got ${count})`,\n\t\t\t};\n\t\t}\n\t\treturn this.#request('POST', FORK_RELAY_HTTP_PATH.ADVANCE_CHECKPOINT, { count }, (body) => {\n\t\t\tif (!isForkStatusBody(body)) {\n\t\t\t\tthrow new Error('Malformed advance-checkpoint response');\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tcheckpoint: BigInt(body.checkpoint),\n\t\t\t\tclockMs: BigInt(body.clockMs),\n\t\t\t\tautoTickMs: body.autoTickMs,\n\t\t\t\tupstream: body.upstream,\n\t\t\t};\n\t\t});\n\t}\n\n\tasync listImpersonations(): Promise<ForkRelayResult<ForkImpersonationSlot[]>> {\n\t\treturn this.#request('GET', FORK_RELAY_HTTP_PATH.IMPERSONATIONS, undefined, (body) => {\n\t\t\tif (!isImpersonationsBody(body)) {\n\t\t\t\tthrow new Error('Malformed impersonations response');\n\t\t\t}\n\t\t\treturn body.slots.map((s) => ({\n\t\t\t\taddress: s.address,\n\t\t\t\tlabel: s.label,\n\t\t\t\tactive: s.active,\n\t\t\t}));\n\t\t});\n\t}\n\n\t/** Toggle the impersonation slot identified by `address`. The server\n\t * is the source of truth — the response carries the resulting slot\n\t * list so the panel re-renders consistently. */\n\tasync setImpersonation(\n\t\taddress: string,\n\t\tactive: boolean,\n\t): Promise<ForkRelayResult<ForkImpersonationSlot[]>> {\n\t\treturn this.#request(\n\t\t\t'POST',\n\t\t\tFORK_RELAY_HTTP_PATH.IMPERSONATIONS,\n\t\t\t{ address, active },\n\t\t\t(body) => {\n\t\t\t\tif (!isImpersonationsBody(body)) {\n\t\t\t\t\tthrow new Error('Malformed impersonations response');\n\t\t\t\t}\n\t\t\t\treturn body.slots.map((s) => ({\n\t\t\t\t\taddress: s.address,\n\t\t\t\t\tlabel: s.label,\n\t\t\t\t\tactive: s.active,\n\t\t\t\t}));\n\t\t\t},\n\t\t);\n\t}\n\n\tasync #request<T>(\n\t\tmethod: 'GET' | 'POST',\n\t\tpath: string,\n\t\tbody: unknown,\n\t\tparse: (raw: unknown) => T,\n\t): Promise<ForkRelayResult<T>> {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\t\tif (this.#authToken !== null) {\n\t\t\theaders['Authorization'] = `Bearer ${this.#authToken}`;\n\t\t}\n\t\tconst init: RequestInit = { method, headers };\n\t\tif (method === 'POST' && body !== undefined) {\n\t\t\tinit.body = JSON.stringify(body);\n\t\t}\n\t\tlet res: Response;\n\t\ttry {\n\t\t\tres = await fetch(`${this.#serverOrigin}${path}`, init);\n\t\t} catch (cause) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `Network error: ${cause instanceof Error ? cause.message : String(cause)}`,\n\t\t\t};\n\t\t}\n\t\tif (!res.ok) {\n\t\t\tconst text = await res.text().catch(() => res.statusText);\n\t\t\treturn { ok: false, error: text || res.statusText, status: res.status };\n\t\t}\n\t\tlet parsedBody: unknown;\n\t\ttry {\n\t\t\tparsedBody = await res.json();\n\t\t} catch (cause) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `Invalid JSON response: ${cause instanceof Error ? cause.message : String(cause)}`,\n\t\t\t\tstatus: res.status,\n\t\t\t};\n\t\t}\n\t\ttry {\n\t\t\treturn { ok: true, value: parse(parsedBody) };\n\t\t} catch (cause) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: cause instanceof Error ? cause.message : String(cause),\n\t\t\t\tstatus: res.status,\n\t\t\t};\n\t\t}\n\t}\n}\n\nexport { ForkRelayHttpError };\n\n/** Narrow input mirroring `DevstackAdapterManifest` — enough to\n * discover the wallet endpoint + fork-mode flag without pulling in\n * the full devstack `Manifest` type (no reverse dependency edge,\n * same reasoning as `devstack-adapter.ts`). */\nexport interface ForkRelayManifest {\n\treadonly app?: {\n\t\treadonly wallet?: {\n\t\t\treadonly url: string;\n\t\t\treadonly pairUrl?: string;\n\t\t};\n\t};\n\treadonly meta?: {\n\t\t/** Devstack's `meta.runtime` is `'bundled' | 'forked'`. Only\n\t\t * `'forked'` triggers fork-controls construction. */\n\t\treadonly runtime?: string;\n\t\t/** Optional upstream label (`'mainnet'`, `'testnet'`, …) used\n\t\t * by the panel banner. */\n\t\treadonly upstream?: string;\n\t};\n}\n\n/** Parse the bearer token off the manifest's `pairUrl`. Duplicates the\n * logic in `devstack-adapter.ts` rather than importing it because the\n * fork-relay module must remain dependency-free from the adapter\n * layer (it's also consumed by codegen-emitted glue that doesn't pull\n * in the signer adapter bundle). */\nfunction tokenFromPairUrl(pairedUrl: string | undefined): string | null {\n\tif (pairedUrl === undefined) return null;\n\ttry {\n\t\tconst url = new URL(pairedUrl);\n\t\tif (url.hash.length > 1) {\n\t\t\tconst params = new URLSearchParams(url.hash.slice(1));\n\t\t\tconst fromHash = params.get('token');\n\t\t\tif (fromHash !== null && fromHash !== '<redacted>') return fromHash;\n\t\t}\n\t\tconst fromQuery = url.searchParams.get('token');\n\t\tif (fromQuery !== null && fromQuery !== '<redacted>') return fromQuery;\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Build a configured {@link ForkRelay} from a devstack manifest, or\n * `null` when fork-mode isn't active (bundled stack, no wallet entry,\n * or the manifest hasn't loaded yet).\n *\n * Codegen-emitted browser glue calls this and forwards the result to\n * `<dev-wallet-panel forkRelay={…}>`.\n */\nexport function createForkRelayFromManifest(manifest: ForkRelayManifest): ForkRelay | null {\n\tif (manifest.meta?.runtime !== 'forked') return null;\n\tconst wallet = manifest.app?.wallet;\n\tif (wallet === undefined) return null;\n\ttry {\n\t\treturn new ForkRelay({\n\t\t\tserverOrigin: wallet.url,\n\t\t\ttoken: tokenFromPairUrl(wallet.pairUrl),\n\t\t});\n\t} catch {\n\t\t// `new URL(…)` failure surfaces as a thrown Error — swallow it\n\t\t// here rather than propagating, the calling app shouldn't\n\t\t// blow up just because the manifest's wallet URL is malformed.\n\t\treturn null;\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"fork-relay.mjs","names":["#serverOrigin","#authToken","#request"],"sources":["../../src/adapters/fork-relay.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n//\n// Browser-side relay for sui-fork admin RPCs. A compatible devstack\n// wallet endpoint can proxy a curated subset of the fork-control surface\n// (status read, advance-clock, advance-checkpoint, impersonation slot\n// list) over HTTP so the fork panel does not have to ship a gRPC\n// client into the browser bundle.\n\nexport const FORK_RELAY_HTTP_PATH = {\n\tSTATUS: '/api/v1/devstack/fork/status',\n\tADVANCE_CLOCK: '/api/v1/devstack/fork/advance-clock',\n\tADVANCE_CHECKPOINT: '/api/v1/devstack/fork/advance-checkpoint',\n\tIMPERSONATIONS: '/api/v1/devstack/fork/impersonations',\n} as const;\n\n/** Snapshot of the fork's current head state.\n *\n * Mirrors the body of `ForkControl.getStatus()` minus the unused\n * heartbeat fields. `clockMs` is a Unix-ms timestamp (the value\n * `clock::timestamp_ms()` returns on chain); `checkpoint` is the\n * latest sealed sequence number.\n */\nexport interface ForkStatus {\n\treadonly checkpoint: bigint;\n\treadonly clockMs: bigint;\n\treadonly autoTickMs?: number;\n\t/** Best-effort label of the upstream the fork is pointed at, e.g.\n\t * `'mainnet'` or `'testnet'`. Sourced from the devstack manifest. */\n\treadonly upstream?: string;\n}\n\n/** A single impersonation slot resolved by devstack — typically every\n * address that was passed to `Sui({fork:{impersonate:[…]}})`. */\nexport interface ForkImpersonationSlot {\n\treadonly address: string;\n\treadonly label?: string;\n\t/** Whether the wallet currently routes user-driven tx signing through\n\t * the `executeImpersonated` path for this address. The dev-wallet\n\t * server stores the toggle so it persists across page reloads. */\n\treadonly active: boolean;\n}\n\nexport interface ForkRelayOptions {\n\t/** Wallet-app origin (e.g. `http://localhost:9420`). Same value\n\t * `DevstackSignerAdapter` consumes — read from the manifest entry's\n\t * `app.wallet.url`. */\n\treadonly serverOrigin: string;\n\t/** Bearer token. Same parsing rules as `DevstackSignerAdapter`. */\n\treadonly token?: string | null;\n}\n\nclass ForkRelayHttpError extends Error {\n\tconstructor(\n\t\tpublic readonly status: number,\n\t\tmessage: string,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = 'ForkRelayHttpError';\n\t}\n}\n\n/** Tagged result so callers can render an inline error without throwing. */\nexport type ForkRelayResult<T> =\n\t| { readonly ok: true; readonly value: T }\n\t| { readonly ok: false; readonly error: string; readonly status?: number };\n\nfunction isForkStatusBody(\n\tvalue: unknown,\n): value is { checkpoint: string; clockMs: string; autoTickMs?: number; upstream?: string } {\n\tif (value === null || typeof value !== 'object') return false;\n\tconst v = value as Record<string, unknown>;\n\treturn typeof v.checkpoint === 'string' && typeof v.clockMs === 'string';\n}\n\nfunction isImpersonationsBody(\n\tvalue: unknown,\n): value is { slots: { address: string; label?: string; active: boolean }[] } {\n\tif (value === null || typeof value !== 'object') return false;\n\tconst slots = (value as { slots?: unknown }).slots;\n\treturn Array.isArray(slots);\n}\n\n/**\n * Thin HTTP client for the fork admin relay. Stateless — each call\n * issues a fresh `fetch`. The UI layer (`dev-wallet-fork-panel`)\n * polls `getStatus` on a low cadence and only refreshes after action\n * verbs land, so the lack of caching here is intentional.\n *\n * @example\n * const relay = new ForkRelay({ serverOrigin, token });\n * const status = await relay.getStatus();\n * if (status.ok) console.log(`clock=${status.value.clockMs}ms`);\n */\nexport class ForkRelay {\n\t#serverOrigin: string;\n\t#authToken: string | null;\n\n\tconstructor(options: ForkRelayOptions) {\n\t\ttry {\n\t\t\tnew URL(options.serverOrigin);\n\t\t} catch {\n\t\t\tthrow new Error(`ForkRelay: invalid serverOrigin \"${options.serverOrigin}\"`);\n\t\t}\n\t\tthis.#serverOrigin = options.serverOrigin;\n\t\tthis.#authToken = options.token ?? null;\n\t}\n\n\tget serverOrigin(): string {\n\t\treturn this.#serverOrigin;\n\t}\n\n\tasync getStatus(): Promise<ForkRelayResult<ForkStatus>> {\n\t\treturn this.#request('GET', FORK_RELAY_HTTP_PATH.STATUS, undefined, (body) => {\n\t\t\tif (!isForkStatusBody(body)) {\n\t\t\t\tthrow new Error('Malformed fork status response');\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tcheckpoint: BigInt(body.checkpoint),\n\t\t\t\tclockMs: BigInt(body.clockMs),\n\t\t\t\tautoTickMs: body.autoTickMs,\n\t\t\t\tupstream: body.upstream,\n\t\t\t};\n\t\t});\n\t}\n\n\tasync advanceClock(durationMs: number): Promise<ForkRelayResult<ForkStatus>> {\n\t\tif (!Number.isFinite(durationMs) || durationMs <= 0 || !Number.isInteger(durationMs)) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `advanceClock: durationMs must be a positive integer (got ${durationMs})`,\n\t\t\t};\n\t\t}\n\t\treturn this.#request('POST', FORK_RELAY_HTTP_PATH.ADVANCE_CLOCK, { durationMs }, (body) => {\n\t\t\tif (!isForkStatusBody(body)) {\n\t\t\t\tthrow new Error('Malformed advance-clock response');\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tcheckpoint: BigInt(body.checkpoint),\n\t\t\t\tclockMs: BigInt(body.clockMs),\n\t\t\t\tautoTickMs: body.autoTickMs,\n\t\t\t\tupstream: body.upstream,\n\t\t\t};\n\t\t});\n\t}\n\n\tasync advanceCheckpoint(count: number): Promise<ForkRelayResult<ForkStatus>> {\n\t\tif (!Number.isFinite(count) || count <= 0 || !Number.isInteger(count)) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `advanceCheckpoint: count must be a positive integer (got ${count})`,\n\t\t\t};\n\t\t}\n\t\treturn this.#request('POST', FORK_RELAY_HTTP_PATH.ADVANCE_CHECKPOINT, { count }, (body) => {\n\t\t\tif (!isForkStatusBody(body)) {\n\t\t\t\tthrow new Error('Malformed advance-checkpoint response');\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tcheckpoint: BigInt(body.checkpoint),\n\t\t\t\tclockMs: BigInt(body.clockMs),\n\t\t\t\tautoTickMs: body.autoTickMs,\n\t\t\t\tupstream: body.upstream,\n\t\t\t};\n\t\t});\n\t}\n\n\tasync listImpersonations(): Promise<ForkRelayResult<ForkImpersonationSlot[]>> {\n\t\treturn this.#request('GET', FORK_RELAY_HTTP_PATH.IMPERSONATIONS, undefined, (body) => {\n\t\t\tif (!isImpersonationsBody(body)) {\n\t\t\t\tthrow new Error('Malformed impersonations response');\n\t\t\t}\n\t\t\treturn body.slots.map((s) => ({\n\t\t\t\taddress: s.address,\n\t\t\t\tlabel: s.label,\n\t\t\t\tactive: s.active,\n\t\t\t}));\n\t\t});\n\t}\n\n\t/** Toggle the impersonation slot identified by `address`. The server\n\t * is the source of truth — the response carries the resulting slot\n\t * list so the panel re-renders consistently. */\n\tasync setImpersonation(\n\t\taddress: string,\n\t\tactive: boolean,\n\t): Promise<ForkRelayResult<ForkImpersonationSlot[]>> {\n\t\treturn this.#request(\n\t\t\t'POST',\n\t\t\tFORK_RELAY_HTTP_PATH.IMPERSONATIONS,\n\t\t\t{ address, active },\n\t\t\t(body) => {\n\t\t\t\tif (!isImpersonationsBody(body)) {\n\t\t\t\t\tthrow new Error('Malformed impersonations response');\n\t\t\t\t}\n\t\t\t\treturn body.slots.map((s) => ({\n\t\t\t\t\taddress: s.address,\n\t\t\t\t\tlabel: s.label,\n\t\t\t\t\tactive: s.active,\n\t\t\t\t}));\n\t\t\t},\n\t\t);\n\t}\n\n\tasync #request<T>(\n\t\tmethod: 'GET' | 'POST',\n\t\tpath: string,\n\t\tbody: unknown,\n\t\tparse: (raw: unknown) => T,\n\t): Promise<ForkRelayResult<T>> {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\t\tif (this.#authToken !== null) {\n\t\t\theaders['Authorization'] = `Bearer ${this.#authToken}`;\n\t\t}\n\t\tconst init: RequestInit = { method, headers };\n\t\tif (method === 'POST' && body !== undefined) {\n\t\t\tinit.body = JSON.stringify(body);\n\t\t}\n\t\tlet res: Response;\n\t\ttry {\n\t\t\tres = await fetch(`${this.#serverOrigin}${path}`, init);\n\t\t} catch (cause) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `Network error: ${cause instanceof Error ? cause.message : String(cause)}`,\n\t\t\t};\n\t\t}\n\t\tif (!res.ok) {\n\t\t\tconst text = await res.text().catch(() => res.statusText);\n\t\t\treturn { ok: false, error: text || res.statusText, status: res.status };\n\t\t}\n\t\tlet parsedBody: unknown;\n\t\ttry {\n\t\t\tparsedBody = await res.json();\n\t\t} catch (cause) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `Invalid JSON response: ${cause instanceof Error ? cause.message : String(cause)}`,\n\t\t\t\tstatus: res.status,\n\t\t\t};\n\t\t}\n\t\ttry {\n\t\t\treturn { ok: true, value: parse(parsedBody) };\n\t\t} catch (cause) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: cause instanceof Error ? cause.message : String(cause),\n\t\t\t\tstatus: res.status,\n\t\t\t};\n\t\t}\n\t}\n}\n\nexport { ForkRelayHttpError };\n\n/** Narrow input mirroring `DevstackAdapterManifest` — enough to\n * discover the wallet endpoint + fork-mode flag without pulling in\n * the full devstack `Manifest` type (no reverse dependency edge,\n * same reasoning as `devstack-adapter.ts`). */\nexport interface ForkRelayManifest {\n\treadonly app?: {\n\t\treadonly wallet?: {\n\t\t\treadonly url: string;\n\t\t\treadonly pairUrl?: string;\n\t\t};\n\t};\n\treadonly meta?: {\n\t\t/** Devstack's `meta.runtime` is `'bundled' | 'forked'`. Only\n\t\t * `'forked'` triggers fork-controls construction. */\n\t\treadonly runtime?: string;\n\t\t/** Optional upstream label (`'mainnet'`, `'testnet'`, …) used\n\t\t * by the panel banner. */\n\t\treadonly upstream?: string;\n\t};\n}\n\n/** Parse the bearer token off the manifest's `pairUrl`. Duplicates the\n * logic in `devstack-adapter.ts` rather than importing it because the\n * fork-relay module must remain dependency-free from the adapter\n * layer (it's also consumed by codegen-emitted glue that doesn't pull\n * in the signer adapter bundle). */\nfunction tokenFromPairUrl(pairedUrl: string | undefined): string | null {\n\tif (pairedUrl === undefined) return null;\n\ttry {\n\t\tconst url = new URL(pairedUrl);\n\t\tif (url.hash.length > 1) {\n\t\t\tconst params = new URLSearchParams(url.hash.slice(1));\n\t\t\tconst fromHash = params.get('token');\n\t\t\tif (fromHash !== null && fromHash !== '<redacted>') return fromHash;\n\t\t}\n\t\tconst fromQuery = url.searchParams.get('token');\n\t\tif (fromQuery !== null && fromQuery !== '<redacted>') return fromQuery;\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Build a configured {@link ForkRelay} from a devstack manifest, or\n * `null` when fork-mode isn't active (bundled stack, no wallet entry,\n * or the manifest hasn't loaded yet).\n *\n * Codegen-emitted browser glue calls this and forwards the result to\n * `<dev-wallet-panel forkRelay={…}>`.\n */\nexport function createForkRelayFromManifest(manifest: ForkRelayManifest): ForkRelay | null {\n\tif (manifest.meta?.runtime !== 'forked') return null;\n\tconst wallet = manifest.app?.wallet;\n\tif (wallet === undefined) return null;\n\ttry {\n\t\treturn new ForkRelay({\n\t\t\tserverOrigin: wallet.url,\n\t\t\ttoken: tokenFromPairUrl(wallet.pairUrl),\n\t\t});\n\t} catch {\n\t\t// `new URL(…)` failure surfaces as a thrown Error — swallow it\n\t\t// here rather than propagating, the calling app shouldn't\n\t\t// blow up just because the manifest's wallet URL is malformed.\n\t\treturn null;\n\t}\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"idb-store.mjs","names":["#dbName","#storeName","#withTx","#open","#db"],"sources":["../../src/adapters/idb-store.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Minimal IndexedDB key-value store replacing the `idb-keyval` package.\n *\n * The database connection is opened lazily on first use and reused for all\n * subsequent operations.\n */\nexport class IDBStore {\n\t#dbName: string;\n\t#storeName: string;\n\t#db: Promise<IDBDatabase> | null = null;\n\n\tconstructor(dbName: string, storeName: string) {\n\t\tthis.#dbName = dbName;\n\t\tthis.#storeName = storeName;\n\t}\n\n\tget<T>(key: IDBValidKey): Promise<T | undefined> {\n\t\treturn this.#withTx('readonly', (s) => s.get(key) as IDBRequest<T | undefined>);\n\t}\n\n\tasync set(key: IDBValidKey, value: unknown): Promise<void> {\n\t\tawait this.#withTx('readwrite', (s) => s.put(value, key));\n\t}\n\n\tdel(key: IDBValidKey): Promise<void> {\n\t\treturn this.#withTx('readwrite', (s) => s.delete(key) as IDBRequest<void>);\n\t}\n\n\tentries<K extends IDBValidKey, V>(): Promise<[K, V][]> {\n\t\treturn this.#open().then(\n\t\t\t(db) =>\n\t\t\t\tnew Promise((resolve, reject) => {\n\t\t\t\t\tconst tx = db.transaction(this.#storeName, 'readonly');\n\t\t\t\t\tconst objectStore = tx.objectStore(this.#storeName);\n\t\t\t\t\tconst results: [K, V][] = [];\n\t\t\t\t\tconst cursor = objectStore.openCursor();\n\t\t\t\t\tcursor.onsuccess = () => {\n\t\t\t\t\t\tconst c = cursor.result;\n\t\t\t\t\t\tif (c) {\n\t\t\t\t\t\t\tresults.push([c.key as K, c.value as V]);\n\t\t\t\t\t\t\tc.continue();\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\ttx.oncomplete = () => resolve(results);\n\t\t\t\t\ttx.onerror = () => reject(tx.error);\n\t\t\t\t}),\n\t\t);\n\t}\n\n\t#open(): Promise<IDBDatabase> {\n\t\tif (!this.#db) {\n\t\t\tthis.#db = new Promise((resolve, reject) => {\n\t\t\t\tconst request = indexedDB.open(this.#dbName);\n\t\t\t\trequest.onupgradeneeded = () => {\n\t\t\t\t\tif (!request.result.objectStoreNames.contains(this.#storeName)) {\n\t\t\t\t\t\trequest.result.createObjectStore(this.#storeName);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\trequest.onsuccess = () => resolve(request.result);\n\t\t\t\trequest.onerror = () => {\n\t\t\t\t\tthis.#db = null;\n\t\t\t\t\treject(request.error);\n\t\t\t\t};\n\t\t\t});\n\t\t}\n\t\treturn this.#db;\n\t}\n\n\t#withTx<T>(\n\t\tmode: IDBTransactionMode,\n\t\tfn: (objectStore: IDBObjectStore) => IDBRequest<T>,\n\t): Promise<T> {\n\t\treturn this.#open().then(\n\t\t\t(db) =>\n\t\t\t\tnew Promise((resolve, reject) => {\n\t\t\t\t\tconst tx = db.transaction(this.#storeName, mode);\n\t\t\t\t\tconst req = fn(tx.objectStore(this.#storeName));\n\t\t\t\t\ttx.oncomplete = () => resolve(req.result);\n\t\t\t\t\ttx.onerror = () => reject(tx.error);\n\t\t\t\t\ttx.onabort = () => reject(tx.error);\n\t\t\t\t}),\n\t\t);\n\t}\n}\n"],"mappings":";;;;;;;AASA,IAAa,WAAb,MAAsB;CACrB;CACA;CACA,MAAmC;CAEnC,YAAY,QAAgB,WAAmB;AAC9C,QAAA,SAAe;AACf,QAAA,YAAkB;;CAGnB,IAAO,KAA0C;AAChD,SAAO,MAAA,OAAa,aAAa,MAAM,EAAE,IAAI,IAAI,CAA8B;;CAGhF,MAAM,IAAI,KAAkB,OAA+B;AAC1D,QAAM,MAAA,OAAa,cAAc,MAAM,EAAE,IAAI,OAAO,IAAI,CAAC;;CAG1D,IAAI,KAAiC;AACpC,SAAO,MAAA,OAAa,cAAc,MAAM,EAAE,OAAO,IAAI,CAAqB;;CAG3E,UAAuD;AACtD,SAAO,MAAA,MAAY,CAAC,MAClB,OACA,IAAI,SAAS,SAAS,WAAW;GAChC,MAAM,KAAK,GAAG,YAAY,MAAA,WAAiB,WAAW;GACtD,MAAM,cAAc,GAAG,YAAY,MAAA,UAAgB;GACnD,MAAM,UAAoB,EAAE;GAC5B,MAAM,SAAS,YAAY,YAAY;AACvC,UAAO,kBAAkB;IACxB,MAAM,IAAI,OAAO;AACjB,QAAI,GAAG;AACN,aAAQ,KAAK,CAAC,EAAE,KAAU,EAAE,MAAW,CAAC;AACxC,OAAE,UAAU;;;AAGd,MAAG,mBAAmB,QAAQ,QAAQ;AACtC,MAAG,gBAAgB,OAAO,GAAG,MAAM;IAClC,CACH;;CAGF,QAA8B;AAC7B,MAAI,CAAC,MAAA,GACJ,OAAA,KAAW,IAAI,SAAS,SAAS,WAAW;GAC3C,MAAM,UAAU,UAAU,KAAK,MAAA,OAAa;AAC5C,WAAQ,wBAAwB;AAC/B,QAAI,CAAC,QAAQ,OAAO,iBAAiB,SAAS,MAAA,UAAgB,CAC7D,SAAQ,OAAO,kBAAkB,MAAA,UAAgB;;AAGnD,WAAQ,kBAAkB,QAAQ,QAAQ,OAAO;AACjD,WAAQ,gBAAgB;AACvB,UAAA,KAAW;AACX,WAAO,QAAQ,MAAM;;IAErB;AAEH,SAAO,MAAA;;CAGR,QACC,MACA,IACa;AACb,SAAO,MAAA,MAAY,CAAC,MAClB,OACA,IAAI,SAAS,SAAS,WAAW;GAChC,MAAM,KAAK,GAAG,YAAY,MAAA,WAAiB,KAAK;GAChD,MAAM,MAAM,GAAG,GAAG,YAAY,MAAA,UAAgB,CAAC;AAC/C,MAAG,mBAAmB,QAAQ,IAAI,OAAO;AACzC,MAAG,gBAAgB,OAAO,GAAG,MAAM;AACnC,MAAG,gBAAgB,OAAO,GAAG,MAAM;IAClC,CACH"}
1
+ {"version":3,"file":"idb-store.mjs","names":["#dbName","#storeName","#withTx","#open","#db"],"sources":["../../src/adapters/idb-store.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Minimal IndexedDB key-value store replacing the `idb-keyval` package.\n *\n * The database connection is opened lazily on first use and reused for all\n * subsequent operations.\n */\nexport class IDBStore {\n\t#dbName: string;\n\t#storeName: string;\n\t#db: Promise<IDBDatabase> | null = null;\n\n\tconstructor(dbName: string, storeName: string) {\n\t\tthis.#dbName = dbName;\n\t\tthis.#storeName = storeName;\n\t}\n\n\tget<T>(key: IDBValidKey): Promise<T | undefined> {\n\t\treturn this.#withTx('readonly', (s) => s.get(key) as IDBRequest<T | undefined>);\n\t}\n\n\tasync set(key: IDBValidKey, value: unknown): Promise<void> {\n\t\tawait this.#withTx('readwrite', (s) => s.put(value, key));\n\t}\n\n\tdel(key: IDBValidKey): Promise<void> {\n\t\treturn this.#withTx('readwrite', (s) => s.delete(key) as IDBRequest<void>);\n\t}\n\n\tentries<K extends IDBValidKey, V>(): Promise<[K, V][]> {\n\t\treturn this.#open().then(\n\t\t\t(db) =>\n\t\t\t\tnew Promise((resolve, reject) => {\n\t\t\t\t\tconst tx = db.transaction(this.#storeName, 'readonly');\n\t\t\t\t\tconst objectStore = tx.objectStore(this.#storeName);\n\t\t\t\t\tconst results: [K, V][] = [];\n\t\t\t\t\tconst cursor = objectStore.openCursor();\n\t\t\t\t\tcursor.onsuccess = () => {\n\t\t\t\t\t\tconst c = cursor.result;\n\t\t\t\t\t\tif (c) {\n\t\t\t\t\t\t\tresults.push([c.key as K, c.value as V]);\n\t\t\t\t\t\t\tc.continue();\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\ttx.oncomplete = () => resolve(results);\n\t\t\t\t\ttx.onerror = () => reject(tx.error);\n\t\t\t\t}),\n\t\t);\n\t}\n\n\t#open(): Promise<IDBDatabase> {\n\t\tif (!this.#db) {\n\t\t\tthis.#db = new Promise((resolve, reject) => {\n\t\t\t\tconst request = indexedDB.open(this.#dbName);\n\t\t\t\trequest.onupgradeneeded = () => {\n\t\t\t\t\tif (!request.result.objectStoreNames.contains(this.#storeName)) {\n\t\t\t\t\t\trequest.result.createObjectStore(this.#storeName);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\trequest.onsuccess = () => resolve(request.result);\n\t\t\t\trequest.onerror = () => {\n\t\t\t\t\tthis.#db = null;\n\t\t\t\t\treject(request.error);\n\t\t\t\t};\n\t\t\t});\n\t\t}\n\t\treturn this.#db;\n\t}\n\n\t#withTx<T>(\n\t\tmode: IDBTransactionMode,\n\t\tfn: (objectStore: IDBObjectStore) => IDBRequest<T>,\n\t): Promise<T> {\n\t\treturn this.#open().then(\n\t\t\t(db) =>\n\t\t\t\tnew Promise((resolve, reject) => {\n\t\t\t\t\tconst tx = db.transaction(this.#storeName, mode);\n\t\t\t\t\tconst req = fn(tx.objectStore(this.#storeName));\n\t\t\t\t\ttx.oncomplete = () => resolve(req.result);\n\t\t\t\t\ttx.onerror = () => reject(tx.error);\n\t\t\t\t\ttx.onabort = () => reject(tx.error);\n\t\t\t\t}),\n\t\t);\n\t}\n}\n"],"mappings":";;;;;;;AASA,IAAa,WAAb,MAAsB;CACrB;CACA;CACA,MAAmC;CAEnC,YAAY,QAAgB,WAAmB;EAC9C,KAAKA,UAAU;EACf,KAAKC,aAAa;CACnB;CAEA,IAAO,KAA0C;EAChD,OAAO,KAAKC,QAAQ,aAAa,MAAM,EAAE,IAAI,GAAG,CAA8B;CAC/E;CAEA,MAAM,IAAI,KAAkB,OAA+B;EAC1D,MAAM,KAAKA,QAAQ,cAAc,MAAM,EAAE,IAAI,OAAO,GAAG,CAAC;CACzD;CAEA,IAAI,KAAiC;EACpC,OAAO,KAAKA,QAAQ,cAAc,MAAM,EAAE,OAAO,GAAG,CAAqB;CAC1E;CAEA,UAAuD;EACtD,OAAO,KAAKC,MAAM,CAAC,CAAC,MAClB,OACA,IAAI,SAAS,SAAS,WAAW;GAChC,MAAM,KAAK,GAAG,YAAY,KAAKF,YAAY,UAAU;GACrD,MAAM,cAAc,GAAG,YAAY,KAAKA,UAAU;GAClD,MAAM,UAAoB,CAAC;GAC3B,MAAM,SAAS,YAAY,WAAW;GACtC,OAAO,kBAAkB;IACxB,MAAM,IAAI,OAAO;IACjB,IAAI,GAAG;KACN,QAAQ,KAAK,CAAC,EAAE,KAAU,EAAE,KAAU,CAAC;KACvC,EAAE,SAAS;IACZ;GACD;GACA,GAAG,mBAAmB,QAAQ,OAAO;GACrC,GAAG,gBAAgB,OAAO,GAAG,KAAK;EACnC,CAAC,CACH;CACD;CAEA,QAA8B;EAC7B,IAAI,CAAC,KAAKG,KACT,KAAKA,MAAM,IAAI,SAAS,SAAS,WAAW;GAC3C,MAAM,UAAU,UAAU,KAAK,KAAKJ,OAAO;GAC3C,QAAQ,wBAAwB;IAC/B,IAAI,CAAC,QAAQ,OAAO,iBAAiB,SAAS,KAAKC,UAAU,GAC5D,QAAQ,OAAO,kBAAkB,KAAKA,UAAU;GAElD;GACA,QAAQ,kBAAkB,QAAQ,QAAQ,MAAM;GAChD,QAAQ,gBAAgB;IACvB,KAAKG,MAAM;IACX,OAAO,QAAQ,KAAK;GACrB;EACD,CAAC;EAEF,OAAO,KAAKA;CACb;CAEA,QACC,MACA,IACa;EACb,OAAO,KAAKD,MAAM,CAAC,CAAC,MAClB,OACA,IAAI,SAAS,SAAS,WAAW;GAChC,MAAM,KAAK,GAAG,YAAY,KAAKF,YAAY,IAAI;GAC/C,MAAM,MAAM,GAAG,GAAG,YAAY,KAAKA,UAAU,CAAC;GAC9C,GAAG,mBAAmB,QAAQ,IAAI,MAAM;GACxC,GAAG,gBAAgB,OAAO,GAAG,KAAK;GAClC,GAAG,gBAAgB,OAAO,GAAG,KAAK;EACnC,CAAC,CACH;CACD;AACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"in-memory-adapter.d.mts","names":[],"sources":["../../src/adapters/in-memory-adapter.ts"],"mappings":";;;;cASa,qBAAA,SAA8B,iBAAA;EAAA,SACjC,EAAA;EAAA,SACA,IAAA;EAEH,UAAA,CAAA,GAAc,OAAA;EAId,aAAA,CAAc,OAAA,GAAU,oBAAA,GAAuB,OAAA,CAAQ,cAAA;EAUvD,aAAA,CAAc,OAAA,EAAS,oBAAA,GAAuB,OAAA,CAAQ,cAAA;EAgBtD,aAAA,CAAc,OAAA,UAAiB,KAAA,WAAgB,OAAA;EAI/C,aAAA,CAAc,OAAA,WAAkB,OAAA;AAAA"}
1
+ {"version":3,"file":"in-memory-adapter.d.mts","names":[],"sources":["../../src/adapters/in-memory-adapter.ts"],"mappings":";;;;cASa,qBAAA,SAA8B,iBAAA;EAAA,SACjC,EAAA;EAAA,SACA,IAAA;EAEH,UAAA,IAAc,OAAA;EAId,aAAA,CAAc,OAAA,GAAU,oBAAA,GAAuB,OAAA,CAAQ,cAAA;EAUvD,aAAA,CAAc,OAAA,EAAS,oBAAA,GAAuB,OAAA,CAAQ,cAAA;EAgBtD,aAAA,CAAc,OAAA,UAAiB,KAAA,WAAgB,OAAA;EAI/C,aAAA,CAAc,OAAA,WAAkB,OAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"in-memory-adapter.mjs","names":[],"sources":["../../src/adapters/in-memory-adapter.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';\n\nimport type { CreateAccountOptions, ImportAccountOptions, ManagedAccount } from '../types.js';\nimport { BaseSignerAdapter } from './base-adapter.js';\nimport { buildManagedAccount } from './build-managed-account.js';\n\nexport class InMemorySignerAdapter extends BaseSignerAdapter {\n\treadonly id = 'in-memory';\n\treadonly name = 'In-Memory Signer';\n\n\tasync initialize(): Promise<void> {\n\t\t// No-op for in-memory adapter\n\t}\n\n\tasync createAccount(options?: CreateAccountOptions): Promise<ManagedAccount> {\n\t\tconst keypair = new Ed25519Keypair();\n\t\tconst address = keypair.getPublicKey().toSuiAddress();\n\t\tconst label = options?.label ?? this.getDefaultLabel();\n\n\t\tconst managedAccount = buildManagedAccount(keypair, address, label);\n\t\tthis.addAccount(managedAccount);\n\t\treturn managedAccount;\n\t}\n\n\tasync importAccount(options: ImportAccountOptions): Promise<ManagedAccount> {\n\t\tconst signer = options.signer;\n\t\tif (!signer) {\n\t\t\tthrow new Error('In-memory adapter requires a signer to import');\n\t\t}\n\t\tconst address = signer.toSuiAddress();\n\n\t\tconst existing = this.getAccount(address);\n\t\tif (existing) return existing;\n\n\t\tconst label = options.label ?? this.getDefaultLabel();\n\t\tconst managedAccount = buildManagedAccount(signer, address, label);\n\t\tthis.addAccount(managedAccount);\n\t\treturn managedAccount;\n\t}\n\n\tasync renameAccount(address: string, label: string): Promise<boolean> {\n\t\treturn this._performRename(address, label);\n\t}\n\n\tasync removeAccount(address: string): Promise<boolean> {\n\t\treturn this.removeAccountByAddress(address);\n\t}\n}\n"],"mappings":";;;;AASA,IAAa,wBAAb,cAA2C,kBAAkB;;;YAC9C;cACE;;CAEhB,MAAM,aAA4B;CAIlC,MAAM,cAAc,SAAyD;EAC5E,MAAM,UAAU,IAAI,gBAAgB;EAIpC,MAAM,iBAAiB,oBAAoB,SAH3B,QAAQ,cAAc,CAAC,cAGa,EAFtC,SAAS,SAAS,KAAK,iBAAiB,CAEa;AACnE,OAAK,WAAW,eAAe;AAC/B,SAAO;;CAGR,MAAM,cAAc,SAAwD;EAC3E,MAAM,SAAS,QAAQ;AACvB,MAAI,CAAC,OACJ,OAAM,IAAI,MAAM,gDAAgD;EAEjE,MAAM,UAAU,OAAO,cAAc;EAErC,MAAM,WAAW,KAAK,WAAW,QAAQ;AACzC,MAAI,SAAU,QAAO;EAGrB,MAAM,iBAAiB,oBAAoB,QAAQ,SADrC,QAAQ,SAAS,KAAK,iBAAiB,CACa;AAClE,OAAK,WAAW,eAAe;AAC/B,SAAO;;CAGR,MAAM,cAAc,SAAiB,OAAiC;AACrE,SAAO,KAAK,eAAe,SAAS,MAAM;;CAG3C,MAAM,cAAc,SAAmC;AACtD,SAAO,KAAK,uBAAuB,QAAQ"}
1
+ {"version":3,"file":"in-memory-adapter.mjs","names":[],"sources":["../../src/adapters/in-memory-adapter.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';\n\nimport type { CreateAccountOptions, ImportAccountOptions, ManagedAccount } from '../types.js';\nimport { BaseSignerAdapter } from './base-adapter.js';\nimport { buildManagedAccount } from './build-managed-account.js';\n\nexport class InMemorySignerAdapter extends BaseSignerAdapter {\n\treadonly id = 'in-memory';\n\treadonly name = 'In-Memory Signer';\n\n\tasync initialize(): Promise<void> {\n\t\t// No-op for in-memory adapter\n\t}\n\n\tasync createAccount(options?: CreateAccountOptions): Promise<ManagedAccount> {\n\t\tconst keypair = new Ed25519Keypair();\n\t\tconst address = keypair.getPublicKey().toSuiAddress();\n\t\tconst label = options?.label ?? this.getDefaultLabel();\n\n\t\tconst managedAccount = buildManagedAccount(keypair, address, label);\n\t\tthis.addAccount(managedAccount);\n\t\treturn managedAccount;\n\t}\n\n\tasync importAccount(options: ImportAccountOptions): Promise<ManagedAccount> {\n\t\tconst signer = options.signer;\n\t\tif (!signer) {\n\t\t\tthrow new Error('In-memory adapter requires a signer to import');\n\t\t}\n\t\tconst address = signer.toSuiAddress();\n\n\t\tconst existing = this.getAccount(address);\n\t\tif (existing) return existing;\n\n\t\tconst label = options.label ?? this.getDefaultLabel();\n\t\tconst managedAccount = buildManagedAccount(signer, address, label);\n\t\tthis.addAccount(managedAccount);\n\t\treturn managedAccount;\n\t}\n\n\tasync renameAccount(address: string, label: string): Promise<boolean> {\n\t\treturn this._performRename(address, label);\n\t}\n\n\tasync removeAccount(address: string): Promise<boolean> {\n\t\treturn this.removeAccountByAddress(address);\n\t}\n}\n"],"mappings":";;;;AASA,IAAa,wBAAb,cAA2C,kBAAkB;;;YAC9C;cACE;;CAEhB,MAAM,aAA4B,CAElC;CAEA,MAAM,cAAc,SAAyD;EAC5E,MAAM,UAAU,IAAI,eAAe;EAInC,MAAM,iBAAiB,oBAAoB,SAH3B,QAAQ,aAAa,CAAC,CAAC,aAGa,GAFtC,SAAS,SAAS,KAAK,gBAAgB,CAEa;EAClE,KAAK,WAAW,cAAc;EAC9B,OAAO;CACR;CAEA,MAAM,cAAc,SAAwD;EAC3E,MAAM,SAAS,QAAQ;EACvB,IAAI,CAAC,QACJ,MAAM,IAAI,MAAM,+CAA+C;EAEhE,MAAM,UAAU,OAAO,aAAa;EAEpC,MAAM,WAAW,KAAK,WAAW,OAAO;EACxC,IAAI,UAAU,OAAO;EAGrB,MAAM,iBAAiB,oBAAoB,QAAQ,SADrC,QAAQ,SAAS,KAAK,gBAAgB,CACa;EACjE,KAAK,WAAW,cAAc;EAC9B,OAAO;CACR;CAEA,MAAM,cAAc,SAAiB,OAAiC;EACrE,OAAO,KAAK,eAAe,SAAS,KAAK;CAC1C;CAEA,MAAM,cAAc,SAAmC;EACtD,OAAO,KAAK,uBAAuB,OAAO;CAC3C;AACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"passkey-adapter.d.mts","names":[],"sources":["../../src/adapters/passkey-adapter.ts"],"mappings":";;;;;;;AAgCA;;;;;cAAa,oBAAA,SAA6B,iBAAA;EAAA;WAChC,EAAA;EAAA,SACA,IAAA;EAAA,SACA,aAAA;cAKG,OAAA;IAAY,MAAA;IAAiB,SAAA;IAAoB,QAAA,GAAW,eAAA;EAAA;EAiBlE,UAAA,CAAA,GAAc,OAAA;EAAA,UAsBD,eAAA,CAAA;EAIb,aAAA,CAAc,OAAA,GAAU,oBAAA,GAAoB,OAAA,CAAA,cAAA;EAY5C,aAAA,CAAc,OAAA,UAAiB,KAAA,WAAgB,OAAA;EAM/C,aAAA,CAAc,OAAA,WAAkB,OAAA;AAAA"}
1
+ {"version":3,"file":"passkey-adapter.d.mts","names":[],"sources":["../../src/adapters/passkey-adapter.ts"],"mappings":";;;;;;;AAgCA;;;;;cAAa,oBAAA,SAA6B,iBAAA;EAAA;WAChC,EAAA;EAAA,SACA,IAAA;EAAA,SACA,aAAA;cAKG,OAAA;IAAY,MAAA;IAAiB,SAAA;IAAoB,QAAA,GAAW,eAAA;EAAA;EAiBlE,UAAA,IAAc,OAAA;EAAA,UAsBD,eAAA;EAIb,aAAA,CAAc,OAAA,GAAU,oBAAA,GAAoB,OAAA,CAAA,cAAA;EAY5C,aAAA,CAAc,OAAA,UAAiB,KAAA,WAAgB,OAAA;EAM/C,aAAA,CAAc,OAAA,WAAkB,OAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"passkey-adapter.mjs","names":["#store","#provider","#getProvider","#saveMeta"],"sources":["../../src/adapters/passkey-adapter.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { PasskeyKeypair } from '@mysten/sui/keypairs/passkey';\nimport type { PasskeyProvider } from '@mysten/sui/keypairs/passkey';\n\nimport type { CreateAccountOptions } from '../types.js';\nimport { BaseSignerAdapter } from './base-adapter.js';\nimport { buildManagedAccount } from './build-managed-account.js';\nimport { IDBStore } from './idb-store.js';\n\n/** Metadata stored in IndexedDB for each passkey account. */\ninterface StoredPasskeyMeta {\n\taddress: string;\n\tlabel: string;\n\t/** Compressed secp256r1 public key (33 bytes, stored as number array). */\n\tpublicKeyBytes: number[];\n\t/** WebAuthn credential ID (stored as number array). Used to constrain credential selection during signing. */\n\tcredentialId?: number[];\n}\n\nconst DEFAULT_DB_NAME = 'dev-wallet-passkey';\nconst DEFAULT_STORE_NAME = 'accounts';\nconst META_KEY = '__passkey_accounts__';\n\n/**\n * Signer adapter backed by WebAuthn passkeys. Each signing operation triggers a\n * browser biometric/PIN prompt. Account metadata is persisted in IndexedDB;\n * private keys are managed by the browser/OS and never enter JavaScript.\n *\n * Browser only — requires `navigator.credentials` and `indexedDB`.\n */\nexport class PasskeySignerAdapter extends BaseSignerAdapter {\n\treadonly id = 'passkey';\n\treadonly name = 'Passkey Signer';\n\treadonly allowAutoSign = false;\n\n\t#store: IDBStore;\n\t#provider: PasskeyProvider | null;\n\n\tconstructor(options?: { dbName?: string; storeName?: string; provider?: PasskeyProvider }) {\n\t\tsuper();\n\t\tthis.#store = new IDBStore(\n\t\t\toptions?.dbName ?? DEFAULT_DB_NAME,\n\t\t\toptions?.storeName ?? DEFAULT_STORE_NAME,\n\t\t);\n\t\t// Allow injecting a custom provider (useful for testing)\n\t\tthis.#provider = options?.provider ?? null;\n\t}\n\n\t#getProvider(): PasskeyProvider {\n\t\tif (!this.#provider) {\n\t\t\tthrow new Error('PasskeySignerAdapter not initialized. Call initialize() first.');\n\t\t}\n\t\treturn this.#provider;\n\t}\n\n\tasync initialize(): Promise<void> {\n\t\tif (!this.#provider) {\n\t\t\tconst { BrowserPasskeyProvider } = await import('@mysten/sui/keypairs/passkey');\n\t\t\tthis.#provider = new BrowserPasskeyProvider('Dev Wallet Passkey', {\n\t\t\t\trp: { id: globalThis.location?.hostname },\n\t\t\t});\n\t\t}\n\n\t\tconst stored = await this.#store.get<StoredPasskeyMeta[]>(META_KEY);\n\t\tif (!stored) return;\n\n\t\tconst provider = this.#getProvider();\n\t\tthis.setInitialAccounts(\n\t\t\tstored.map((meta) => {\n\t\t\t\tconst publicKeyBytes = new Uint8Array(meta.publicKeyBytes);\n\t\t\t\tconst credentialId = meta.credentialId ? new Uint8Array(meta.credentialId) : undefined;\n\t\t\t\tconst signer = new PasskeyKeypair(publicKeyBytes, provider, credentialId);\n\t\t\t\treturn buildManagedAccount(signer, meta.address, meta.label);\n\t\t\t}),\n\t\t);\n\t}\n\n\tprotected override getDefaultLabel(): string {\n\t\treturn `Passkey Account ${this.getAccounts().length + 1}`;\n\t}\n\n\tasync createAccount(options?: CreateAccountOptions) {\n\t\tconst signer = await PasskeyKeypair.getPasskeyInstance(this.#getProvider());\n\t\tconst address = signer.toSuiAddress();\n\t\tconst label = options?.label ?? this.getDefaultLabel();\n\n\t\tconst managedAccount = buildManagedAccount(signer, address, label);\n\t\tthis.addAccount(managedAccount);\n\t\tawait this.#saveMeta();\n\n\t\treturn managedAccount;\n\t}\n\n\tasync renameAccount(address: string, label: string): Promise<boolean> {\n\t\tif (!this._performRename(address, label)) return false;\n\t\tawait this.#saveMeta();\n\t\treturn true;\n\t}\n\n\tasync removeAccount(address: string): Promise<boolean> {\n\t\tif (!this.removeAccountByAddress(address)) return false;\n\t\tawait this.#saveMeta();\n\t\treturn true;\n\t}\n\n\tasync #saveMeta(): Promise<void> {\n\t\tconst meta: StoredPasskeyMeta[] = this.getAccounts().map((a) => {\n\t\t\tconst credentialId = (a.signer as PasskeyKeypair).getCredentialId?.();\n\t\t\treturn {\n\t\t\t\taddress: a.address,\n\t\t\t\tlabel: a.label,\n\t\t\t\tpublicKeyBytes: [...a.signer.getPublicKey().toRawBytes()],\n\t\t\t\t...(credentialId && { credentialId: [...credentialId] }),\n\t\t\t};\n\t\t});\n\t\tawait this.#store.set(META_KEY, meta);\n\t}\n}\n"],"mappings":";;;;;AAqBA,MAAM,kBAAkB;AACxB,MAAM,qBAAqB;AAC3B,MAAM,WAAW;;;;;;;;AASjB,IAAa,uBAAb,cAA0C,kBAAkB;CAK3D;CACA;CAEA,YAAY,SAA+E;AAC1F,SAAO;YARM;cACE;uBACS;AAOxB,QAAA,QAAc,IAAI,SACjB,SAAS,UAAU,iBACnB,SAAS,aAAa,mBACtB;AAED,QAAA,WAAiB,SAAS,YAAY;;CAGvC,eAAgC;AAC/B,MAAI,CAAC,MAAA,SACJ,OAAM,IAAI,MAAM,iEAAiE;AAElF,SAAO,MAAA;;CAGR,MAAM,aAA4B;AACjC,MAAI,CAAC,MAAA,UAAgB;GACpB,MAAM,EAAE,2BAA2B,MAAM,OAAO;AAChD,SAAA,WAAiB,IAAI,uBAAuB,sBAAsB,EACjE,IAAI,EAAE,IAAI,WAAW,UAAU,UAAU,EACzC,CAAC;;EAGH,MAAM,SAAS,MAAM,MAAA,MAAY,IAAyB,SAAS;AACnE,MAAI,CAAC,OAAQ;EAEb,MAAM,WAAW,MAAA,aAAmB;AACpC,OAAK,mBACJ,OAAO,KAAK,SAAS;AAIpB,UAAO,oBAAoB,IADR,eAAe,IAFP,WAAW,KAAK,eAET,EAAgB,UAD7B,KAAK,eAAe,IAAI,WAAW,KAAK,aAAa,GAAG,OAElD,EAAQ,KAAK,SAAS,KAAK,MAAM;IAC3D,CACF;;CAGF,kBAA6C;AAC5C,SAAO,mBAAmB,KAAK,aAAa,CAAC,SAAS;;CAGvD,MAAM,cAAc,SAAgC;EACnD,MAAM,SAAS,MAAM,eAAe,mBAAmB,MAAA,aAAmB,CAAC;EAI3E,MAAM,iBAAiB,oBAAoB,QAH3B,OAAO,cAG4B,EAFrC,SAAS,SAAS,KAAK,iBAAiB,CAEY;AAClE,OAAK,WAAW,eAAe;AAC/B,QAAM,MAAA,UAAgB;AAEtB,SAAO;;CAGR,MAAM,cAAc,SAAiB,OAAiC;AACrE,MAAI,CAAC,KAAK,eAAe,SAAS,MAAM,CAAE,QAAO;AACjD,QAAM,MAAA,UAAgB;AACtB,SAAO;;CAGR,MAAM,cAAc,SAAmC;AACtD,MAAI,CAAC,KAAK,uBAAuB,QAAQ,CAAE,QAAO;AAClD,QAAM,MAAA,UAAgB;AACtB,SAAO;;CAGR,OAAA,WAAiC;EAChC,MAAM,OAA4B,KAAK,aAAa,CAAC,KAAK,MAAM;GAC/D,MAAM,eAAgB,EAAE,OAA0B,mBAAmB;AACrE,UAAO;IACN,SAAS,EAAE;IACX,OAAO,EAAE;IACT,gBAAgB,CAAC,GAAG,EAAE,OAAO,cAAc,CAAC,YAAY,CAAC;IACzD,GAAI,gBAAgB,EAAE,cAAc,CAAC,GAAG,aAAa,EAAE;IACvD;IACA;AACF,QAAM,MAAA,MAAY,IAAI,UAAU,KAAK"}
1
+ {"version":3,"file":"passkey-adapter.mjs","names":["#store","#provider","#getProvider","#saveMeta"],"sources":["../../src/adapters/passkey-adapter.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { PasskeyKeypair } from '@mysten/sui/keypairs/passkey';\nimport type { PasskeyProvider } from '@mysten/sui/keypairs/passkey';\n\nimport type { CreateAccountOptions } from '../types.js';\nimport { BaseSignerAdapter } from './base-adapter.js';\nimport { buildManagedAccount } from './build-managed-account.js';\nimport { IDBStore } from './idb-store.js';\n\n/** Metadata stored in IndexedDB for each passkey account. */\ninterface StoredPasskeyMeta {\n\taddress: string;\n\tlabel: string;\n\t/** Compressed secp256r1 public key (33 bytes, stored as number array). */\n\tpublicKeyBytes: number[];\n\t/** WebAuthn credential ID (stored as number array). Used to constrain credential selection during signing. */\n\tcredentialId?: number[];\n}\n\nconst DEFAULT_DB_NAME = 'dev-wallet-passkey';\nconst DEFAULT_STORE_NAME = 'accounts';\nconst META_KEY = '__passkey_accounts__';\n\n/**\n * Signer adapter backed by WebAuthn passkeys. Each signing operation triggers a\n * browser biometric/PIN prompt. Account metadata is persisted in IndexedDB;\n * private keys are managed by the browser/OS and never enter JavaScript.\n *\n * Browser only — requires `navigator.credentials` and `indexedDB`.\n */\nexport class PasskeySignerAdapter extends BaseSignerAdapter {\n\treadonly id = 'passkey';\n\treadonly name = 'Passkey Signer';\n\treadonly allowAutoSign = false;\n\n\t#store: IDBStore;\n\t#provider: PasskeyProvider | null;\n\n\tconstructor(options?: { dbName?: string; storeName?: string; provider?: PasskeyProvider }) {\n\t\tsuper();\n\t\tthis.#store = new IDBStore(\n\t\t\toptions?.dbName ?? DEFAULT_DB_NAME,\n\t\t\toptions?.storeName ?? DEFAULT_STORE_NAME,\n\t\t);\n\t\t// Allow injecting a custom provider (useful for testing)\n\t\tthis.#provider = options?.provider ?? null;\n\t}\n\n\t#getProvider(): PasskeyProvider {\n\t\tif (!this.#provider) {\n\t\t\tthrow new Error('PasskeySignerAdapter not initialized. Call initialize() first.');\n\t\t}\n\t\treturn this.#provider;\n\t}\n\n\tasync initialize(): Promise<void> {\n\t\tif (!this.#provider) {\n\t\t\tconst { BrowserPasskeyProvider } = await import('@mysten/sui/keypairs/passkey');\n\t\t\tthis.#provider = new BrowserPasskeyProvider('Dev Wallet Passkey', {\n\t\t\t\trp: { id: globalThis.location?.hostname },\n\t\t\t});\n\t\t}\n\n\t\tconst stored = await this.#store.get<StoredPasskeyMeta[]>(META_KEY);\n\t\tif (!stored) return;\n\n\t\tconst provider = this.#getProvider();\n\t\tthis.setInitialAccounts(\n\t\t\tstored.map((meta) => {\n\t\t\t\tconst publicKeyBytes = new Uint8Array(meta.publicKeyBytes);\n\t\t\t\tconst credentialId = meta.credentialId ? new Uint8Array(meta.credentialId) : undefined;\n\t\t\t\tconst signer = new PasskeyKeypair(publicKeyBytes, provider, credentialId);\n\t\t\t\treturn buildManagedAccount(signer, meta.address, meta.label);\n\t\t\t}),\n\t\t);\n\t}\n\n\tprotected override getDefaultLabel(): string {\n\t\treturn `Passkey Account ${this.getAccounts().length + 1}`;\n\t}\n\n\tasync createAccount(options?: CreateAccountOptions) {\n\t\tconst signer = await PasskeyKeypair.getPasskeyInstance(this.#getProvider());\n\t\tconst address = signer.toSuiAddress();\n\t\tconst label = options?.label ?? this.getDefaultLabel();\n\n\t\tconst managedAccount = buildManagedAccount(signer, address, label);\n\t\tthis.addAccount(managedAccount);\n\t\tawait this.#saveMeta();\n\n\t\treturn managedAccount;\n\t}\n\n\tasync renameAccount(address: string, label: string): Promise<boolean> {\n\t\tif (!this._performRename(address, label)) return false;\n\t\tawait this.#saveMeta();\n\t\treturn true;\n\t}\n\n\tasync removeAccount(address: string): Promise<boolean> {\n\t\tif (!this.removeAccountByAddress(address)) return false;\n\t\tawait this.#saveMeta();\n\t\treturn true;\n\t}\n\n\tasync #saveMeta(): Promise<void> {\n\t\tconst meta: StoredPasskeyMeta[] = this.getAccounts().map((a) => {\n\t\t\tconst credentialId = (a.signer as PasskeyKeypair).getCredentialId?.();\n\t\t\treturn {\n\t\t\t\taddress: a.address,\n\t\t\t\tlabel: a.label,\n\t\t\t\tpublicKeyBytes: [...a.signer.getPublicKey().toRawBytes()],\n\t\t\t\t...(credentialId && { credentialId: [...credentialId] }),\n\t\t\t};\n\t\t});\n\t\tawait this.#store.set(META_KEY, meta);\n\t}\n}\n"],"mappings":";;;;;AAqBA,MAAM,kBAAkB;AACxB,MAAM,qBAAqB;AAC3B,MAAM,WAAW;;;;;;;;AASjB,IAAa,uBAAb,cAA0C,kBAAkB;CAK3D;CACA;CAEA,YAAY,SAA+E;EAC1F,MAAM;YARO;cACE;uBACS;EAOxB,KAAKA,SAAS,IAAI,SACjB,SAAS,UAAU,iBACnB,SAAS,aAAa,kBACvB;EAEA,KAAKC,YAAY,SAAS,YAAY;CACvC;CAEA,eAAgC;EAC/B,IAAI,CAAC,KAAKA,WACT,MAAM,IAAI,MAAM,gEAAgE;EAEjF,OAAO,KAAKA;CACb;CAEA,MAAM,aAA4B;EACjC,IAAI,CAAC,KAAKA,WAAW;GACpB,MAAM,EAAE,2BAA2B,MAAM,OAAO;GAChD,KAAKA,YAAY,IAAI,uBAAuB,sBAAsB,EACjE,IAAI,EAAE,IAAI,WAAW,UAAU,SAAS,EACzC,CAAC;EACF;EAEA,MAAM,SAAS,MAAM,KAAKD,OAAO,IAAyB,QAAQ;EAClE,IAAI,CAAC,QAAQ;EAEb,MAAM,WAAW,KAAKE,aAAa;EACnC,KAAK,mBACJ,OAAO,KAAK,SAAS;GAIpB,OAAO,oBAAoB,IADR,eAAe,IAFP,WAAW,KAAK,cAET,GAAgB,UAD7B,KAAK,eAAe,IAAI,WAAW,KAAK,YAAY,IAAI,MAElD,GAAQ,KAAK,SAAS,KAAK,KAAK;EAC5D,CAAC,CACF;CACD;CAEA,kBAA6C;EAC5C,OAAO,mBAAmB,KAAK,YAAY,CAAC,CAAC,SAAS;CACvD;CAEA,MAAM,cAAc,SAAgC;EACnD,MAAM,SAAS,MAAM,eAAe,mBAAmB,KAAKA,aAAa,CAAC;EAI1E,MAAM,iBAAiB,oBAAoB,QAH3B,OAAO,aAG4B,GAFrC,SAAS,SAAS,KAAK,gBAAgB,CAEY;EACjE,KAAK,WAAW,cAAc;EAC9B,MAAM,KAAKC,UAAU;EAErB,OAAO;CACR;CAEA,MAAM,cAAc,SAAiB,OAAiC;EACrE,IAAI,CAAC,KAAK,eAAe,SAAS,KAAK,GAAG,OAAO;EACjD,MAAM,KAAKA,UAAU;EACrB,OAAO;CACR;CAEA,MAAM,cAAc,SAAmC;EACtD,IAAI,CAAC,KAAK,uBAAuB,OAAO,GAAG,OAAO;EAClD,MAAM,KAAKA,UAAU;EACrB,OAAO;CACR;CAEA,MAAMA,YAA2B;EAChC,MAAM,OAA4B,KAAK,YAAY,CAAC,CAAC,KAAK,MAAM;GAC/D,MAAM,eAAgB,EAAE,OAA0B,kBAAkB;GACpE,OAAO;IACN,SAAS,EAAE;IACX,OAAO,EAAE;IACT,gBAAgB,CAAC,GAAG,EAAE,OAAO,aAAa,CAAC,CAAC,WAAW,CAAC;IACxD,GAAI,gBAAgB,EAAE,cAAc,CAAC,GAAG,YAAY,EAAE;GACvD;EACD,CAAC;EACD,MAAM,KAAKH,OAAO,IAAI,UAAU,IAAI;CACrC;AACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"remote-cli-adapter.d.mts","names":[],"sources":["../../src/adapters/remote-cli-adapter.ts"],"mappings":";;;;;;AAqEA;;;;;;cAAa,cAAA,SAAuB,MAAA;EAAA;cAOvB,OAAA;IACX,OAAA;IACA,SAAA,EAAW,SAAA;IACX,MAAA,EAAQ,eAAA;IACR,YAAA;IACA,SAAA;EAAA;EAUK,IAAA,CAAK,MAAA,EAAQ,UAAA,GAAa,OAAA,CAAQ,UAAA,CAAW,WAAA;EAOnD,YAAA,CAAA,GAAgB,eAAA;EAIhB,YAAA,CAAA,GAAgB,SAAA;EAIP,YAAA,CAAA;EAIM,eAAA,CAAgB,KAAA,EAAO,UAAA,GAAU,OAAA;;;;EA8BjC,mBAAA,CAAoB,MAAA,EAAQ,UAAA,GAAa,OAAA;AAAA;;;;;;;;cAgB5C,gBAAA,SAAyB,iBAAA;EAAA;WAC5B,EAAA;EAAA,SACA,IAAA;EAAA,SACA,aAAA;EAAA,UAEU,eAAA,CAAA;EAAA,OAOZ,UAAA,CAAW,MAAA;EAAA,WAKP,WAAA,CAAA;EAAA,WAIA,SAAA,CAAA;cASC,OAAA;IAAY,YAAA;IAAuB,KAAA;EAAA;EAAA,IAgB3C,QAAA,CAAA;EAIE,UAAA,CAAA,GAAc,OAAA;EAyBd,qBAAA,CAAA,GAAyB,OAAA,CAC9B,KAAA;IAAQ,OAAA;IAAiB,MAAA;IAAgB,KAAA;EAAA;EAgBpC,aAAA,CAAc,OAAA,EAAS,oBAAA,GAAuB,OAAA,CAAQ,cAAA;EA2BtD,aAAA,CAAc,OAAA,WAAkB,OAAA;EAQ7B,OAAA,CAAA;AAAA"}
1
+ {"version":3,"file":"remote-cli-adapter.d.mts","names":[],"sources":["../../src/adapters/remote-cli-adapter.ts"],"mappings":";;;;;;AAqEA;;;;;;cAAa,cAAA,SAAuB,MAAA;EAAA;cAOvB,OAAA;IACX,OAAA;IACA,SAAA,EAAW,SAAA;IACX,MAAA,EAAQ,eAAA;IACR,YAAA;IACA,SAAA;EAAA;EAUK,IAAA,CAAK,MAAA,EAAQ,UAAA,GAAa,OAAA,CAAQ,UAAA,CAAW,WAAA;EAOnD,YAAA,IAAgB,eAAA;EAIhB,YAAA,IAAgB,SAAA;EAIP,YAAA;EAIM,eAAA,CAAgB,KAAA,EAAO,UAAA,GAAU,OAAA;;;;EA8BjC,mBAAA,CAAoB,MAAA,EAAQ,UAAA,GAAa,OAAA;AAAA;;;;;;;;cAgB5C,gBAAA,SAAyB,iBAAA;EAAA;WAC5B,EAAA;EAAA,SACA,IAAA;EAAA,SACA,aAAA;EAAA,UAEU,eAAA;EAAA,OAOZ,UAAA,CAAW,MAAA;EAAA,WAKP,WAAA;EAAA,WAIA,SAAA;cASC,OAAA;IAAY,YAAA;IAAuB,KAAA;EAAA;EAAA,IAgB3C,QAAA;EAIE,UAAA,IAAc,OAAA;EAyBd,qBAAA,IAAyB,OAAA,CAC9B,KAAA;IAAQ,OAAA;IAAiB,MAAA;IAAgB,KAAA;EAAA;EAgBpC,aAAA,CAAc,OAAA,EAAS,oBAAA,GAAuB,OAAA,CAAQ,cAAA;EA2BtD,aAAA,CAAc,OAAA,WAAkB,OAAA;EAQ7B,OAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"remote-cli-adapter.mjs","names":["#address","#publicKey","#scheme","#serverOrigin","#authToken","#fetchServerAccounts","#restoreImportedAccounts","#allServerAccounts","#serverAccountToManaged","#saveImportedAddresses","#lastFetch","#authHeaders","#loadImportedAddresses"],"sources":["../../src/adapters/remote-cli-adapter.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Signer } from '@mysten/sui/cryptography';\nimport type { PublicKey, SignatureScheme } from '@mysten/sui/cryptography';\nimport { Ed25519PublicKey } from '@mysten/sui/keypairs/ed25519';\nimport { Secp256k1PublicKey } from '@mysten/sui/keypairs/secp256k1';\nimport { Secp256r1PublicKey } from '@mysten/sui/keypairs/secp256r1';\nimport { fromBase64, toBase64 } from '@mysten/sui/utils';\nimport type { ImportAccountOptions, ManagedAccount } from '../types.js';\nimport { BaseSignerAdapter } from './base-adapter.js';\nimport { buildManagedAccount } from './build-managed-account.js';\n\n/**\n * Wallet features advertised by CLI-backed accounts.\n * Personal message signing is not supported because the `sui keytool sign`\n * command only accepts BCS-serialized TransactionData.\n */\nconst CLI_WALLET_FEATURES = ['sui:signTransaction', 'sui:signAndExecuteTransaction'] as const;\n\n/** Cache TTL for the CLI server accounts fetch (milliseconds). */\nconst ACCOUNTS_CACHE_TTL_MS = 5000;\n\n/** Thrown when the server rejects a request with a 401 or 403 status. */\nclass AuthError extends Error {\n\tconstructor() {\n\t\tsuper('Unauthorized');\n\t\tthis.name = 'AuthError';\n\t}\n}\n\n/** Map CLI `keyScheme` strings to SDK {@link SignatureScheme} values. */\nconst KEY_SCHEME_MAP: Record<string, SignatureScheme> = {\n\ted25519: 'ED25519',\n\tsecp256k1: 'Secp256k1',\n\tsecp256r1: 'Secp256r1',\n};\n\n/** Account metadata returned by the server `/api/v1/accounts` endpoint. */\ninterface ServerAccountInfo {\n\tsuiAddress: string;\n\tpublicBase64Key: string;\n\tkeyScheme: string;\n\talias: string | null;\n}\n\nfunction publicKeyFromSuiBytes(publicBase64Key: string, scheme: SignatureScheme): PublicKey {\n\tconst allBytes = fromBase64(publicBase64Key);\n\tconst rawBytes = allBytes.slice(1);\n\n\tswitch (scheme) {\n\t\tcase 'ED25519':\n\t\t\treturn new Ed25519PublicKey(rawBytes);\n\t\tcase 'Secp256k1':\n\t\t\treturn new Secp256k1PublicKey(rawBytes);\n\t\tcase 'Secp256r1':\n\t\t\treturn new Secp256r1PublicKey(rawBytes);\n\t\tdefault:\n\t\t\tthrow new Error(`Unsupported key scheme: ${scheme}`);\n\t}\n}\n\n/**\n * A {@link Signer} that delegates transaction signing to a server HTTP endpoint\n * backed by `sui keytool sign`. Private keys never enter JavaScript.\n *\n * `signPersonalMessage()` is not supported — `sui keytool sign` only accepts\n * BCS-serialized `TransactionData`.\n */\nexport class CliProxySigner extends Signer {\n\t#address: string;\n\t#publicKey: PublicKey;\n\t#scheme: SignatureScheme;\n\t#serverOrigin: string;\n\t#authToken: string | null;\n\n\tconstructor(options: {\n\t\taddress: string;\n\t\tpublicKey: PublicKey;\n\t\tscheme: SignatureScheme;\n\t\tserverOrigin: string;\n\t\tauthToken?: string | null;\n\t}) {\n\t\tsuper();\n\t\tthis.#address = options.address;\n\t\tthis.#publicKey = options.publicKey;\n\t\tthis.#scheme = options.scheme;\n\t\tthis.#serverOrigin = options.serverOrigin;\n\t\tthis.#authToken = options.authToken ?? null;\n\t}\n\n\tasync sign(_bytes: Uint8Array): Promise<Uint8Array<ArrayBuffer>> {\n\t\tthrow new Error(\n\t\t\t'CliProxySigner does not support direct digest signing. ' +\n\t\t\t\t'Transaction signing is handled via the sui CLI.',\n\t\t);\n\t}\n\n\tgetKeyScheme(): SignatureScheme {\n\t\treturn this.#scheme;\n\t}\n\n\tgetPublicKey(): PublicKey {\n\t\treturn this.#publicKey;\n\t}\n\n\toverride toSuiAddress(): string {\n\t\treturn this.#address;\n\t}\n\n\toverride async signTransaction(bytes: Uint8Array) {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\t\tif (this.#authToken) {\n\t\t\theaders['Authorization'] = `Bearer ${this.#authToken}`;\n\t\t}\n\n\t\tconst res = await fetch(`${this.#serverOrigin}/api/v1/sign-transaction`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders,\n\t\t\tbody: JSON.stringify({\n\t\t\t\taddress: this.#address,\n\t\t\t\ttxBytes: toBase64(bytes),\n\t\t\t}),\n\t\t});\n\n\t\tif (!res.ok) {\n\t\t\tconst body = await res.json().catch(() => ({}));\n\t\t\tthrow new Error(`Transaction signing failed: ${body.error ?? res.statusText}`);\n\t\t}\n\n\t\tconst { suiSignature } = await res.json();\n\t\tif (typeof suiSignature !== 'string' || suiSignature.length === 0) {\n\t\t\tthrow new Error('Transaction signing failed: server returned invalid signature');\n\t\t}\n\t\treturn {\n\t\t\tbytes: toBase64(bytes),\n\t\t\tsignature: suiSignature,\n\t\t};\n\t}\n\n\toverride async signPersonalMessage(_bytes: Uint8Array): Promise<never> {\n\t\tthrow new Error(\n\t\t\t'Personal message signing is not supported in CLI mode. ' +\n\t\t\t\t'The sui keytool sign command only supports TransactionData. ' +\n\t\t\t\t'Use --adapter=webcrypto or --adapter=memory for personal message signing.',\n\t\t);\n\t}\n}\n\n/**\n * {@link SignerAdapter} that delegates signing to a server running the `sui` CLI.\n *\n * Authentication uses a token-in-URL approach: the CLI server prints a URL with\n * a token, which gets stored in `localStorage` for subsequent page loads. Imported\n * account addresses are also persisted so they survive CLI restarts.\n */\nexport class RemoteCliAdapter extends BaseSignerAdapter {\n\treadonly id = 'remote-cli';\n\treadonly name = 'Remote CLI Signer';\n\treadonly allowAutoSign = false;\n\n\tprotected override getDefaultLabel(): string {\n\t\treturn `CLI Account ${this.getAccounts().length + 1}`;\n\t}\n\n\t// Keys are scoped per origin to prevent cross-site leakage on shared localhost setups.\n\t// localStorage is used rather than sessionStorage because popup wallet flows\n\t// (DevWalletClient) may open new tabs that need access to the token.\n\tstatic storageKey(suffix: string): string {\n\t\tconst origin = typeof location !== 'undefined' ? location.origin : 'unknown';\n\t\treturn `dev-wallet:${suffix}:${origin}`;\n\t}\n\n\tstatic get STORAGE_KEY(): string {\n\t\treturn RemoteCliAdapter.storageKey('cli-imported-addresses');\n\t}\n\n\tstatic get TOKEN_KEY(): string {\n\t\treturn RemoteCliAdapter.storageKey('cli-token');\n\t}\n\n\t#serverOrigin: string;\n\t#authToken: string | null = null;\n\t#allServerAccounts: ServerAccountInfo[] = [];\n\t#lastFetch: { time: number; result: ServerAccountInfo[] } | null = null;\n\n\tconstructor(options?: { serverOrigin?: string; token?: string }) {\n\t\tsuper();\n\t\tconst origin = options?.serverOrigin ?? '';\n\t\tif (origin) {\n\t\t\ttry {\n\t\t\t\tnew URL(origin);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`Invalid serverOrigin URL: \"${origin}\"`);\n\t\t\t}\n\t\t}\n\t\tthis.#serverOrigin = origin;\n\t\tif (options?.token) {\n\t\t\tthis.#authToken = options.token;\n\t\t}\n\t}\n\n\tget isPaired(): boolean {\n\t\treturn this.#authToken !== null;\n\t}\n\n\tasync initialize(): Promise<void> {\n\t\tif (!this.#authToken && typeof localStorage !== 'undefined') {\n\t\t\tconst stored = localStorage.getItem(RemoteCliAdapter.TOKEN_KEY);\n\t\t\tif (stored) {\n\t\t\t\tthis.#authToken = stored;\n\t\t\t}\n\t\t}\n\n\t\tif (this.#authToken) {\n\t\t\ttry {\n\t\t\t\tawait this.#fetchServerAccounts();\n\t\t\t\tawait this.#restoreImportedAccounts();\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof AuthError) {\n\t\t\t\t\tthis.#authToken = null;\n\t\t\t\t\tif (typeof localStorage !== 'undefined') {\n\t\t\t\t\t\tlocalStorage.removeItem(RemoteCliAdapter.TOKEN_KEY);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconsole.warn('[dev-wallet] CLI adapter initialization failed:', error);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tasync listAvailableAccounts(): Promise<\n\t\tArray<{ address: string; scheme: string; alias: string | null }>\n\t> {\n\t\tawait this.#fetchServerAccounts();\n\t\tconst importedAddresses = new Set(this._accounts.map((a) => a.address));\n\t\treturn this.#allServerAccounts\n\t\t\t.filter((info) => {\n\t\t\t\tconst scheme = KEY_SCHEME_MAP[info.keyScheme];\n\t\t\t\treturn scheme && !importedAddresses.has(info.suiAddress);\n\t\t\t})\n\t\t\t.map((info) => ({\n\t\t\t\taddress: info.suiAddress,\n\t\t\t\tscheme: info.keyScheme,\n\t\t\t\talias: info.alias,\n\t\t\t}));\n\t}\n\n\tasync importAccount(options: ImportAccountOptions): Promise<ManagedAccount> {\n\t\tconst address = options.address;\n\t\tif (!address) {\n\t\t\tthrow new Error('Remote CLI adapter requires an address to import');\n\t\t}\n\n\t\tif (this._accounts.some((a) => a.address === address)) {\n\t\t\tthrow new Error(`Account ${address} is already imported`);\n\t\t}\n\n\t\tawait this.#fetchServerAccounts();\n\t\tconst info = this.#allServerAccounts.find((a) => a.suiAddress === address);\n\t\tif (!info) {\n\t\t\tthrow new Error(`Account ${address} not found on server`);\n\t\t}\n\n\t\tconst scheme = KEY_SCHEME_MAP[info.keyScheme];\n\t\tif (!scheme) {\n\t\t\tthrow new Error(`Unsupported key scheme: ${info.keyScheme}`);\n\t\t}\n\n\t\tconst account = this.#serverAccountToManaged(info, scheme, options.label);\n\t\tthis.addAccount(account);\n\t\tthis.#saveImportedAddresses();\n\t\treturn account;\n\t}\n\n\tasync removeAccount(address: string): Promise<boolean> {\n\t\tconst removed = this.removeAccountByAddress(address);\n\t\tif (removed) {\n\t\t\tthis.#saveImportedAddresses();\n\t\t}\n\t\treturn removed;\n\t}\n\n\toverride destroy(): void {\n\t\tthis.#allServerAccounts = [];\n\t\tthis.#authToken = null;\n\t\tif (typeof localStorage !== 'undefined') {\n\t\t\tlocalStorage.removeItem(RemoteCliAdapter.TOKEN_KEY);\n\t\t\tlocalStorage.removeItem(RemoteCliAdapter.STORAGE_KEY);\n\t\t}\n\t\tsuper.destroy();\n\t}\n\n\t#authHeaders(): Record<string, string> {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\t\tif (this.#authToken) {\n\t\t\theaders['Authorization'] = `Bearer ${this.#authToken}`;\n\t\t}\n\t\treturn headers;\n\t}\n\n\tasync #fetchServerAccounts(forceRefresh = false): Promise<void> {\n\t\tif (\n\t\t\t!forceRefresh &&\n\t\t\tthis.#lastFetch &&\n\t\t\tDate.now() - this.#lastFetch.time < ACCOUNTS_CACHE_TTL_MS\n\t\t) {\n\t\t\tthis.#allServerAccounts = this.#lastFetch.result;\n\t\t\treturn;\n\t\t}\n\t\tconst res = await fetch(`${this.#serverOrigin}/api/v1/accounts`, {\n\t\t\theaders: this.#authHeaders(),\n\t\t});\n\t\tif (res.status === 401 || res.status === 403) {\n\t\t\tthrow new AuthError();\n\t\t}\n\t\tif (!res.ok) {\n\t\t\tthrow new Error(`Failed to fetch accounts: ${res.statusText}`);\n\t\t}\n\n\t\tconst { accounts } = (await res.json()) as { accounts: ServerAccountInfo[] };\n\t\tthis.#allServerAccounts = accounts;\n\t\tthis.#lastFetch = { time: Date.now(), result: accounts };\n\t}\n\n\tasync #restoreImportedAccounts(): Promise<void> {\n\t\tconst saved = this.#loadImportedAddresses();\n\t\tif (saved.length === 0) return;\n\n\t\tconst existingAddresses = new Set(this.getAccounts().map((a) => a.address));\n\t\tconst restored: ManagedAccount[] = [];\n\n\t\tfor (const address of saved) {\n\t\t\tif (existingAddresses.has(address)) continue;\n\n\t\t\tconst info = this.#allServerAccounts.find((a) => a.suiAddress === address);\n\t\t\tif (!info) continue; // Account no longer on server — skip\n\n\t\t\tconst scheme = KEY_SCHEME_MAP[info.keyScheme];\n\t\t\tif (!scheme) continue;\n\n\t\t\trestored.push(this.#serverAccountToManaged(info, scheme));\n\t\t}\n\n\t\tif (restored.length > 0) {\n\t\t\tthis.setInitialAccounts([...this.getAccounts(), ...restored]);\n\t\t}\n\t}\n\n\t#loadImportedAddresses(): string[] {\n\t\tif (typeof localStorage === 'undefined') return [];\n\t\ttry {\n\t\t\tconst raw = localStorage.getItem(RemoteCliAdapter.STORAGE_KEY);\n\t\t\tif (!raw) return [];\n\t\t\tconst parsed: unknown = JSON.parse(raw);\n\t\t\tif (!Array.isArray(parsed)) return [];\n\t\t\treturn parsed.filter((v): v is string => typeof v === 'string');\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t#saveImportedAddresses(): void {\n\t\tif (typeof localStorage === 'undefined') return;\n\t\tconst addresses = this._accounts.map((a) => a.address);\n\t\tlocalStorage.setItem(RemoteCliAdapter.STORAGE_KEY, JSON.stringify(addresses));\n\t}\n\n\t#serverAccountToManaged(\n\t\tinfo: ServerAccountInfo,\n\t\tscheme: SignatureScheme,\n\t\tlabel?: string,\n\t): ManagedAccount {\n\t\tconst publicKey = publicKeyFromSuiBytes(info.publicBase64Key, scheme);\n\n\t\tconst signer = new CliProxySigner({\n\t\t\taddress: info.suiAddress,\n\t\t\tpublicKey,\n\t\t\tscheme,\n\t\t\tserverOrigin: this.#serverOrigin,\n\t\t\tauthToken: this.#authToken,\n\t\t});\n\n\t\tconst accountLabel = label ?? info.alias ?? this.getDefaultLabel();\n\n\t\treturn buildManagedAccount(signer, info.suiAddress, accountLabel, CLI_WALLET_FEATURES);\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"remote-cli-adapter.mjs","names":["#address","#publicKey","#scheme","#serverOrigin","#authToken","#fetchServerAccounts","#restoreImportedAccounts","#allServerAccounts","#serverAccountToManaged","#saveImportedAddresses","#lastFetch","#authHeaders","#loadImportedAddresses"],"sources":["../../src/adapters/remote-cli-adapter.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Signer } from '@mysten/sui/cryptography';\nimport type { PublicKey, SignatureScheme } from '@mysten/sui/cryptography';\nimport { Ed25519PublicKey } from '@mysten/sui/keypairs/ed25519';\nimport { Secp256k1PublicKey } from '@mysten/sui/keypairs/secp256k1';\nimport { Secp256r1PublicKey } from '@mysten/sui/keypairs/secp256r1';\nimport { fromBase64, toBase64 } from '@mysten/sui/utils';\nimport type { ImportAccountOptions, ManagedAccount } from '../types.js';\nimport { BaseSignerAdapter } from './base-adapter.js';\nimport { buildManagedAccount } from './build-managed-account.js';\n\n/**\n * Wallet features advertised by CLI-backed accounts.\n * Personal message signing is not supported because the `sui keytool sign`\n * command only accepts BCS-serialized TransactionData.\n */\nconst CLI_WALLET_FEATURES = ['sui:signTransaction', 'sui:signAndExecuteTransaction'] as const;\n\n/** Cache TTL for the CLI server accounts fetch (milliseconds). */\nconst ACCOUNTS_CACHE_TTL_MS = 5000;\n\n/** Thrown when the server rejects a request with a 401 or 403 status. */\nclass AuthError extends Error {\n\tconstructor() {\n\t\tsuper('Unauthorized');\n\t\tthis.name = 'AuthError';\n\t}\n}\n\n/** Map CLI `keyScheme` strings to SDK {@link SignatureScheme} values. */\nconst KEY_SCHEME_MAP: Record<string, SignatureScheme> = {\n\ted25519: 'ED25519',\n\tsecp256k1: 'Secp256k1',\n\tsecp256r1: 'Secp256r1',\n};\n\n/** Account metadata returned by the server `/api/v1/accounts` endpoint. */\ninterface ServerAccountInfo {\n\tsuiAddress: string;\n\tpublicBase64Key: string;\n\tkeyScheme: string;\n\talias: string | null;\n}\n\nfunction publicKeyFromSuiBytes(publicBase64Key: string, scheme: SignatureScheme): PublicKey {\n\tconst allBytes = fromBase64(publicBase64Key);\n\tconst rawBytes = allBytes.slice(1);\n\n\tswitch (scheme) {\n\t\tcase 'ED25519':\n\t\t\treturn new Ed25519PublicKey(rawBytes);\n\t\tcase 'Secp256k1':\n\t\t\treturn new Secp256k1PublicKey(rawBytes);\n\t\tcase 'Secp256r1':\n\t\t\treturn new Secp256r1PublicKey(rawBytes);\n\t\tdefault:\n\t\t\tthrow new Error(`Unsupported key scheme: ${scheme}`);\n\t}\n}\n\n/**\n * A {@link Signer} that delegates transaction signing to a server HTTP endpoint\n * backed by `sui keytool sign`. Private keys never enter JavaScript.\n *\n * `signPersonalMessage()` is not supported — `sui keytool sign` only accepts\n * BCS-serialized `TransactionData`.\n */\nexport class CliProxySigner extends Signer {\n\t#address: string;\n\t#publicKey: PublicKey;\n\t#scheme: SignatureScheme;\n\t#serverOrigin: string;\n\t#authToken: string | null;\n\n\tconstructor(options: {\n\t\taddress: string;\n\t\tpublicKey: PublicKey;\n\t\tscheme: SignatureScheme;\n\t\tserverOrigin: string;\n\t\tauthToken?: string | null;\n\t}) {\n\t\tsuper();\n\t\tthis.#address = options.address;\n\t\tthis.#publicKey = options.publicKey;\n\t\tthis.#scheme = options.scheme;\n\t\tthis.#serverOrigin = options.serverOrigin;\n\t\tthis.#authToken = options.authToken ?? null;\n\t}\n\n\tasync sign(_bytes: Uint8Array): Promise<Uint8Array<ArrayBuffer>> {\n\t\tthrow new Error(\n\t\t\t'CliProxySigner does not support direct digest signing. ' +\n\t\t\t\t'Transaction signing is handled via the sui CLI.',\n\t\t);\n\t}\n\n\tgetKeyScheme(): SignatureScheme {\n\t\treturn this.#scheme;\n\t}\n\n\tgetPublicKey(): PublicKey {\n\t\treturn this.#publicKey;\n\t}\n\n\toverride toSuiAddress(): string {\n\t\treturn this.#address;\n\t}\n\n\toverride async signTransaction(bytes: Uint8Array) {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\t\tif (this.#authToken) {\n\t\t\theaders['Authorization'] = `Bearer ${this.#authToken}`;\n\t\t}\n\n\t\tconst res = await fetch(`${this.#serverOrigin}/api/v1/sign-transaction`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders,\n\t\t\tbody: JSON.stringify({\n\t\t\t\taddress: this.#address,\n\t\t\t\ttxBytes: toBase64(bytes),\n\t\t\t}),\n\t\t});\n\n\t\tif (!res.ok) {\n\t\t\tconst body = await res.json().catch(() => ({}));\n\t\t\tthrow new Error(`Transaction signing failed: ${body.error ?? res.statusText}`);\n\t\t}\n\n\t\tconst { suiSignature } = await res.json();\n\t\tif (typeof suiSignature !== 'string' || suiSignature.length === 0) {\n\t\t\tthrow new Error('Transaction signing failed: server returned invalid signature');\n\t\t}\n\t\treturn {\n\t\t\tbytes: toBase64(bytes),\n\t\t\tsignature: suiSignature,\n\t\t};\n\t}\n\n\toverride async signPersonalMessage(_bytes: Uint8Array): Promise<never> {\n\t\tthrow new Error(\n\t\t\t'Personal message signing is not supported in CLI mode. ' +\n\t\t\t\t'The sui keytool sign command only supports TransactionData. ' +\n\t\t\t\t'Use --adapter=webcrypto or --adapter=memory for personal message signing.',\n\t\t);\n\t}\n}\n\n/**\n * {@link SignerAdapter} that delegates signing to a server running the `sui` CLI.\n *\n * Authentication uses a token-in-URL approach: the CLI server prints a URL with\n * a token, which gets stored in `localStorage` for subsequent page loads. Imported\n * account addresses are also persisted so they survive CLI restarts.\n */\nexport class RemoteCliAdapter extends BaseSignerAdapter {\n\treadonly id = 'remote-cli';\n\treadonly name = 'Remote CLI Signer';\n\treadonly allowAutoSign = false;\n\n\tprotected override getDefaultLabel(): string {\n\t\treturn `CLI Account ${this.getAccounts().length + 1}`;\n\t}\n\n\t// Keys are scoped per origin to prevent cross-site leakage on shared localhost setups.\n\t// localStorage is used rather than sessionStorage because popup wallet flows\n\t// (DevWalletClient) may open new tabs that need access to the token.\n\tstatic storageKey(suffix: string): string {\n\t\tconst origin = typeof location !== 'undefined' ? location.origin : 'unknown';\n\t\treturn `dev-wallet:${suffix}:${origin}`;\n\t}\n\n\tstatic get STORAGE_KEY(): string {\n\t\treturn RemoteCliAdapter.storageKey('cli-imported-addresses');\n\t}\n\n\tstatic get TOKEN_KEY(): string {\n\t\treturn RemoteCliAdapter.storageKey('cli-token');\n\t}\n\n\t#serverOrigin: string;\n\t#authToken: string | null = null;\n\t#allServerAccounts: ServerAccountInfo[] = [];\n\t#lastFetch: { time: number; result: ServerAccountInfo[] } | null = null;\n\n\tconstructor(options?: { serverOrigin?: string; token?: string }) {\n\t\tsuper();\n\t\tconst origin = options?.serverOrigin ?? '';\n\t\tif (origin) {\n\t\t\ttry {\n\t\t\t\tnew URL(origin);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`Invalid serverOrigin URL: \"${origin}\"`);\n\t\t\t}\n\t\t}\n\t\tthis.#serverOrigin = origin;\n\t\tif (options?.token) {\n\t\t\tthis.#authToken = options.token;\n\t\t}\n\t}\n\n\tget isPaired(): boolean {\n\t\treturn this.#authToken !== null;\n\t}\n\n\tasync initialize(): Promise<void> {\n\t\tif (!this.#authToken && typeof localStorage !== 'undefined') {\n\t\t\tconst stored = localStorage.getItem(RemoteCliAdapter.TOKEN_KEY);\n\t\t\tif (stored) {\n\t\t\t\tthis.#authToken = stored;\n\t\t\t}\n\t\t}\n\n\t\tif (this.#authToken) {\n\t\t\ttry {\n\t\t\t\tawait this.#fetchServerAccounts();\n\t\t\t\tawait this.#restoreImportedAccounts();\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof AuthError) {\n\t\t\t\t\tthis.#authToken = null;\n\t\t\t\t\tif (typeof localStorage !== 'undefined') {\n\t\t\t\t\t\tlocalStorage.removeItem(RemoteCliAdapter.TOKEN_KEY);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconsole.warn('[dev-wallet] CLI adapter initialization failed:', error);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tasync listAvailableAccounts(): Promise<\n\t\tArray<{ address: string; scheme: string; alias: string | null }>\n\t> {\n\t\tawait this.#fetchServerAccounts();\n\t\tconst importedAddresses = new Set(this._accounts.map((a) => a.address));\n\t\treturn this.#allServerAccounts\n\t\t\t.filter((info) => {\n\t\t\t\tconst scheme = KEY_SCHEME_MAP[info.keyScheme];\n\t\t\t\treturn scheme && !importedAddresses.has(info.suiAddress);\n\t\t\t})\n\t\t\t.map((info) => ({\n\t\t\t\taddress: info.suiAddress,\n\t\t\t\tscheme: info.keyScheme,\n\t\t\t\talias: info.alias,\n\t\t\t}));\n\t}\n\n\tasync importAccount(options: ImportAccountOptions): Promise<ManagedAccount> {\n\t\tconst address = options.address;\n\t\tif (!address) {\n\t\t\tthrow new Error('Remote CLI adapter requires an address to import');\n\t\t}\n\n\t\tif (this._accounts.some((a) => a.address === address)) {\n\t\t\tthrow new Error(`Account ${address} is already imported`);\n\t\t}\n\n\t\tawait this.#fetchServerAccounts();\n\t\tconst info = this.#allServerAccounts.find((a) => a.suiAddress === address);\n\t\tif (!info) {\n\t\t\tthrow new Error(`Account ${address} not found on server`);\n\t\t}\n\n\t\tconst scheme = KEY_SCHEME_MAP[info.keyScheme];\n\t\tif (!scheme) {\n\t\t\tthrow new Error(`Unsupported key scheme: ${info.keyScheme}`);\n\t\t}\n\n\t\tconst account = this.#serverAccountToManaged(info, scheme, options.label);\n\t\tthis.addAccount(account);\n\t\tthis.#saveImportedAddresses();\n\t\treturn account;\n\t}\n\n\tasync removeAccount(address: string): Promise<boolean> {\n\t\tconst removed = this.removeAccountByAddress(address);\n\t\tif (removed) {\n\t\t\tthis.#saveImportedAddresses();\n\t\t}\n\t\treturn removed;\n\t}\n\n\toverride destroy(): void {\n\t\tthis.#allServerAccounts = [];\n\t\tthis.#authToken = null;\n\t\tif (typeof localStorage !== 'undefined') {\n\t\t\tlocalStorage.removeItem(RemoteCliAdapter.TOKEN_KEY);\n\t\t\tlocalStorage.removeItem(RemoteCliAdapter.STORAGE_KEY);\n\t\t}\n\t\tsuper.destroy();\n\t}\n\n\t#authHeaders(): Record<string, string> {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\t\tif (this.#authToken) {\n\t\t\theaders['Authorization'] = `Bearer ${this.#authToken}`;\n\t\t}\n\t\treturn headers;\n\t}\n\n\tasync #fetchServerAccounts(forceRefresh = false): Promise<void> {\n\t\tif (\n\t\t\t!forceRefresh &&\n\t\t\tthis.#lastFetch &&\n\t\t\tDate.now() - this.#lastFetch.time < ACCOUNTS_CACHE_TTL_MS\n\t\t) {\n\t\t\tthis.#allServerAccounts = this.#lastFetch.result;\n\t\t\treturn;\n\t\t}\n\t\tconst res = await fetch(`${this.#serverOrigin}/api/v1/accounts`, {\n\t\t\theaders: this.#authHeaders(),\n\t\t});\n\t\tif (res.status === 401 || res.status === 403) {\n\t\t\tthrow new AuthError();\n\t\t}\n\t\tif (!res.ok) {\n\t\t\tthrow new Error(`Failed to fetch accounts: ${res.statusText}`);\n\t\t}\n\n\t\tconst { accounts } = (await res.json()) as { accounts: ServerAccountInfo[] };\n\t\tthis.#allServerAccounts = accounts;\n\t\tthis.#lastFetch = { time: Date.now(), result: accounts };\n\t}\n\n\tasync #restoreImportedAccounts(): Promise<void> {\n\t\tconst saved = this.#loadImportedAddresses();\n\t\tif (saved.length === 0) return;\n\n\t\tconst existingAddresses = new Set(this.getAccounts().map((a) => a.address));\n\t\tconst restored: ManagedAccount[] = [];\n\n\t\tfor (const address of saved) {\n\t\t\tif (existingAddresses.has(address)) continue;\n\n\t\t\tconst info = this.#allServerAccounts.find((a) => a.suiAddress === address);\n\t\t\tif (!info) continue; // Account no longer on server — skip\n\n\t\t\tconst scheme = KEY_SCHEME_MAP[info.keyScheme];\n\t\t\tif (!scheme) continue;\n\n\t\t\trestored.push(this.#serverAccountToManaged(info, scheme));\n\t\t}\n\n\t\tif (restored.length > 0) {\n\t\t\tthis.setInitialAccounts([...this.getAccounts(), ...restored]);\n\t\t}\n\t}\n\n\t#loadImportedAddresses(): string[] {\n\t\tif (typeof localStorage === 'undefined') return [];\n\t\ttry {\n\t\t\tconst raw = localStorage.getItem(RemoteCliAdapter.STORAGE_KEY);\n\t\t\tif (!raw) return [];\n\t\t\tconst parsed: unknown = JSON.parse(raw);\n\t\t\tif (!Array.isArray(parsed)) return [];\n\t\t\treturn parsed.filter((v): v is string => typeof v === 'string');\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t#saveImportedAddresses(): void {\n\t\tif (typeof localStorage === 'undefined') return;\n\t\tconst addresses = this._accounts.map((a) => a.address);\n\t\tlocalStorage.setItem(RemoteCliAdapter.STORAGE_KEY, JSON.stringify(addresses));\n\t}\n\n\t#serverAccountToManaged(\n\t\tinfo: ServerAccountInfo,\n\t\tscheme: SignatureScheme,\n\t\tlabel?: string,\n\t): ManagedAccount {\n\t\tconst publicKey = publicKeyFromSuiBytes(info.publicBase64Key, scheme);\n\n\t\tconst signer = new CliProxySigner({\n\t\t\taddress: info.suiAddress,\n\t\t\tpublicKey,\n\t\t\tscheme,\n\t\t\tserverOrigin: this.#serverOrigin,\n\t\t\tauthToken: this.#authToken,\n\t\t});\n\n\t\tconst accountLabel = label ?? info.alias ?? this.getDefaultLabel();\n\n\t\treturn buildManagedAccount(signer, info.suiAddress, accountLabel, CLI_WALLET_FEATURES);\n\t}\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"webcrypto-adapter.d.mts","names":[],"sources":["../../src/adapters/webcrypto-adapter.ts"],"mappings":";;;;cAoBa,sBAAA,SAA+B,iBAAA;EAAA;WAClC,EAAA;EAAA,SACA,IAAA;cAIG,OAAA;IAAY,MAAA;IAAiB,SAAA;EAAA;EAQnC,UAAA,CAAA,GAAc,OAAA;EA0Bd,aAAA,CAAc,OAAA,GAAU,oBAAA,GAAuB,OAAA,CAAQ,cAAA;EAcvD,aAAA,CAAc,OAAA,UAAiB,KAAA,WAAgB,OAAA;EAM/C,aAAA,CAAc,OAAA,WAAkB,OAAA;AAAA"}
1
+ {"version":3,"file":"webcrypto-adapter.d.mts","names":[],"sources":["../../src/adapters/webcrypto-adapter.ts"],"mappings":";;;;cAoBa,sBAAA,SAA+B,iBAAA;EAAA;WAClC,EAAA;EAAA,SACA,IAAA;cAIG,OAAA;IAAY,MAAA;IAAiB,SAAA;EAAA;EAQnC,UAAA,IAAc,OAAA;EA0Bd,aAAA,CAAc,OAAA,GAAU,oBAAA,GAAuB,OAAA,CAAQ,cAAA;EAcvD,aAAA,CAAc,OAAA,UAAiB,KAAA,WAAgB,OAAA;EAM/C,aAAA,CAAc,OAAA,WAAkB,OAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"webcrypto-adapter.mjs","names":["#store","#saveMeta"],"sources":["../../src/adapters/webcrypto-adapter.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { ExportedWebCryptoKeypair } from '@mysten/signers/webcrypto';\nimport { WebCryptoSigner } from '@mysten/signers/webcrypto';\n\nimport type { CreateAccountOptions, ManagedAccount } from '../types.js';\nimport { BaseSignerAdapter } from './base-adapter.js';\nimport { buildManagedAccount } from './build-managed-account.js';\nimport { IDBStore } from './idb-store.js';\n\ninterface StoredAccountMeta {\n\taddress: string;\n\tlabel: string;\n}\n\nconst DEFAULT_DB_NAME = 'dev-wallet-webcrypto';\nconst DEFAULT_STORE_NAME = 'accounts';\nconst META_KEY = '__account_meta__';\n\nexport class WebCryptoSignerAdapter extends BaseSignerAdapter {\n\treadonly id = 'webcrypto';\n\treadonly name = 'WebCrypto Signer';\n\n\t#store: IDBStore;\n\n\tconstructor(options?: { dbName?: string; storeName?: string }) {\n\t\tsuper();\n\t\tthis.#store = new IDBStore(\n\t\t\toptions?.dbName ?? DEFAULT_DB_NAME,\n\t\t\toptions?.storeName ?? DEFAULT_STORE_NAME,\n\t\t);\n\t}\n\n\tasync initialize(): Promise<void> {\n\t\tconst allEntries = await this.#store.entries<\n\t\t\tstring,\n\t\t\tExportedWebCryptoKeypair | StoredAccountMeta[]\n\t\t>();\n\t\tconst meta: StoredAccountMeta[] = (await this.#store.get<StoredAccountMeta[]>(META_KEY)) ?? [];\n\n\t\tconst accounts = [];\n\n\t\tfor (const [key, value] of allEntries) {\n\t\t\tif (key === META_KEY) continue;\n\n\t\t\tconst address = key as string;\n\t\t\ttry {\n\t\t\t\tconst exported = value as ExportedWebCryptoKeypair;\n\t\t\t\tconst signer = WebCryptoSigner.import(exported);\n\t\t\t\tconst accountMeta = meta.find((m) => m.address === address);\n\t\t\t\taccounts.push(buildManagedAccount(signer, address, accountMeta?.label ?? 'Account'));\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(`[dev-wallet] Failed to load account ${address}, skipping:`, error);\n\t\t\t}\n\t\t}\n\n\t\tthis.setInitialAccounts(accounts);\n\t}\n\n\tasync createAccount(options?: CreateAccountOptions): Promise<ManagedAccount> {\n\t\tconst signer = await WebCryptoSigner.generate();\n\t\tconst address = signer.getPublicKey().toSuiAddress();\n\t\tconst label = options?.label ?? this.getDefaultLabel();\n\n\t\tconst managedAccount = buildManagedAccount(signer, address, label);\n\n\t\tawait this.#store.set(address, signer.export());\n\t\tthis.addAccount(managedAccount);\n\t\tawait this.#saveMeta();\n\n\t\treturn managedAccount;\n\t}\n\n\tasync renameAccount(address: string, label: string): Promise<boolean> {\n\t\tif (!this._performRename(address, label)) return false;\n\t\tawait this.#saveMeta();\n\t\treturn true;\n\t}\n\n\tasync removeAccount(address: string): Promise<boolean> {\n\t\tif (!this.removeAccountByAddress(address)) return false;\n\n\t\tawait this.#store.del(address);\n\t\tawait this.#saveMeta();\n\t\treturn true;\n\t}\n\n\tasync #saveMeta(): Promise<void> {\n\t\tconst meta: StoredAccountMeta[] = this.getAccounts().map((a) => ({\n\t\t\taddress: a.address,\n\t\t\tlabel: a.label,\n\t\t}));\n\t\tawait this.#store.set(META_KEY, meta);\n\t}\n}\n"],"mappings":";;;;;AAgBA,MAAM,kBAAkB;AACxB,MAAM,qBAAqB;AAC3B,MAAM,WAAW;AAEjB,IAAa,yBAAb,cAA4C,kBAAkB;CAI7D;CAEA,YAAY,SAAmD;AAC9D,SAAO;YANM;cACE;AAMf,QAAA,QAAc,IAAI,SACjB,SAAS,UAAU,iBACnB,SAAS,aAAa,mBACtB;;CAGF,MAAM,aAA4B;EACjC,MAAM,aAAa,MAAM,MAAA,MAAY,SAGlC;EACH,MAAM,OAA6B,MAAM,MAAA,MAAY,IAAyB,SAAS,IAAK,EAAE;EAE9F,MAAM,WAAW,EAAE;AAEnB,OAAK,MAAM,CAAC,KAAK,UAAU,YAAY;AACtC,OAAI,QAAQ,SAAU;GAEtB,MAAM,UAAU;AAChB,OAAI;IACH,MAAM,WAAW;IACjB,MAAM,SAAS,gBAAgB,OAAO,SAAS;IAC/C,MAAM,cAAc,KAAK,MAAM,MAAM,EAAE,YAAY,QAAQ;AAC3D,aAAS,KAAK,oBAAoB,QAAQ,SAAS,aAAa,SAAS,UAAU,CAAC;YAC5E,OAAO;AACf,YAAQ,KAAK,uCAAuC,QAAQ,cAAc,MAAM;;;AAIlF,OAAK,mBAAmB,SAAS;;CAGlC,MAAM,cAAc,SAAyD;EAC5E,MAAM,SAAS,MAAM,gBAAgB,UAAU;EAC/C,MAAM,UAAU,OAAO,cAAc,CAAC,cAAc;EAGpD,MAAM,iBAAiB,oBAAoB,QAAQ,SAFrC,SAAS,SAAS,KAAK,iBAAiB,CAEY;AAElE,QAAM,MAAA,MAAY,IAAI,SAAS,OAAO,QAAQ,CAAC;AAC/C,OAAK,WAAW,eAAe;AAC/B,QAAM,MAAA,UAAgB;AAEtB,SAAO;;CAGR,MAAM,cAAc,SAAiB,OAAiC;AACrE,MAAI,CAAC,KAAK,eAAe,SAAS,MAAM,CAAE,QAAO;AACjD,QAAM,MAAA,UAAgB;AACtB,SAAO;;CAGR,MAAM,cAAc,SAAmC;AACtD,MAAI,CAAC,KAAK,uBAAuB,QAAQ,CAAE,QAAO;AAElD,QAAM,MAAA,MAAY,IAAI,QAAQ;AAC9B,QAAM,MAAA,UAAgB;AACtB,SAAO;;CAGR,OAAA,WAAiC;EAChC,MAAM,OAA4B,KAAK,aAAa,CAAC,KAAK,OAAO;GAChE,SAAS,EAAE;GACX,OAAO,EAAE;GACT,EAAE;AACH,QAAM,MAAA,MAAY,IAAI,UAAU,KAAK"}
1
+ {"version":3,"file":"webcrypto-adapter.mjs","names":["#store","#saveMeta"],"sources":["../../src/adapters/webcrypto-adapter.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { ExportedWebCryptoKeypair } from '@mysten/signers/webcrypto';\nimport { WebCryptoSigner } from '@mysten/signers/webcrypto';\n\nimport type { CreateAccountOptions, ManagedAccount } from '../types.js';\nimport { BaseSignerAdapter } from './base-adapter.js';\nimport { buildManagedAccount } from './build-managed-account.js';\nimport { IDBStore } from './idb-store.js';\n\ninterface StoredAccountMeta {\n\taddress: string;\n\tlabel: string;\n}\n\nconst DEFAULT_DB_NAME = 'dev-wallet-webcrypto';\nconst DEFAULT_STORE_NAME = 'accounts';\nconst META_KEY = '__account_meta__';\n\nexport class WebCryptoSignerAdapter extends BaseSignerAdapter {\n\treadonly id = 'webcrypto';\n\treadonly name = 'WebCrypto Signer';\n\n\t#store: IDBStore;\n\n\tconstructor(options?: { dbName?: string; storeName?: string }) {\n\t\tsuper();\n\t\tthis.#store = new IDBStore(\n\t\t\toptions?.dbName ?? DEFAULT_DB_NAME,\n\t\t\toptions?.storeName ?? DEFAULT_STORE_NAME,\n\t\t);\n\t}\n\n\tasync initialize(): Promise<void> {\n\t\tconst allEntries = await this.#store.entries<\n\t\t\tstring,\n\t\t\tExportedWebCryptoKeypair | StoredAccountMeta[]\n\t\t>();\n\t\tconst meta: StoredAccountMeta[] = (await this.#store.get<StoredAccountMeta[]>(META_KEY)) ?? [];\n\n\t\tconst accounts = [];\n\n\t\tfor (const [key, value] of allEntries) {\n\t\t\tif (key === META_KEY) continue;\n\n\t\t\tconst address = key as string;\n\t\t\ttry {\n\t\t\t\tconst exported = value as ExportedWebCryptoKeypair;\n\t\t\t\tconst signer = WebCryptoSigner.import(exported);\n\t\t\t\tconst accountMeta = meta.find((m) => m.address === address);\n\t\t\t\taccounts.push(buildManagedAccount(signer, address, accountMeta?.label ?? 'Account'));\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(`[dev-wallet] Failed to load account ${address}, skipping:`, error);\n\t\t\t}\n\t\t}\n\n\t\tthis.setInitialAccounts(accounts);\n\t}\n\n\tasync createAccount(options?: CreateAccountOptions): Promise<ManagedAccount> {\n\t\tconst signer = await WebCryptoSigner.generate();\n\t\tconst address = signer.getPublicKey().toSuiAddress();\n\t\tconst label = options?.label ?? this.getDefaultLabel();\n\n\t\tconst managedAccount = buildManagedAccount(signer, address, label);\n\n\t\tawait this.#store.set(address, signer.export());\n\t\tthis.addAccount(managedAccount);\n\t\tawait this.#saveMeta();\n\n\t\treturn managedAccount;\n\t}\n\n\tasync renameAccount(address: string, label: string): Promise<boolean> {\n\t\tif (!this._performRename(address, label)) return false;\n\t\tawait this.#saveMeta();\n\t\treturn true;\n\t}\n\n\tasync removeAccount(address: string): Promise<boolean> {\n\t\tif (!this.removeAccountByAddress(address)) return false;\n\n\t\tawait this.#store.del(address);\n\t\tawait this.#saveMeta();\n\t\treturn true;\n\t}\n\n\tasync #saveMeta(): Promise<void> {\n\t\tconst meta: StoredAccountMeta[] = this.getAccounts().map((a) => ({\n\t\t\taddress: a.address,\n\t\t\tlabel: a.label,\n\t\t}));\n\t\tawait this.#store.set(META_KEY, meta);\n\t}\n}\n"],"mappings":";;;;;AAgBA,MAAM,kBAAkB;AACxB,MAAM,qBAAqB;AAC3B,MAAM,WAAW;AAEjB,IAAa,yBAAb,cAA4C,kBAAkB;CAI7D;CAEA,YAAY,SAAmD;EAC9D,MAAM;YANO;cACE;EAMf,KAAKA,SAAS,IAAI,SACjB,SAAS,UAAU,iBACnB,SAAS,aAAa,kBACvB;CACD;CAEA,MAAM,aAA4B;EACjC,MAAM,aAAa,MAAM,KAAKA,OAAO,QAGnC;EACF,MAAM,OAA6B,MAAM,KAAKA,OAAO,IAAyB,QAAQ,KAAM,CAAC;EAE7F,MAAM,WAAW,CAAC;EAElB,KAAK,MAAM,CAAC,KAAK,UAAU,YAAY;GACtC,IAAI,QAAQ,UAAU;GAEtB,MAAM,UAAU;GAChB,IAAI;IACH,MAAM,WAAW;IACjB,MAAM,SAAS,gBAAgB,OAAO,QAAQ;IAC9C,MAAM,cAAc,KAAK,MAAM,MAAM,EAAE,YAAY,OAAO;IAC1D,SAAS,KAAK,oBAAoB,QAAQ,SAAS,aAAa,SAAS,SAAS,CAAC;GACpF,SAAS,OAAO;IACf,QAAQ,KAAK,uCAAuC,QAAQ,cAAc,KAAK;GAChF;EACD;EAEA,KAAK,mBAAmB,QAAQ;CACjC;CAEA,MAAM,cAAc,SAAyD;EAC5E,MAAM,SAAS,MAAM,gBAAgB,SAAS;EAC9C,MAAM,UAAU,OAAO,aAAa,CAAC,CAAC,aAAa;EAGnD,MAAM,iBAAiB,oBAAoB,QAAQ,SAFrC,SAAS,SAAS,KAAK,gBAAgB,CAEY;EAEjE,MAAM,KAAKA,OAAO,IAAI,SAAS,OAAO,OAAO,CAAC;EAC9C,KAAK,WAAW,cAAc;EAC9B,MAAM,KAAKC,UAAU;EAErB,OAAO;CACR;CAEA,MAAM,cAAc,SAAiB,OAAiC;EACrE,IAAI,CAAC,KAAK,eAAe,SAAS,KAAK,GAAG,OAAO;EACjD,MAAM,KAAKA,UAAU;EACrB,OAAO;CACR;CAEA,MAAM,cAAc,SAAmC;EACtD,IAAI,CAAC,KAAK,uBAAuB,OAAO,GAAG,OAAO;EAElD,MAAM,KAAKD,OAAO,IAAI,OAAO;EAC7B,MAAM,KAAKC,UAAU;EACrB,OAAO;CACR;CAEA,MAAMA,YAA2B;EAChC,MAAM,OAA4B,KAAK,YAAY,CAAC,CAAC,KAAK,OAAO;GAChE,SAAS,EAAE;GACX,OAAO,EAAE;EACV,EAAE;EACF,MAAM,KAAKD,OAAO,IAAI,UAAU,IAAI;CACrC;AACD"}