@meddleware/token-deployer-ui 0.0.10 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -5
- package/package.json +19 -8
- package/src/App.vue +1 -1
- package/src/component-styles.css +39 -57
- package/src/components/GithubPush.vue +2 -2
- package/src/components/IconPicker.vue +44 -75
- package/src/components/LicensePicker.vue +2 -2
- package/src/components/ResultPanel.vue +2 -2
- package/src/components/ReviewPanel.vue +11 -3
- package/src/components/TokenDeployerView.vue +52 -48
- package/src/components/WalletBar.vue +12 -1
- package/src/components/WalrusBlobBrowser.vue +4 -4
- package/src/composables/useWalrusRelay.ts +1 -2
- package/src/config.ts +19 -17
- package/src/lib/buildPublishTx.ts +0 -10
- package/src/lib/deploy.ts +9 -3
- package/src/lib/deployExecutor.ts +18 -45
- package/src/lib/template.ts +3 -0
- package/src/lib/walrus-constants.ts +2 -2
- package/src/main.ts +4 -6
- package/src/styles.css +22 -0
- package/.claude/move-language-reference/SKILL.md +0 -445
- package/.claude/skills/move-language-reference/SKILL.md +0 -445
- package/.claude/skills/sui-move-bootcamp/SKILL.md +0 -128
- package/.claude/sui-move-bootcamp/SKILL.md +0 -128
- package/.editorconfig +0 -8
- package/.env.example +0 -72
- package/.gitattributes +0 -1
- package/.github/workflows/docker-publish.yml +0 -348
- package/.github/workflows/e2e-realchain.yml +0 -64
- package/.github/workflows/node-ci.yml +0 -43
- package/.github/workflows/npm-publish.yml +0 -83
- package/.htmlvalidate.mjs +0 -30
- package/.oxfmtrc.json +0 -5
- package/.oxlintrc.json +0 -10
- package/.vscode/extensions.json +0 -9
- package/AGENTS.md +0 -39
- package/CLAUDE.md +0 -166
- package/Dockerfile +0 -96
- package/eslint.config.ts +0 -63
- package/index.html +0 -22
- package/playwright.config.ts +0 -30
- package/postcss.config.js +0 -8
- package/public/CNAME +0 -1
- package/public/_headers +0 -12
- package/public/_redirects +0 -1
- package/public/favicon.ico +0 -0
- package/public/favicon.svg +0 -5
- package/public/og-image.png +0 -0
- package/public/robots.txt +0 -3
- package/public/sitemap.xml +0 -8
- package/scripts/e2e-browser.mjs +0 -15
- package/scripts/e2e-deploy.mjs +0 -227
- package/scripts/e2e-localnet.mjs +0 -67
- package/scripts/e2e-walrus-browser.mjs +0 -112
- package/scripts/gen-og-image.mjs +0 -31
- package/scripts/publish-localnet.mjs +0 -56
- package/scripts/regen-template.sh +0 -45
- package/scripts/sync-template-src.sh +0 -36
- package/scripts/verify-bytecode.mjs +0 -49
- package/src/__tests__/App.spec.js +0 -11
- package/src/composables/useAccessGate.ts +0 -127
- package/src/lib/accessGate.ts +0 -155
- package/src/lib/walrus.ts +0 -155
- package/stylelint.config.js +0 -33
- package/test-results/.last-run.json +0 -4
- package/tests/accessGate.test.ts +0 -113
- package/tests/buildPublishTx.test.ts +0 -243
- package/tests/deploy.test.ts +0 -149
- package/tests/errors.test.ts +0 -32
- package/tests/generatePackage.test.ts +0 -193
- package/tests/github.test.ts +0 -61
- package/tests/licenses.test.ts +0 -81
- package/tests/listMyTokens.test.ts +0 -30
- package/tests/template.test.ts +0 -93
- package/tests/templateArtifact.test.ts +0 -61
- package/tests/templateParity.test.ts +0 -55
- package/tests/useAccessGate.test.ts +0 -97
- package/tests/useWallet.test.ts +0 -74
- package/tests/useWalrusRelayGating.test.ts +0 -30
- package/tests/validation.test.ts +0 -117
- package/tests/walrusUpload.test.ts +0 -93
- package/tests/walrus_url_patch.test.ts +0 -26
- package/tsconfig.json +0 -19
- package/typedoc.json +0 -34
- package/vite.config.ts +0 -73
- package/vitest.config.ts +0 -10
|
@@ -191,57 +191,61 @@ function restart(): void {
|
|
|
191
191
|
</script>
|
|
192
192
|
|
|
193
193
|
<template>
|
|
194
|
-
<
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
194
|
+
<div class="mw-token-deployer">
|
|
195
|
+
<UiToolIntro
|
|
196
|
+
>Deploy your own Sui coin — your wallet signs and pays; no intermediaries ({{
|
|
197
|
+
network
|
|
198
|
+
}}).</UiToolIntro
|
|
199
|
+
>
|
|
200
|
+
|
|
201
|
+
<AppTabNav
|
|
202
|
+
v-model="activeTab"
|
|
203
|
+
:tabs="TABS"
|
|
204
|
+
id-prefix="token-deployer"
|
|
205
|
+
aria-label="Sections"
|
|
206
|
+
class="deployer-tabs"
|
|
207
|
+
/>
|
|
208
|
+
|
|
209
|
+
<!-- The tab list and its panel always render (the selected tab controls a live panel); the wallet
|
|
210
|
+
prompt replaces only the panel's content until a wallet is connected. -->
|
|
209
211
|
<UiTabPanel id-prefix="token-deployer" :tab="activeTab">
|
|
210
|
-
<
|
|
211
|
-
<
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
212
|
+
<WalletGuard message="Connect a Sui wallet to deploy your token.">
|
|
213
|
+
<template v-if="activeTab === 'deploy'">
|
|
214
|
+
<UiStepper :steps="STEPS" :model-value="stepIndex" @update:model-value="onStepperBack" />
|
|
215
|
+
<ConfigForm
|
|
216
|
+
v-if="step === 'identity' || step === 'token' || step === 'settings'"
|
|
217
|
+
:form="form"
|
|
218
|
+
:errors="errors"
|
|
219
|
+
:can-proceed="canProceed"
|
|
220
|
+
:network="network"
|
|
221
|
+
:connected="connected"
|
|
222
|
+
:form-step="formStep"
|
|
223
|
+
@next="goNext"
|
|
224
|
+
@back="goBack"
|
|
225
|
+
/>
|
|
226
|
+
<ReviewPanel
|
|
227
|
+
v-if="step === 'review'"
|
|
228
|
+
:config="config"
|
|
229
|
+
:network="network"
|
|
230
|
+
@back="goBack"
|
|
231
|
+
@confirm="confirmDeploy"
|
|
232
|
+
/>
|
|
233
|
+
<DeployProgress v-if="step === 'deploying'" :step="deployStep" />
|
|
234
|
+
<ResultPanel
|
|
235
|
+
v-if="step === 'done' && result"
|
|
236
|
+
:result="result"
|
|
237
|
+
:config="config"
|
|
238
|
+
@restart="restart"
|
|
239
|
+
/>
|
|
240
|
+
<UiNotice v-if="deployError" type="error">{{ deployError }}</UiNotice>
|
|
241
|
+
</template>
|
|
242
|
+
|
|
243
|
+
<MyTokens v-else :owner="account?.address ?? null" :network="network" />
|
|
244
|
+
</WalletGuard>
|
|
241
245
|
</UiTabPanel>
|
|
242
|
-
</WalletGuard>
|
|
243
246
|
|
|
244
|
-
|
|
247
|
+
<TermsDialog :open="showTerms" @accept="onTermsAccept" @cancel="onTermsCancel" />
|
|
248
|
+
</div>
|
|
245
249
|
</template>
|
|
246
250
|
|
|
247
251
|
<style scoped>
|
|
@@ -3,6 +3,17 @@ import { computed, ref, watch } from 'vue'
|
|
|
3
3
|
import { useWallet, useNetwork } from '@meddleware/wallet-adapter'
|
|
4
4
|
import { SELECTABLE_NETWORKS } from '../config.js'
|
|
5
5
|
import type { Network } from '../lib/types.js'
|
|
6
|
+
|
|
7
|
+
/** Wallet-supplied icons are untrusted: render only inline images or https URLs. */
|
|
8
|
+
function safeIcon(icon: string | undefined): string | undefined {
|
|
9
|
+
if (!icon) return undefined
|
|
10
|
+
if (/^data:image\/(png|svg\+xml|jpeg|webp|gif);/i.test(icon)) return icon
|
|
11
|
+
try {
|
|
12
|
+
return new URL(icon).protocol === 'https:' ? icon : undefined
|
|
13
|
+
} catch {
|
|
14
|
+
return undefined
|
|
15
|
+
}
|
|
16
|
+
}
|
|
6
17
|
import { UiDialog, UiFormField, UiNotice, UiSelect } from '@meddleware/ui'
|
|
7
18
|
|
|
8
19
|
const { wallets, currentWallet, account, connecting, error, connect, disconnect } = useWallet()
|
|
@@ -66,7 +77,7 @@ watch(
|
|
|
66
77
|
<menu class="wallet-list">
|
|
67
78
|
<li v-for="w in wallets" :key="w.name">
|
|
68
79
|
<button type="button" class="primary" :disabled="connecting" @click="connect(w)">
|
|
69
|
-
<img v-if="w.icon" :src="w.icon" alt="" width="20" height="20" />
|
|
80
|
+
<img v-if="safeIcon(w.icon)" :src="safeIcon(w.icon)" alt="" width="20" height="20" />
|
|
70
81
|
{{ w.name }}
|
|
71
82
|
</button>
|
|
72
83
|
</li>
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { ref, watch } from 'vue'
|
|
3
3
|
import { useWallet } from '@meddleware/wallet-adapter'
|
|
4
4
|
import { getReadClient } from '../lib/readClient.js'
|
|
5
|
-
import type { OwnedBlob
|
|
5
|
+
import type { OwnedBlob } from '@meddleware/walrus-client'
|
|
6
|
+
import { WALRUS_RPC_URLS, type WalrusNetwork } from '../config.js'
|
|
6
7
|
import type { Network } from '../lib/types.js'
|
|
7
8
|
import { UiNotice } from '@meddleware/ui'
|
|
8
9
|
|
|
@@ -46,10 +47,9 @@ async function load(): Promise<void> {
|
|
|
46
47
|
blobs.value = []
|
|
47
48
|
hasLoaded.value = false
|
|
48
49
|
try {
|
|
49
|
-
const { createWalrusClient, walrusBlobUrl, fetchOwnedWalrusBlobs } =
|
|
50
|
-
await import('../lib/walrus.js')
|
|
50
|
+
const { createWalrusClient, walrusBlobUrl, fetchOwnedWalrusBlobs } = await import('@meddleware/walrus-client')
|
|
51
51
|
const suiClient = getReadClient(props.network as Network)
|
|
52
|
-
const client = createWalrusClient(props.network)
|
|
52
|
+
const client = createWalrusClient({ network: props.network, rpcUrl: WALRUS_RPC_URLS[props.network] })
|
|
53
53
|
const raw = await fetchOwnedWalrusBlobs(suiClient, client, addr)
|
|
54
54
|
blobs.value = raw.map((b) => ({ ...b, url: walrusBlobUrl(props.network, b.blobId) }))
|
|
55
55
|
hasLoaded.value = true
|
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
// (`IconPicker.vue`) and tests are unaffected.
|
|
5
5
|
import { useWalrusRelay as useSharedWalrusRelay } from '@meddleware/walrus-relay'
|
|
6
6
|
import type { RelayAccessOptions, RelayOption } from '@meddleware/walrus-relay'
|
|
7
|
-
import { WALRUS_RELAY_HOSTS, PUBLIC_WALRUS_RELAY_HOSTS } from '../config.js'
|
|
8
|
-
import type { WalrusNetwork } from '../lib/walrus.js'
|
|
7
|
+
import { WALRUS_RELAY_HOSTS, PUBLIC_WALRUS_RELAY_HOSTS, type WalrusNetwork } from '../config.js'
|
|
9
8
|
|
|
10
9
|
export type { RelayAccessOptions, RelayOption }
|
|
11
10
|
|
package/src/config.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Vite env vars so they can be set at build/deploy time without code changes.
|
|
3
3
|
// See .env.example.
|
|
4
4
|
import type { Network } from './lib/types.js'
|
|
5
|
-
import type
|
|
5
|
+
import { relayGateConfig, type RelayGateConfig } from '@meddleware/walrus-relay'
|
|
6
6
|
|
|
7
7
|
const env = (import.meta as unknown as { env?: Record<string, string | undefined> }).env ?? {}
|
|
8
8
|
|
|
@@ -56,7 +56,7 @@ export const SELECTABLE_NETWORKS: Network[] = ['testnet', 'mainnet']
|
|
|
56
56
|
// ---------------------------------------------------------------------------
|
|
57
57
|
// Walrus is a separate protocol from Sui; only testnet/mainnet exist (no
|
|
58
58
|
// localnet). Icon uploads go through an upload relay (direct-to-storage-node
|
|
59
|
-
// writes fail from browsers).
|
|
59
|
+
// writes fail from browsers). The upload is `@meddleware/walrus-client/flow`; see the relay runbook in
|
|
60
60
|
// README.md / CLAUDE.md for the operator side.
|
|
61
61
|
|
|
62
62
|
/** A Walrus network — a strict subset of {@link Network} (no localnet). */
|
|
@@ -113,27 +113,29 @@ export const WALRUS_MAX_TIP_MIST: bigint = BigInt(env.VITE_WALRUS_MAX_TIP_MIST |
|
|
|
113
113
|
// NFT-gated relay access (optional)
|
|
114
114
|
// ---------------------------------------------------------------------------
|
|
115
115
|
// When the operator relay runs behind an `nft-gate` gateway, using it requires
|
|
116
|
-
// holding the gate's access NFT.
|
|
117
|
-
//
|
|
118
|
-
//
|
|
116
|
+
// holding the gate's access NFT. The operator sets the gate id, soulbound flag (default true) and
|
|
117
|
+
// price per network via env; `relayGateConfig` (walrus-relay) fixes the package and PlatformConfig
|
|
118
|
+
// to the published access_gate deployment. When no gate id is set the relay is ungated.
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
/** The gate config for a network, or `null` (ungated) without a gate id or a recorded deployment. */
|
|
121
|
+
export function parseAccessGate(network: WalrusNetwork, envSource: Record<string, string | undefined> = env): RelayGateConfig | null {
|
|
121
122
|
const NET = network.toUpperCase()
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
123
|
+
const gateId = envSource[`VITE_ACCESS_GATE_ID_${NET}`]
|
|
124
|
+
if (!gateId) return null
|
|
125
|
+
try {
|
|
126
|
+
return relayGateConfig(network, {
|
|
127
|
+
gateId,
|
|
128
|
+
soulbound: (envSource[`VITE_ACCESS_GATE_SOULBOUND_${NET}`] ?? 'true') !== 'false',
|
|
129
|
+
priceMist: BigInt(envSource[`VITE_ACCESS_GATE_PRICE_MIST_${NET}`] || '0'),
|
|
130
|
+
})
|
|
131
|
+
} catch (e) {
|
|
132
|
+
console.warn(`[token-deployer-ui] access gate for ${network} disabled: ${e instanceof Error ? e.message : String(e)}`)
|
|
133
|
+
return null
|
|
132
134
|
}
|
|
133
135
|
}
|
|
134
136
|
|
|
135
137
|
/** The access gate per Walrus network, or `null` when gating is not configured. */
|
|
136
|
-
export const ACCESS_GATE: Record<WalrusNetwork,
|
|
138
|
+
export const ACCESS_GATE: Record<WalrusNetwork, RelayGateConfig | null> = {
|
|
137
139
|
testnet: parseAccessGate('testnet'),
|
|
138
140
|
mainnet: parseAccessGate('mainnet'),
|
|
139
141
|
}
|
|
@@ -73,16 +73,6 @@ export interface BuildFinalizeArgs {
|
|
|
73
73
|
gasBudget: bigint
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
/** Whether a finalize transaction is needed at all for this config. */
|
|
77
|
-
export function needsFinalize(config: TokenConfig, sender: string): boolean {
|
|
78
|
-
return (
|
|
79
|
-
config.initialSupply > 0n ||
|
|
80
|
-
config.supplyPolicy === 'fixed' ||
|
|
81
|
-
config.metadataPolicy === 'frozen' ||
|
|
82
|
-
config.recipient.toLowerCase() !== sender.toLowerCase()
|
|
83
|
-
)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
76
|
/**
|
|
87
77
|
* Finalize PTB: register the coin in the registry, mint initial supply, apply
|
|
88
78
|
* supply/metadata policy, and route caps to the recipient.
|
package/src/lib/deploy.ts
CHANGED
|
@@ -87,10 +87,16 @@ export interface DeployArgs {
|
|
|
87
87
|
onStep?: (step: DeployStep) => void
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Only an explicit `success` effects status counts: a missing status (an executor that did not
|
|
92
|
+
* return effects) is treated as failure, never as success.
|
|
93
|
+
*/
|
|
90
94
|
function assertSuccess(res: SuiTxResult, what: string): void {
|
|
91
95
|
const status = res.effects?.status?.status
|
|
92
|
-
if (status
|
|
93
|
-
throw new Error(
|
|
96
|
+
if (status !== 'success') {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`${what} failed: ${res.effects?.status?.error ?? status ?? 'no effects status returned'}`,
|
|
99
|
+
)
|
|
94
100
|
}
|
|
95
101
|
}
|
|
96
102
|
|
|
@@ -103,7 +109,7 @@ export async function deployToken(args: DeployArgs): Promise<PublishResult> {
|
|
|
103
109
|
const structName = deriveStructName(config.moduleName)
|
|
104
110
|
const normalizedConfig: TokenConfig = { ...config, structName }
|
|
105
111
|
const recipient = normalizedConfig.recipient || args.sender
|
|
106
|
-
const isE2e = import.meta.env.VITE_E2E === '1'
|
|
112
|
+
const isE2e = import.meta.env.MODE === 'e2e' && import.meta.env.VITE_E2E === '1'
|
|
107
113
|
|
|
108
114
|
onStep?.('patching')
|
|
109
115
|
const moduleBytes = await patchTemplateModule({
|
|
@@ -1,63 +1,36 @@
|
|
|
1
|
-
// Builds
|
|
2
|
-
//
|
|
3
|
-
// extractPublishResult
|
|
4
|
-
//
|
|
5
|
-
// The wallet-adapter's own Executor only returns { digest } which is not sufficient
|
|
6
|
-
// for the token deploy flow; we sign via the shared wallet connection but execute
|
|
7
|
-
// via our own client so we can request effects + object types.
|
|
1
|
+
// Builds the deploy Executor from the wallet-adapter's shared executor, executing through this
|
|
2
|
+
// app's gRPC client (`getReadClient`, which E2E builds stub) and asking for the effects and
|
|
3
|
+
// object types `extractPublishResult` needs. Signing, the chain check and the account binding
|
|
4
|
+
// are the wallet-adapter's.
|
|
8
5
|
|
|
9
|
-
import {
|
|
10
|
-
import { useWallet } from '@meddleware/wallet-adapter'
|
|
6
|
+
import { buildExecutor } from '@meddleware/wallet-adapter'
|
|
11
7
|
import type { Transaction } from '@mysten/sui/transactions'
|
|
12
8
|
import { toSuiTxResult } from './deploy.js'
|
|
13
9
|
import type { SuiTxResult, Executor } from './deploy.js'
|
|
14
10
|
import type { Network } from './types.js'
|
|
15
11
|
import { getReadClient } from './readClient.js'
|
|
12
|
+
import { RPC_URLS } from '../config.js'
|
|
16
13
|
|
|
17
14
|
/**
|
|
18
|
-
* Build a deploy Executor bound to the currently connected wallet
|
|
19
|
-
* wallet-adapter singleton) and the given network's gRPC client.
|
|
15
|
+
* Build a deploy Executor bound to the currently connected wallet and `network`.
|
|
20
16
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
17
|
+
* `signAndExecute` requests `effects + objectTypes` and maps the result with
|
|
18
|
+
* {@link toSuiTxResult}, so the caller can extract the published package id, coin type,
|
|
19
|
+
* TreasuryCap, MetadataCap and the Currency ref. A failed transaction is returned (not thrown);
|
|
20
|
+
* `deploy.ts` checks its status.
|
|
21
|
+
*
|
|
22
|
+
* @throws {Error} if no wallet is connected, it cannot sign transactions, or its account does not
|
|
23
|
+
* list `sui:<network>`.
|
|
24
24
|
*/
|
|
25
25
|
export async function buildDeployExecutor(network: Network): Promise<Executor> {
|
|
26
|
-
const
|
|
27
|
-
const wallet = currentWallet.value
|
|
28
|
-
const acct = account.value
|
|
29
|
-
if (!wallet || !acct) throw new Error('Connect a wallet first.')
|
|
30
|
-
|
|
31
|
-
const suiClient = getReadClient(network)
|
|
32
|
-
const chain = `sui:${network}` as const
|
|
33
|
-
|
|
34
|
-
const signFeature = wallet.features['sui:signTransaction'] as
|
|
35
|
-
| {
|
|
36
|
-
signTransaction: (input: {
|
|
37
|
-
transaction: Transaction
|
|
38
|
-
account: typeof acct
|
|
39
|
-
chain: `sui:${string}`
|
|
40
|
-
}) => Promise<{ bytes: string; signature: string }>
|
|
41
|
-
}
|
|
42
|
-
| undefined
|
|
43
|
-
if (!signFeature) throw new Error('This wallet cannot sign transactions.')
|
|
44
|
-
|
|
26
|
+
const executor = await buildExecutor(network, RPC_URLS[network], { client: getReadClient(network) })
|
|
45
27
|
return {
|
|
46
28
|
async signAndExecute(tx: Transaction): Promise<SuiTxResult> {
|
|
47
|
-
const {
|
|
48
|
-
|
|
49
|
-
account: acct,
|
|
50
|
-
chain,
|
|
51
|
-
})
|
|
52
|
-
const res = await suiClient.executeTransaction({
|
|
53
|
-
transaction: fromBase64(bytes),
|
|
54
|
-
signatures: [signature],
|
|
55
|
-
include: { effects: true, objectTypes: true },
|
|
56
|
-
})
|
|
57
|
-
return toSuiTxResult(res)
|
|
29
|
+
const { result } = await executor.signAndExecute(tx, { include: { effects: true, objectTypes: true } })
|
|
30
|
+
return toSuiTxResult(result)
|
|
58
31
|
},
|
|
59
32
|
async waitForTransaction(digest: string): Promise<void> {
|
|
60
|
-
await
|
|
33
|
+
await executor.waitForTransaction(digest)
|
|
61
34
|
},
|
|
62
35
|
}
|
|
63
36
|
}
|
package/src/lib/template.ts
CHANGED
|
@@ -38,6 +38,9 @@ export function initTemplateWasm(): Promise<unknown> {
|
|
|
38
38
|
if (!initPromise) {
|
|
39
39
|
initPromise = Promise.resolve()
|
|
40
40
|
.then(() => (init as unknown as (i?: unknown) => unknown)(wasmInput))
|
|
41
|
+
// Deliberate: under Node the package's nodejs build loads its own wasm and this init call
|
|
42
|
+
// is unnecessary (and may throw). A genuine browser init failure is not hidden — the first
|
|
43
|
+
// bytecode call in patchTemplateModule then fails with the wasm error.
|
|
41
44
|
.catch(() => undefined)
|
|
42
45
|
}
|
|
43
46
|
return initPromise
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// Lightweight Walrus constants with NO heavy imports, so
|
|
2
|
-
//
|
|
1
|
+
// Lightweight Walrus constants with NO heavy imports, so the eagerly rendered `IconPicker.vue`
|
|
2
|
+
// component and the lazily loaded upload
|
|
3
3
|
// can share a single source of truth without pulling `@mysten/walrus` into the
|
|
4
4
|
// component's chunk.
|
|
5
5
|
|
package/src/main.ts
CHANGED
|
@@ -8,7 +8,7 @@ import './component-styles.css'
|
|
|
8
8
|
import { configureWasm } from './lib/template.js'
|
|
9
9
|
// Static: readClient is already in the main chunk (IconPicker, WalrusBlobBrowser and listMyTokens
|
|
10
10
|
// import it), so a dynamic import in the E2E block below could not split it out anyway.
|
|
11
|
-
import {
|
|
11
|
+
import { setReadClient } from './lib/readClient.js'
|
|
12
12
|
import type { SuiGrpcClient } from '@mysten/sui/grpc'
|
|
13
13
|
import { useColorMode, useSeason } from '@meddleware/ui'
|
|
14
14
|
|
|
@@ -28,9 +28,10 @@ configureWasm(wasmUrl)
|
|
|
28
28
|
|
|
29
29
|
// E2E-only: stub the Sui client, register a mock wallet and expose getSuiClient for headless tests.
|
|
30
30
|
// Wrapped in async IIFE to ensure wallet is registered BEFORE app mounts (timing critical).
|
|
31
|
-
//
|
|
31
|
+
// Only the `e2e` build mode may set VITE_E2E (vite.config.ts refuses otherwise); both are static,
|
|
32
|
+
// so every other build tree-shakes this entire block (verified by `npm run check:bundle`).
|
|
32
33
|
;(async () => {
|
|
33
|
-
if (import.meta.env.VITE_E2E === '1') {
|
|
34
|
+
if (import.meta.env.MODE === 'e2e' && import.meta.env.VITE_E2E === '1') {
|
|
34
35
|
const { getWallets } = await import('@mysten/wallet-standard')
|
|
35
36
|
|
|
36
37
|
// Stub gRPC client in place of the network (gRPC-web is binary, so it cannot be faked
|
|
@@ -137,9 +138,6 @@ configureWasm(wasmUrl)
|
|
|
137
138
|
unregister?.()
|
|
138
139
|
unregister = undefined
|
|
139
140
|
}
|
|
140
|
-
|
|
141
|
-
// Expose getSuiClient for tests to build transactions
|
|
142
|
-
w.__getSuiClient = getReadClient
|
|
143
141
|
}
|
|
144
142
|
|
|
145
143
|
createApp(App).mount('#app')
|
package/src/styles.css
CHANGED
|
@@ -58,3 +58,25 @@ footer {
|
|
|
58
58
|
padding-top: 1.5rem;
|
|
59
59
|
margin-top: 2rem;
|
|
60
60
|
}
|
|
61
|
+
|
|
62
|
+
/* Skip link (standalone app shell only — not part of the embeddable library styles). */
|
|
63
|
+
.skip-link {
|
|
64
|
+
position: absolute;
|
|
65
|
+
left: 0.5rem;
|
|
66
|
+
top: -3rem;
|
|
67
|
+
z-index: 100;
|
|
68
|
+
background: var(--accent);
|
|
69
|
+
color: #fff;
|
|
70
|
+
padding: 0.5rem 0.9rem;
|
|
71
|
+
border-radius: 6px;
|
|
72
|
+
text-decoration: none;
|
|
73
|
+
transition: top 0.15s ease;
|
|
74
|
+
}
|
|
75
|
+
.skip-link:focus {
|
|
76
|
+
top: 0.5rem;
|
|
77
|
+
}
|
|
78
|
+
@media (prefers-reduced-motion: reduce) {
|
|
79
|
+
.skip-link {
|
|
80
|
+
transition: none;
|
|
81
|
+
}
|
|
82
|
+
}
|