@meddleware/seal-ui 0.0.12 → 0.0.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meddleware/seal-ui",
3
- "version": "0.0.12",
3
+ "version": "0.0.14",
4
4
  "homepage": "https://docs.meddleware.co.uk/blockchain/sui/sealed-storage/",
5
5
  "description": "Standalone Vue 3 SPA for Sealed Storage — client-side Seal encryption + access-gated, decentralised Walrus storage on Sui.",
6
6
  "author": "MeddleWare <meddleware@proton.me>",
@@ -32,26 +32,31 @@
32
32
  "dev": "vite",
33
33
  "build": "vue-tsc --noEmit && vite build",
34
34
  "preview": "vite preview",
35
- "type-check": "vue-tsc --noEmit"
35
+ "type-check": "vue-tsc --noEmit",
36
+ "test": "vitest run",
37
+ "test:watch": "vitest"
36
38
  },
37
39
  "publishConfig": {
38
40
  "access": "public"
39
41
  },
40
42
  "dependencies": {
41
- "@meddleware/design-tokens": "^0.1.2",
42
- "@meddleware/seal-client": "^0.0.3",
43
- "@meddleware/ui": "^0.1.11",
44
- "@meddleware/wallet-adapter": "^0.0.5",
45
- "@mysten/sui": "^2.30.0",
46
- "@mysten/wallet-standard": "^0.20.0",
47
- "vue": "^3.5.40"
43
+ "@meddleware/design-tokens": "^0.1.3",
44
+ "@meddleware/seal-client": "^0.0.5",
45
+ "@meddleware/ui": "^0.1.13",
46
+ "@meddleware/wallet-adapter": "^0.0.6",
47
+ "@mysten/sui": "^2.31.0",
48
+ "@mysten/wallet-standard": "^0.21.0",
49
+ "vue": "^3.5.43"
48
50
  },
49
51
  "devDependencies": {
50
52
  "@types/node": "~24.12.2",
51
- "@vitejs/plugin-vue": "^6.0.8",
53
+ "@vitejs/plugin-vue": "^6.0.9",
54
+ "@vue/test-utils": "^2.5.1",
55
+ "jsdom": "^30.1.0",
52
56
  "typescript": "~6.0.0",
53
- "vite": "^8.1.5",
54
- "vue-tsc": "~3.3.0"
57
+ "vite": "^8.3.0",
58
+ "vitest": "~5.0.1",
59
+ "vue-tsc": "~3.3.11"
55
60
  },
56
61
  "engines": {
57
62
  "node": "^22.18.0 || >=24.12.0"
package/src/App.vue CHANGED
@@ -7,6 +7,7 @@ import SealView from './components/SealView.vue'
7
7
 
8
8
  const { mode, set } = useColorMode('dark')
9
9
  const DOCS_URL = import.meta.env.VITE_DOCS_URL || 'https://docs.meddleware.co.uk/blockchain/sui/sealed-storage/'
10
+ const DEV_URL = import.meta.env.VITE_DEV_URL || 'https://dev.meddleware.co.uk/sui/sealed-storage/'
10
11
  </script>
11
12
 
12
13
  <template>
@@ -23,7 +24,7 @@ const DOCS_URL = import.meta.env.VITE_DOCS_URL || 'https://docs.meddleware.co.uk
23
24
 
24
25
  <SealView />
25
26
 
26
- <AppFooter :docs-url="DOCS_URL" />
27
+ <AppFooter :docs-url="DOCS_URL" :dev-url="DEV_URL" />
27
28
  </div>
28
29
  </template>
29
30
 
@@ -17,6 +17,7 @@ import {
17
17
  type SealedContentPointer,
18
18
  type FieldSuggestion,
19
19
  } from '@meddleware/seal-client'
20
+ import { checkManifestForNetwork } from '../manifest-guard.js'
20
21
  import { WalletGuard } from '@meddleware/wallet-adapter'
21
22
  import { useWallet, getSuiClient } from '../wallet.js'
22
23
  import { registry, getSealController } from '../seal.js'
@@ -175,14 +176,13 @@ function downloadManifest(): void {
175
176
  // ── Decrypt ──────────────────────────────────────────────────────────────────
176
177
  const decManifestText = ref('')
177
178
  const decValues = ref<Record<string, string | boolean>>({})
178
- const decManifest = computed<SealedManifest | null>(() => {
179
- if (!decManifestText.value.trim()) return null
180
- try {
181
- return JSON.parse(decManifestText.value) as SealedManifest
182
- } catch {
183
- return null
184
- }
185
- })
179
+ // Validate the pasted/loaded manifest through the shared schema guard (rejects malformed shapes)
180
+ // AND enforce that it targets the network this app is built for — a manifest sealed on another
181
+ // network references a package + committee that don't exist here and can never decrypt. The
182
+ // `decManifestError` is surfaced next to the input so a rejection explains itself.
183
+ const decCheck = computed(() => checkManifestForNetwork(decManifestText.value, NETWORK))
184
+ const decManifest = computed<SealedManifest | null>(() => decCheck.value.manifest)
185
+ const decManifestError = computed<string | null>(() => decCheck.value.error)
186
186
  const decProvider = computed(() =>
187
187
  decManifest.value ? (providers.find((p) => p.type === decManifest.value?.policyType) ?? null) : null,
188
188
  )
@@ -494,6 +494,7 @@ async function performUnlock(item: SealedContentPointer): Promise<void> {
494
494
  />
495
495
  </label>
496
496
  </template>
497
+ <p v-else-if="decManifestError" class="muted">{{ decManifestError }}</p>
497
498
  <p v-else-if="decManifestText.trim()" class="muted">Unrecognised or invalid manifest.</p>
498
499
 
499
500
  <button class="primary" :disabled="busy || !decProvider" @click="performDecrypt">
@@ -0,0 +1,30 @@
1
+ import { parseSealedManifest, type SealedManifest } from '@meddleware/seal-client'
2
+
3
+ /**
4
+ * Result of validating a pasted/loaded manifest: either the parsed manifest, or a human-readable
5
+ * reason it was rejected. An empty input is `{ manifest: null, error: null }` (nothing to show yet).
6
+ */
7
+ export type ManifestCheck =
8
+ | { manifest: SealedManifest; error: null }
9
+ | { manifest: null; error: string | null }
10
+
11
+ /**
12
+ * Validate a manifest string for use on `network`. Rejects a malformed shape (via the shared
13
+ * `parseSealedManifest` schema guard) and a manifest sealed for a different network — the latter
14
+ * references a policy package + key-server committee that don't exist here and can never decrypt.
15
+ */
16
+ export function checkManifestForNetwork(text: string, network: string): ManifestCheck {
17
+ if (!text.trim()) return { manifest: null, error: null }
18
+ try {
19
+ const manifest = parseSealedManifest(text)
20
+ if (manifest.network !== network) {
21
+ return {
22
+ manifest: null,
23
+ error: `This manifest is for '${manifest.network}', but this app is on '${network}'. It cannot be decrypted here.`,
24
+ }
25
+ }
26
+ return { manifest, error: null }
27
+ } catch (e) {
28
+ return { manifest: null, error: e instanceof Error ? e.message : 'Invalid manifest.' }
29
+ }
30
+ }