@meddleware/walrus-storage-ui 0.1.68 → 0.1.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,9 +1,38 @@
1
1
  # Changelog
2
2
 
3
- All notable changes to `@meddleware/walrus-ui` are documented here.
3
+ All notable changes to `@meddleware/walrus-storage-ui` are documented here.
4
4
  Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning follows
5
5
  [Semantic Versioning](https://semver.org/). Before 0.2.0, breaking changes ship as patch releases.
6
6
 
7
+ ## [0.1.70] - 2026-11-12
8
+
9
+ ### Changed
10
+
11
+ - The header shows the company mark (the Kopimi "K") beside the tool name (`@meddleware/ui` 0.1.41 `AppToolShell`).
12
+ - The favicon set (SVG, ICO, Apple touch icon, 192 and 512 icons, web manifest named "Walrus Assets") now comes from `@meddleware/frontend-config` 0.0.8 (`meddlewareApp({ name })`); the app's own `public/favicon.svg` and its icon link tag are removed.
13
+ - Dependencies: `@meddleware/ui` ^0.1.41, `@meddleware/frontend-config` ^0.0.8, `@meddleware/web-utils` ^0.0.4.
14
+ - CI and release workflows set `DO_NOT_TRACK=1`.
15
+
16
+ ### Added
17
+
18
+ - Tests: the header shows the mark; sources and the production build name no third-party font, CDN or tracker, ship no font files and carry the brand icon set (run over `dist/` in CI after the build).
19
+
20
+ ## [0.1.69] - 2026-11-07
21
+
22
+ ### Changed
23
+
24
+ - Words for a person: the page title is "Walrus Assets · Meddleware" (it was "Walrus Uploader · MeddleWare") and the description says what the app does for its reader. The licence holder is spelled Meddleware.
25
+ - Where Walrus storage cannot be used the page says so in plain words and shows no tool: "not available on localnet" (and the networks to switch to), or "not available here right now" for a relay address that is not https or a gate id that is not an object id. A gate id set for a network with no recorded `access_gate` deployment makes that network unavailable instead of silently serving it without the pass. Which networks to suggest comes from the same checks, not from prose. The variable names go to the browser console only.
26
+ - A failed owned-blob load says "Your blobs could not be loaded. Check your connection and press Refresh."; the cause goes to the console.
27
+ - `@meddleware/walrus-storage-kit` ^0.1.38 (the access-pass prompt reads "This upload relay" and "Gate owner receives"; the estimated relay fee is exact), `@meddleware/ui` ^0.1.40, `@meddleware/web-utils` ^0.0.3, `@meddleware/wallet-adapter` ^0.0.22 (peer range now `>=0.0.22 <0.2.0`).
28
+
29
+ ### Internal
30
+
31
+ - The standalone shell is the shared `AppToolShell` (the relay badge stays in its `actions` slot); the network comes from `selectBuiltNetwork`.
32
+ - `@meddleware/frontend-config` ^0.0.7: `vite.config.ts` is `meddlewareApp`, `vitest.config.ts` is `vueTestConfig`, `tsconfig.json` extends the shared base; the local audit gate and licence scripts are replaced by `mw-audit-gate` and `mw-third-party-licenses`; `mw-sync --check` and a CSP inline-script check (`npm run check:csp`, also in the image build) run in CI. The `"meddleware"` section of package.json declares the CSP profile (`chain-wasm`) and Lighthouse use.
33
+ - Removed the unused `public/CNAME`, `public/_headers` and `public/_redirects` (the image is served by static-server, which sets the headers and the SPA fallback). `AGENTS.md` points to `CLAUDE.md`.
34
+ - New tests: `availability`, the availability cases of `config`, the unavailable view, the shell, the plain load failure and a wording test over `index.html` and every template.
35
+
7
36
  ## [0.1.68] - 2026-11-06
8
37
 
9
38
  ### Changed
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  BSD Zero Clause License
2
2
 
3
- Copyright (c) 2026 MeddleWare
3
+ Copyright (c) 2026 Meddleware
4
4
 
5
5
  Permission to use, copy, modify, and/or distribute this software for any
6
6
  purpose with or without fee is hereby granted.
package/README.md CHANGED
@@ -3,13 +3,13 @@
3
3
  [![License: 0BSD](https://img.shields.io/badge/license-0BSD-blue)](LICENSE)
4
4
 
5
5
  A Vue 3 SPA for uploading and managing blobs on [Walrus](https://walrus.xyz) decentralised
6
- storage on Sui. Supports NFT-gated operator relays (commission-enforced on-chain via
6
+ storage on Sui. Supports NFT-gated Meddleware relays (commission-enforced on-chain via
7
7
  `access_gate`) with automatic fallback to the public Mysten relay.
8
8
 
9
9
  ## Features
10
10
 
11
11
  - Connect any Sui wallet (wallet-standard)
12
- - Select between operator relay (supports the app) and public relay (free)
12
+ - Select between the Meddleware relay (supports the app) and the public relay (free)
13
13
  - Upload blobs through an NFT-gated or open relay
14
14
  - View and extend your owned Walrus blobs
15
15
  - Two-approval upload flow: register tx → upload → certify tx
@@ -32,17 +32,22 @@ All `VITE_*` vars are baked into the static bundle at build time.
32
32
  | Variable | Default | Description |
33
33
  | --- | --- | --- |
34
34
  | `VITE_NETWORK` | `testnet` | Network the standalone build selects (`testnet` or `mainnet`); embedded, the host's selector rules |
35
- | `VITE_WALRUS_RELAY_TESTNET` | Public Mysten relay | Operator relay URL for testnet |
36
- | `VITE_WALRUS_RELAY_MAINNET` | Public Mysten relay | Operator relay URL for mainnet |
35
+ | `VITE_WALRUS_RELAY_TESTNET` | Public Mysten relay | Meddleware relay URL for testnet (https; plain http only on localhost) |
36
+ | `VITE_WALRUS_RELAY_MAINNET` | Public Mysten relay | Meddleware relay URL for mainnet (https; plain http only on localhost) |
37
37
  | `VITE_ACCESS_GATE_ID_TESTNET` | — | Gate object ID (testnet; unset = no gate) |
38
38
  | `VITE_ACCESS_GATE_SOULBOUND_TESTNET` | `false` | `true` if NFTs are soulbound |
39
39
  | `VITE_ACCESS_GATE_ID_MAINNET` | — | Gate object ID (mainnet) |
40
40
  | `VITE_UPLOAD_RELAY_MAX_TIP_MIST` | `5000000` | Max relay tip cap in MIST (0.005 SUI) |
41
41
 
42
42
  The `access_gate` package and `PlatformConfig` come from the published deployment
43
- (`relayGateConfig` in `@meddleware/walrus-storage-kit`) — operators only configure the values listed
43
+ (`relayGateConfig` in `@meddleware/walrus-storage-kit`) — a deployment only configures the values listed
44
44
  above.
45
45
 
46
+ A setting the app cannot serve (a relay URL that is not https, a gate id that is not an object id) makes that network
47
+ read "not available here right now" to a person; the variable is named only in the browser console. A gate id set for a
48
+ network with no recorded `access_gate` deployment makes the network "not available on <network>" (it would otherwise be
49
+ served without the pass). Networks to switch to are the ones that pass the same checks.
50
+
46
51
  ## Docker build
47
52
 
48
53
  The Docker context is this repo root. `@meddleware/walrus-storage-kit` resolves from the npm registry,
package/index.html CHANGED
@@ -3,9 +3,8 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <link rel="icon" type="image/svg+xml" href="/favicon.svg">
7
- <title>Walrus Uploader · MeddleWare</title>
8
- <meta name="description" content="Upload blobs to Walrus decentralised storage on Sui through the MeddleWare relay. Client-side; your wallet signs and pays." />
6
+ <title>Walrus Assets · Meddleware</title>
7
+ <meta name="description" content="Upload files to Walrus decentralised storage on Sui and manage the blobs you own: see how long they last, extend them, finish an upload. Runs in your browser; your wallet signs and pays." />
9
8
  </head>
10
9
  <body>
11
10
  <div id="app"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meddleware/walrus-storage-ui",
3
- "version": "0.1.68",
3
+ "version": "0.1.70",
4
4
  "homepage": "https://docs.meddleware.co.uk/blockchain/sui/walrus-storage/",
5
5
  "description": "Standalone Vue 3 SPA for uploading blobs to Walrus decentralised storage and managing owned blobs — wallet-connected, NFT-gated relay support.",
6
6
  "author": "Meddleware <dev@meddleware.co.uk>",
@@ -42,34 +42,35 @@
42
42
  "lint:js": "eslint .",
43
43
  "lint": "npm run lint:css && npm run lint:js && npm run lint:html",
44
44
  "lint:html": "html-validate \"src/**/*.vue\"",
45
- "licenses": "node scripts/third-party-licenses.mjs",
46
- "check:licenses": "node scripts/third-party-licenses.mjs --check",
45
+ "licenses": "mw-third-party-licenses",
46
+ "check:licenses": "mw-third-party-licenses --check",
47
47
  "shots": "SHOTS=1 playwright test",
48
48
  "lhci": "npm run build && lhci autorun",
49
- "check:bundle": "check-prod-bundle dist"
49
+ "check:bundle": "check-prod-bundle dist",
50
+ "check:csp": "mw-check-csp-inline @Dockerfile dist"
50
51
  },
51
52
  "publishConfig": {
52
53
  "access": "public"
53
54
  },
54
55
  "dependencies": {
55
56
  "@meddleware/design-tokens": "^0.1.13",
56
- "@meddleware/ui": "^0.1.39",
57
+ "@meddleware/ui": "^0.1.41",
57
58
  "@meddleware/walrus-storage-client": "^0.0.34",
58
- "@meddleware/walrus-storage-kit": "^0.1.37",
59
- "@meddleware/web-utils": "^0.0.2",
59
+ "@meddleware/walrus-storage-kit": "^0.1.39",
60
+ "@meddleware/web-utils": "^0.0.4",
60
61
  "@mysten/sui": "^2.33.2",
61
62
  "@mysten/walrus": "~1.2.32",
62
63
  "@mysten/walrus-wasm": "~0.3.1",
63
64
  "vue": "^3.5.43"
64
65
  },
65
66
  "peerDependencies": {
66
- "@meddleware/wallet-adapter": ">=0.0.12 <0.2.0"
67
+ "@meddleware/wallet-adapter": ">=0.0.22 <0.2.0"
67
68
  },
68
69
  "devDependencies": {
69
70
  "@lhci/cli": "^0.15.1",
70
71
  "@meddleware/access-gate-client": "^0.0.13",
71
- "@meddleware/frontend-config": "^0.0.2",
72
- "@meddleware/wallet-adapter": "^0.0.21",
72
+ "@meddleware/frontend-config": "^0.0.8",
73
+ "@meddleware/wallet-adapter": "^0.0.22",
73
74
  "@playwright/test": "~1.64.1",
74
75
  "@types/node": "~26.6.5",
75
76
  "@vitejs/plugin-vue": "^6.0.8",
@@ -103,5 +104,10 @@
103
104
  "basic-ftp": "^6.2.4",
104
105
  "tmp": "^0.2.7",
105
106
  "zod": "^4.6.7"
107
+ },
108
+ "meddleware": {
109
+ "kind": "app",
110
+ "csp": "chain-wasm",
111
+ "lhci": true
106
112
  }
107
113
  }
package/src/App.vue CHANGED
@@ -1,35 +1,24 @@
1
1
  <script setup lang="ts">
2
- // Standalone shell for the Walrus SPA: app header + footer wrapping the core tool view.
2
+ // Standalone shell for the Walrus SPA: the shared tool shell (header, colour mode, footer) around the core tool view.
3
3
  // The core UI lives in WalrusStorageView.vue (also exported for inline embedding in the dashboard).
4
- import { AppFooter, AppHeader, ColorModeControl, CopyrightLine, UiAppShell, useColorMode } from '@meddleware/ui'
4
+ import { computed } from 'vue'
5
+ import { AppToolShell } from '@meddleware/ui'
5
6
  import { TipConfigBadge } from '@meddleware/walrus-storage-kit'
6
7
  import { walrusNetwork, OPERATOR_RELAY_HOSTS } from './config.js'
7
8
  import WalrusStorageView from './components/WalrusStorageView.vue'
8
9
 
9
- const { mode, set } = useColorMode('dark')
10
- const DOCS_URL = import.meta.env.VITE_DOCS_URL || 'https://docs.meddleware.co.uk/blockchain/sui/walrus-storage/'
11
- const DEV_URL = import.meta.env.VITE_DEV_URL || 'https://meddleware.dev/sui/walrus-storage/'
10
+ /** The relay of the active network (the badge shows whether it is up and what it tips); none where Walrus storage cannot be used. */
11
+ const relayHost = computed(() => (walrusNetwork.value ? OPERATOR_RELAY_HOSTS[walrusNetwork.value] : null))
12
+ const DOCS_URL = import.meta.env.VITE_DOCS_URL || undefined
13
+ const DEV_URL = import.meta.env.VITE_DEV_URL || undefined
12
14
  </script>
13
15
 
14
16
  <template>
15
- <UiAppShell contained>
16
- <template #header>
17
- <AppHeader variant="dark">
18
- <template #brand>Walrus Assets</template>
19
- <template #actions>
20
- <TipConfigBadge v-if="walrusNetwork" :host="OPERATOR_RELAY_HOSTS[walrusNetwork]" />
21
- <ColorModeControl :model-value="mode" @update:model-value="set" />
22
- </template>
23
- </AppHeader>
17
+ <AppToolShell title="Walrus Assets" slug="walrus-storage" :docs-url="DOCS_URL" :dev-url="DEV_URL">
18
+ <template #actions>
19
+ <TipConfigBadge v-if="relayHost" :host="relayHost" />
24
20
  </template>
25
21
 
26
- <h1 class="mw-visually-hidden">Walrus Assets</h1>
27
22
  <WalrusStorageView />
28
-
29
- <template #footer>
30
- <AppFooter :docs-url="DOCS_URL" :dev-url="DEV_URL">
31
- <CopyrightLine symbol-variant="kopimi" organisation-name="Meddleware" rights-statement="jam" />
32
- </AppFooter>
33
- </template>
34
- </UiAppShell>
23
+ </AppToolShell>
35
24
  </template>
@@ -0,0 +1,21 @@
1
+ // What a person is told when Walrus storage cannot be used on a network. Nothing here names a build variable or a missing setting: a
2
+ // person can do nothing about those, so they read what they can do (wait, or use another network); the technical cause goes to the
3
+ // console (`config.ts` logs it) and to the developer docs.
4
+ //
5
+ // Which networks to suggest is not decided here: `config.ts` passes the networks that currently pass its checks, which follow the
6
+ // deployment records and the configured relay hosts, never prose.
7
+
8
+ /** " Switch to testnet." (or "testnet or mainnet"), or nothing where no other network can be used. */
9
+ function switchHint(others: readonly string[]): string {
10
+ return others.length ? ` Switch to ${others.join(' or ')}.` : ''
11
+ }
12
+
13
+ /** The network is not one Walrus storage is offered on (no recorded deployment, or no Walrus there): not a fault. */
14
+ export function notAvailableOn(net: string, others: readonly string[]): string {
15
+ return `Walrus storage is not available on ${net}.${switchHint(others)}`
16
+ }
17
+
18
+ /** The network is offered but this site cannot serve it right now (a setting the person cannot change). */
19
+ export function notAvailableNow(others: readonly string[]): string {
20
+ return `Walrus storage is not available here right now. Try again later${others.length ? ` or switch to ${others.join(' or ')}` : ''}.`
21
+ }
@@ -9,7 +9,7 @@ import { WalletGuard } from '@meddleware/wallet-adapter'
9
9
  import { AccessGateCta, WalrusUpload, useAccessGate, type WalrusNetwork } from '@meddleware/walrus-storage-kit'
10
10
  import type { ExistingCopy } from '@meddleware/walrus-storage-client/flow'
11
11
  import { buildExecutor, getSuiClient, useWallet } from '../wallet.js'
12
- import { accessGate, network, relayHosts, walrusNetwork } from '../config.js'
12
+ import { accessGate, availability, network, relayHosts, walrusNetwork } from '../config.js'
13
13
  import { useOwnedBlobs } from '../composables/useOwnedBlobs.js'
14
14
  import { useStorageUpload, type UploadResult } from '../composables/useStorageUpload.js'
15
15
  import MyBlobs from './MyBlobs.vue'
@@ -97,7 +97,7 @@ function onSettled(): void {
97
97
  <h2 id="walrus-title">Walrus storage</h2>
98
98
  <UiToolIntro>Upload and manage blobs on Walrus decentralised storage ({{ network }}).</UiToolIntro>
99
99
 
100
- <UiNotice v-if="!walrusNetwork" type="info" :message="`Walrus storage is not available on ${network}. Switch to testnet or mainnet.`" />
100
+ <UiNotice v-if="!walrusNetwork" type="info" :message="availability.message ?? ''" />
101
101
  <template v-else>
102
102
  <AppTabNav v-model="activeTab" :tabs="TABS" id-prefix="walrus" aria-label="Feature tabs" />
103
103
 
@@ -8,11 +8,13 @@
8
8
  // (used after an upload/extend, which can create or mutate on-chain blobs).
9
9
  import { ref, shallowRef } from 'vue'
10
10
  import type { OwnedBlob } from '@meddleware/walrus-storage-client'
11
- import { errorMessage } from '@meddleware/web-utils'
12
11
  import { getSuiClient } from '../wallet.js'
13
12
  import { walrusNetwork } from '../config.js'
14
13
  import { walrusClient, walrusLib } from '../walrus.js'
15
14
 
15
+ /** What a person reads when the list could not be loaded. */
16
+ export const LOAD_FAILED = 'Your blobs could not be loaded. Check your connection and press Refresh.'
17
+
16
18
  const blobs = shallowRef<OwnedBlob[]>([])
17
19
  const currentEpoch = ref(0)
18
20
  const loading = ref(false)
@@ -75,7 +77,9 @@ async function load(address: string | null, opts: { force?: boolean } = {}): Pro
75
77
  loadedFor.value = key
76
78
  } catch (e) {
77
79
  if (mine !== latest) return
78
- error.value = errorMessage(e)
80
+ // The page says what the person can do; the technical cause (an RPC or decoding error) goes to the console.
81
+ console.error('walrus-storage-ui: could not load the owned blobs', e)
82
+ error.value = LOAD_FAILED
79
83
  } finally {
80
84
  if (mine === latest) {
81
85
  loading.value = false
@@ -14,7 +14,7 @@ import {
14
14
  type ExistingCopy,
15
15
  type UploadProgress,
16
16
  } from '@meddleware/walrus-storage-client/flow'
17
- import { network, relayHosts, uploadRelayMaxTipMist, walrusNetwork } from '../config.js'
17
+ import { availability, relayHosts, uploadRelayMaxTipMist, walrusNetwork } from '../config.js'
18
18
  import { buildExecutor, getSuiClient, useWallet } from '../wallet.js'
19
19
  import { walrusClient, walrusLib, walrusWasmUrl } from '../walrus.js'
20
20
 
@@ -59,7 +59,7 @@ export function useStorageUpload(gate: () => AccessGateState) {
59
59
  async function performUpload(bytes: Uint8Array, opts: UploadOptions): Promise<UploadResult> {
60
60
  if (!account.value) throw new Error('Connect your wallet first.')
61
61
  const net = walrusNetwork.value
62
- if (!net) throw new Error(`Walrus storage is not available on ${network.value}.`)
62
+ if (!net) throw new Error(availability.value.message ?? 'Walrus storage is not available here.')
63
63
  const executor = await buildExecutor()
64
64
  const address = account.value.address
65
65
  const access = accessFor({
package/src/config.ts CHANGED
@@ -1,15 +1,22 @@
1
1
  // Configuration. The network is wallet-adapter's shared runtime selector (the standalone build
2
- // selects VITE_NETWORK in main.ts; embedded, the host's selector rules). Operator settings are
2
+ // selects VITE_NETWORK in main.ts; embedded, the host's selector rules). Deployment settings are
3
3
  // build-time VITE_* per network: relay hostnames default to the public Mysten relay (set
4
- // VITE_WALRUS_RELAY_* to your operator relay to collect the tip); the access gate is optional
4
+ // VITE_WALRUS_RELAY_* to the Meddleware relay to collect the tip); the access gate is optional
5
5
  // (unset ⇒ ungated), and its package + PlatformConfig come from the published deployment; the
6
6
  // purchase price and payees are read from the chain, never configured.
7
- import { computed } from 'vue'
7
+ //
8
+ // A configuration fault is shown to a person as a plain "not available" (`./availability.js`); the technical cause (which variable)
9
+ // is logged to the console for the developer (D117). Which networks are offered follows the deployment records and the configured
10
+ // relay hosts, never prose.
11
+ import { computed, watch } from 'vue'
8
12
  import type { WalrusNetwork, RelayGateConfig } from '@meddleware/walrus-storage-kit'
9
- import { PUBLIC_WALRUS_RELAY_HOSTS, relayGateConfig } from '@meddleware/walrus-storage-kit'
13
+ import { PUBLIC_WALRUS_RELAY_HOSTS, relayGateConfig, requireHttpsHost } from '@meddleware/walrus-storage-kit'
14
+ import { accessGateDeployment } from '@meddleware/access-gate-client/deployments'
15
+ import { isValidSuiObjectId } from '@mysten/sui/utils'
10
16
  import { useNetwork } from '@meddleware/wallet-adapter'
11
17
  import { withRuntimeEnv } from '@meddleware/ui'
12
18
  import { errorMessage } from '@meddleware/web-utils'
19
+ import { notAvailableNow, notAvailableOn } from './availability.js'
13
20
 
14
21
  /** Build-time env bag. Injectable on the env-reading helpers below purely so unit tests can exercise
15
22
  * each branch without depending on Vite's (frozen) `import.meta.env` inlining. */
@@ -21,25 +28,96 @@ const env: EnvSource = withRuntimeEnv((import.meta as unknown as { env?: EnvSour
21
28
  /** The active network (read-only ref). */
22
29
  export const network = useNetwork().network
23
30
 
24
- /** The active network as a Walrus network, or null where Walrus has none (e.g. localnet). */
25
- export const walrusNetwork = computed<WalrusNetwork | null>(() =>
26
- network.value === 'testnet' || network.value === 'mainnet' ? network.value : null,
27
- )
31
+ /** The Walrus networks this app can be built for (Walrus runs on these two; localnet has none). */
32
+ const WALRUS_NETWORKS: readonly WalrusNetwork[] = ['testnet', 'mainnet']
33
+
34
+ /** The Meddleware relay host for a network (env → public fallback). */
35
+ function relayHostFor(net: WalrusNetwork, envSource: EnvSource): string {
36
+ return (net === 'mainnet' ? envSource.VITE_WALRUS_RELAY_MAINNET : envSource.VITE_WALRUS_RELAY_TESTNET) || PUBLIC_WALRUS_RELAY_HOSTS[net]
37
+ }
28
38
 
29
- /** Operator relay host per network (env → public fallback). */
39
+ /** Meddleware relay host per network (env → public fallback). */
30
40
  export const OPERATOR_RELAY_HOSTS: Record<WalrusNetwork, string> = {
31
- testnet: env.VITE_WALRUS_RELAY_TESTNET || PUBLIC_WALRUS_RELAY_HOSTS.testnet,
32
- mainnet: env.VITE_WALRUS_RELAY_MAINNET || PUBLIC_WALRUS_RELAY_HOSTS.mainnet,
41
+ testnet: relayHostFor('testnet', env),
42
+ mainnet: relayHostFor('mainnet', env),
33
43
  }
34
44
 
35
- /** The operator/public host pair for `useWalrusRelay`. */
45
+ /** The Meddleware/public host pair for `useWalrusRelay` (its `operator` key is the library's name for the Meddleware relay). */
36
46
  export function relayHosts(network: WalrusNetwork): { operator: string; public: string } {
37
47
  return { operator: OPERATOR_RELAY_HOSTS[network], public: PUBLIC_WALRUS_RELAY_HOSTS[network] }
38
48
  }
39
49
 
50
+ /** Whether Walrus storage can be used on a network, and what to tell a person when it cannot. */
51
+ export interface Availability {
52
+ /** True when the network is offered and its settings are usable. */
53
+ usable: boolean
54
+ /** Plain words for a person when `usable` is false; null otherwise. */
55
+ message: string | null
56
+ /** The technical cause (which setting or record), for the console; null when there is none. */
57
+ cause: string | null
58
+ }
59
+
60
+ /**
61
+ * The state of a network, before any message is written (pure; `envSource` is injectable for tests).
62
+ * `offered: false` is "it is not offered there" (localnet; a gate configured on a network with no recorded `access_gate`
63
+ * deployment, which could not be honoured); `offered: true` with a `cause` is a setting this site cannot serve.
64
+ */
65
+ function inspect(net: string, envSource: EnvSource): { offered: boolean; cause: string | null } {
66
+ if (net !== 'testnet' && net !== 'mainnet') return { offered: false, cause: null }
67
+ const NET = net.toUpperCase()
68
+ const gateId = envSource[`VITE_ACCESS_GATE_ID_${NET}`]
69
+ if (gateId) {
70
+ try {
71
+ accessGateDeployment(net)
72
+ } catch {
73
+ // A gate cannot be honoured without the published package: serving the network ungated would silently skip the pass.
74
+ return { offered: false, cause: `VITE_ACCESS_GATE_ID_${NET} is set but no access_gate deployment is recorded for ${net}` }
75
+ }
76
+ if (!isValidSuiObjectId(gateId)) {
77
+ return { offered: true, cause: `VITE_ACCESS_GATE_ID_${NET} is not a Sui object id: "${gateId}"` }
78
+ }
79
+ }
80
+ try {
81
+ requireHttpsHost(relayHostFor(net, envSource), `VITE_WALRUS_RELAY_${NET}`)
82
+ } catch (e) {
83
+ return { offered: true, cause: errorMessage(e) }
84
+ }
85
+ return { offered: true, cause: null }
86
+ }
87
+
88
+ /** Availability of Walrus storage on `net` (pure; the networks suggested to switch to are the ones that pass the same checks). */
89
+ export function availabilityOf(net: string, envSource: EnvSource = env): Availability {
90
+ const own = inspect(net, envSource)
91
+ if (own.offered && own.cause === null) return { usable: true, message: null, cause: null }
92
+ const others = WALRUS_NETWORKS.filter((n) => {
93
+ if (n === net) return false
94
+ const o = inspect(n, envSource)
95
+ return o.offered && o.cause === null
96
+ })
97
+ return { usable: false, message: own.offered ? notAvailableNow(others) : notAvailableOn(net, others), cause: own.cause }
98
+ }
99
+
100
+ /** Availability on the active network. */
101
+ export const availability = computed(() => availabilityOf(network.value))
102
+
103
+ /** The active network as a Walrus network, or null where Walrus storage cannot be used (localnet, or a network this site cannot serve). */
104
+ export const walrusNetwork = computed<WalrusNetwork | null>(() => (availability.value.usable ? (network.value as WalrusNetwork) : null))
105
+
106
+ // A configuration fault is told to the developer once per cause (a person sees only "not available").
107
+ const reported = new Set<string>()
108
+ watch(
109
+ availability,
110
+ (a) => {
111
+ if (!a.cause || reported.has(a.cause)) return
112
+ reported.add(a.cause)
113
+ console.error(`walrus-storage-ui: Walrus storage is unavailable on ${network.value} (${a.cause})`)
114
+ },
115
+ { immediate: true },
116
+ )
117
+
40
118
  /**
41
119
  * Default relay tip ceiling (MIST) when `VITE_UPLOAD_RELAY_MAX_TIP_MIST` is unset: 0.005 SUI, the
42
- * workspace-wide ceiling (~8× the operator relay's worst-case tip at the 100 MiB edge cap).
120
+ * workspace-wide ceiling (~8× the Meddleware relay's worst-case tip at the 100 MiB edge cap).
43
121
  */
44
122
  export const DEFAULT_UPLOAD_RELAY_MAX_TIP_MIST = 5_000_000
45
123
 
@@ -54,7 +132,7 @@ export function uploadRelayMaxTipMist(envSource: EnvSource = env): number {
54
132
  }
55
133
 
56
134
  /**
57
- * The optional NFT access gate for a network: the operator supplies only the Gate id and soulbound
135
+ * The optional NFT access gate for a network: the deployment supplies only the Gate id and soulbound
58
136
  * flag (`VITE_ACCESS_GATE_*_{NET}`); `relayGateConfig` fixes the package and `PlatformConfig` to the
59
137
  * published deployment (commission enforcement), and the price is read from the gate on the chain.
60
138
  * `null` — an ungated
package/src/main.ts CHANGED
@@ -3,13 +3,11 @@ import '@meddleware/design-tokens/seasons.css'
3
3
  import '@meddleware/ui/reset.css'
4
4
  import { createApp } from 'vue'
5
5
  import { loadRuntimeConfig, useSeason } from '@meddleware/ui'
6
- import { useNetwork } from '@meddleware/wallet-adapter'
6
+ import { selectBuiltNetwork } from '@meddleware/wallet-adapter'
7
7
 
8
8
  // The standalone build targets one network; select it in the shared selector so the client, the
9
9
  // relay, the gate and the stored resume keys all agree. (Embedded, the host's selector rules.)
10
- const env = (import.meta as unknown as { env?: Record<string, string | undefined> }).env ?? {}
11
- const built = env.VITE_NETWORK || 'testnet'
12
- if (built === 'testnet' || built === 'mainnet') useNetwork().setNetwork(built)
10
+ selectBuiltNetwork(import.meta.env.VITE_NETWORK || 'testnet')
13
11
 
14
12
  useSeason()
15
13
  // Machine-zone endpoints (the indexer, the Walrus relays) come from /runtime-config.json when the deployment
package/tsconfig.json CHANGED
@@ -1,18 +1,4 @@
1
1
  {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "module": "ESNext",
5
- "moduleResolution": "Bundler",
6
- "lib": ["ESNext", "DOM", "DOM.Iterable"],
7
- "strict": true,
8
- "esModuleInterop": true,
9
- "skipLibCheck": true,
10
- "resolveJsonModule": true,
11
- "isolatedModules": true,
12
- "verbatimModuleSyntax": true,
13
- "jsx": "preserve",
14
- "types": ["node"],
15
- "noEmit": true
16
- },
2
+ "extends": "@meddleware/frontend-config/tsconfig/vue-app.json",
17
3
  "include": ["src/**/*.ts", "src/**/*.vue", "src/**/*.d.ts", "vite.config.ts", "playwright.config.ts", "e2e/**/*.ts"]
18
4
  }
package/vite.config.ts CHANGED
@@ -1,19 +1,14 @@
1
- import { defineConfig } from 'vite'
2
1
  import vue from '@vitejs/plugin-vue'
3
- import { assertE2eOnlyInE2eMode, themeInit, viteTarget } from '@meddleware/frontend-config/vite'
2
+ import { defineConfig } from 'vite'
3
+ import { meddlewareApp } from '@meddleware/frontend-config/vite'
4
4
 
5
- // design-tokens and ui resolve from node_modules (published packages).
6
- // @mysten/walrus is excluded from dep-optimization so its wasm `?url` import resolves.
7
- export default defineConfig(({ mode }) => {
8
- // The end-to-end build (`--mode e2e`) includes a mock wallet; nothing else may.
9
- assertE2eOnlyInE2eMode(mode)
10
- return {
11
- build: { target: viteTarget() },
12
- plugins: [vue(), themeInit({ default: 'dark' })],
13
- optimizeDeps: {
14
- // @meddleware/wallet-adapter ships TS + .vue source and holds the shared wallet singleton; if it
15
- // were pre-bundled, its .vue files (served raw) would load a second copy of the singleton in dev.
16
- exclude: ['@mysten/walrus', '@mysten/walrus-wasm', '@meddleware/wallet-adapter'],
17
- },
18
- }
19
- })
5
+ // design-tokens and ui resolve from node_modules (published packages). meddlewareApp refuses VITE_E2E outside the e2e mode and keeps
6
+ // @meddleware/wallet-adapter (the shared wallet singleton) out of pre-bundling. @mysten/walrus and its wasm are excluded too, so the
7
+ // wasm `?url` import resolves.
8
+ export default defineConfig(
9
+ meddlewareApp({
10
+ plugins: [vue()],
11
+ name: 'Walrus Assets',
12
+ config: { optimizeDeps: { exclude: ['@mysten/walrus', '@mysten/walrus-wasm'] } },
13
+ }),
14
+ )