useDecryptedAsset()

useDecryptedAsset is the low-level composable shared by SwImage and SwAsyncText. It abstracts the "remote-asset fetch → conditional AES decryption → IndexedDB persistent caching → state machine" pipeline. Application code rarely calls it directly, but it's available when you need to build custom encrypted-asset loaders (custom store tables, custom cache encodings).

Usage

Real .bnc decryption (status + result)

加载中...

The toCache / fromCache hooks let the caller decide both the cache format and the final business-result format:

// Example 1: encrypted image → Blob URL (this is exactly what SwImage does internally)
const { status, result: blobUrl } = useDecryptedAsset<Blob, string>({
  url: computed(() => props.src),
  key: imgKey,
  table: 'image',
  toCache: buf => new Blob([buf], { type: detectMime(buf) }),
  fromCache: blob => URL.createObjectURL(blob),
})

// Example 2: encrypted text → string (reactively watching url changes — SwAsyncText's pattern)
const { result: text } = useDecryptedAsset<Uint8Array, string>({
  url: computed(() => props.url),
  key: imgKey,
  table: 'text',
  toCache: buf => new Uint8Array(buf),
  fromCache: bytes => new TextDecoder().decode(bytes),
  reactive: true,
})

How It Works

  1. SSR short-circuit: when import.meta.server is true, returns immediately. status stays 'loading'; no network request, no decryption
  2. Cache lookup: fileId = md5(new URL(url).pathname).toUpperCase(). Queries the IndexedDB store table first; on hit, returns fromCache(data)
  3. Remote fetch + conditional decryption: on miss, fetch(url) returns an ArrayBuffer; if url contains .bnc → decryptBinary(buf, key), otherwise skips decryption and goes straight to toCache
  4. Cache write: the toCache-encoded data is written to IndexedDB (instant on next visit); result flips from null → business result; status from 'loading' → 'loaded'. The cache write is a degradable side path — it is not awaited (rendering never waits on the DB) and a write failure is silently ignored (fw#380)
  5. Reactive reload: when reactive: true, watchEffect watches url for reactive changes and re-runs the pipeline; with false (default), only triggers once via onMounted

Options

NameTypeRequiredDefaultDescription
urlMaybeRef<string>✓—Asset URL. Accepts a reactive value (Ref / computed)
keystring✓—AES decryption key. Only used for .bnc URLs; ignored on the plain path but still required
table'image' | 'text'✓—IndexedDB store-table name. Two are reserved: 'image' for image-class assets, 'text' for text-class
toCache(buffer: ArrayBuffer) => TCached✓—Encodes the (decrypted) ArrayBuffer into the cache format (e.g. new Blob([buf]) / new Uint8Array(buf))
fromCache(cached: TCached) => TResult✓—Decodes the cache format into the business result (e.g. URL.createObjectURL(blob) / new TextDecoder().decode(bytes))
reactivebooleanfalsetrue watches url reactively via watchEffect; false triggers once via onMounted

Return value

NameTypeDescription
statusRef<'loading' | 'loaded' | 'error'>Current load state. Always 'loading' during SSR
resultRef<TResult | null>Decoded business result. null means loading (or SSR); non-null means ready

Caveats

  • Client-only — depends on IndexedDB / fetch / Web Crypto. The function returns immediately during SSR; the caller's status stays 'loading'. Render a skeleton placeholder for result === null
  • Encryption routing is automatic from the URL: contains .bnc → decrypt, otherwise skip. No need for callers to branch
  • The dbGet/dbPut data shape is { id: string, data: TCached }, compatible with the existing SwImage / SwAsyncText cache format
  • Cache-write failures are silently ignored (fw#380): rendering is unaffected (result comes from the in-memory cacheData) and the rejection never bubbles up as an unhandledrejection polluting errorHandler reports. The typical trigger is writing a Blob to IndexedDB in storage-restricted WebKit environments (crawlers / Safari private mode / restricted WebViews). A cache-read failure is treated as a miss and falls back to remote loading; network, decryption, and conversion failures still set status='error'
  • Adding new store-table names requires registering the schema in framework/src/runtime/utils/db.ts first; otherwise IndexedDB writes will fail
  • Stale requests: each load clears the previous result and resets the status to loading. Only the latest request may update the result and status. An empty URL or scope disposal invalidates earlier requests; network requests already sent are not actively cancelled.