@zerochain/sdk
Version:
The Züs JS SDK is a JavaScript client library that provides a convenient interface for interacting with the Züs Network. It allows developers to perform various operations such as creating and managing allocations, uploading and downloading files, executi
225 lines (186 loc) • 6.72 kB
text/typescript
import { sleep } from '@/utils'
import {
getBridge,
type Config,
type GoInstance,
type SetIsWasmLoaded,
} from './bridge'
const DEFAULT_CONFIG: Config = {
wasmBaseUrl: '',
useCachedWasm: false,
isWasmGzipped: true,
}
export const getConfig = (): Config => getBridge().__config__ || DEFAULT_CONFIG
const getWasmPath = () => {
return getIsEnterpriseMode() ? '/enterprise-zcn.wasm' : '/zcn.wasm'
}
const getDefaultUrl = () => {
const c = getConfig()
let suffix = 'mainnet'
const isEnterpriseMode = getIsEnterpriseMode()
const wasmType = isEnterpriseMode ? 'enterprise-zcn.wasm' : 'zcn.wasm'
if (c.wasmBaseUrl) {
const fallbackUrl = `${c.wasmBaseUrl}/${wasmType}`
return { defaultUrl: fallbackUrl, suffix }
}
// For Zus prod apps only
const currentLocation = window?.location?.hostname
const isHost = (host: string) => currentLocation?.includes(host)
if (isHost('localhost') || isHost('mob')) suffix = 'mob'
else if (isHost('dev') || isHost('mob.desktop')) suffix = 'dev'
else if (isHost('demo')) suffix = 'demo'
else if (isHost('staging')) suffix = 'staging'
else if (isHost('test')) suffix = 'test'
if (!c.zus?.cdnUrl) return { defaultUrl: null, suffix }
const defaultUrl = `${c.zus.cdnUrl}/${suffix}/${wasmType}`
return { defaultUrl, suffix }
}
const getVersionedWasmDetails = () => {
const c = getConfig()
if (!c.useCachedWasm) return { url: null, version: null, type: null }
const isEnterpriseMode = getIsEnterpriseMode()
const wasmVersion = isEnterpriseMode
? c.cacheConfig.enterpriseGosdkVersion
: c.cacheConfig.standardGosdkVersion
const wasmType = isEnterpriseMode ? 'enterprise' : 'normal'
const customUrl = isEnterpriseMode
? c.cacheConfig.enterpriseWasmUrl
: c.cacheConfig.standardWasmUrl
if (customUrl) return { url: customUrl, version: wasmVersion, type: wasmType }
// For Zus prod apps only
if (!c.zus?.cdnUrl) return { url: null, version: null, type: null }
const defaultVersionedUrl = `${c.zus.cdnUrl}/wasm/zcn-${wasmVersion}-${wasmType}.wasm`
return { url: defaultVersionedUrl, version: wasmVersion, type: wasmType }
}
const getIsEnterpriseMode = () =>
localStorage.getItem('enterpriseAlloc') === 'enabled'
const getWasmUrl = () => {
const wasmPath = getWasmPath()
const { defaultUrl, suffix } = getDefaultUrl()
if (getConfig().useCachedWasm) {
const wasm = getVersionedWasmDetails()
const wasmUrl = !wasm.version ? defaultUrl : wasm.url
return { suffix, wasmUrl, wasmPath, defaultUrl }
} else {
return { suffix, wasmUrl: defaultUrl, wasmPath, defaultUrl }
}
}
const getCachedWasmResponse = async ({
wasmCache,
wasmPath,
}: {
wasmCache: Cache | undefined
wasmPath: string
}) => {
if (!wasmCache || !wasmPath) return null
const c = getConfig()
if (!c.useCachedWasm) return null
const expectedWasmVersion = getIsEnterpriseMode()
? c.cacheConfig.enterpriseGosdkVersion
: c.cacheConfig.standardGosdkVersion
const cachedWasmVersion = localStorage.getItem(wasmPath)
const shouldUseCachedWasm = cachedWasmVersion === expectedWasmVersion
if (!shouldUseCachedWasm) return null
return wasmCache.match(wasmPath)
}
const fetchWasm = async ({
wasmUrl,
wasmPath,
defaultUrl,
}: {
wasmUrl: string | null
wasmPath: string
defaultUrl: string | null
}) => {
const caches = 'caches' in window ? window.caches : null
// Check if the WASM file is cached
const wasmCache = await caches?.open('wasm-cache')
let response = await getCachedWasmResponse({ wasmCache, wasmPath })
let shouldCache = false
if (!response?.ok) {
let headers: Record<string, string> = { 'Content-Type': 'application/wasm' }
const cfg = getConfig()
if (cfg.isWasmGzipped) headers['Content-Encoding'] = 'gzip'
// WASM not found in cache, fetching from CDN
if (wasmUrl) {
response = await fetch(wasmUrl, { headers }).catch(err => {
console.error('Failed to fetch from CDN, trying CDN fallback:', err)
return null
})
if (response?.ok) shouldCache = true
}
if (!response?.ok && wasmUrl !== defaultUrl && defaultUrl) {
response = await fetch(defaultUrl, { headers }).catch(err => {
console.error('Failed to fetch from CDN fallback, trying local:', err)
return null
})
}
// If CDN network request fails, fallback to the local WASM path
if (!response?.ok) {
wasmUrl = wasmPath
response = await fetch(wasmUrl, { headers })
}
if (!response?.ok) throw new Error(`Failed to fetch WASM`)
if (shouldCache) {
// Cache the fetched WASM response
await wasmCache?.put(wasmPath, response.clone())
if (cfg.useCachedWasm) {
const storedWasmVersion = getIsEnterpriseMode()
? cfg.cacheConfig.enterpriseGosdkVersion
: cfg.cacheConfig.standardGosdkVersion
localStorage.setItem(wasmPath, storedWasmVersion)
}
}
} else {
console.log('Using cached WASM.')
}
return { source: response, isVersioned: shouldCache }
}
/**
* The maximum amount of time that we would expect Wasm to take to initialize.
* If it doesn't initialize after this time, we send a warning to console.
* Most likely something has gone wrong if it takes more than 10 seconds to
* initialize.
*/
const MAX_LOAD_TIMOUT = 10000 // 10 seconds
/**
* Loads the WebAssembly (Wasm) module and runs it within the Go instance.
*
* @param go The Go instance.
*/
export async function loadWasm(
go: GoInstance,
setIsWasmLoaded: SetIsWasmLoaded
): Promise<void> {
// If instantiateStreaming doesn't exists, polyfill/create it on top of instantiate
if (!WebAssembly?.instantiateStreaming) {
WebAssembly.instantiateStreaming = async (resp, importObject) => {
const source = await (await resp).arrayBuffer()
return await WebAssembly.instantiate(source, importObject)
}
}
const { suffix, wasmUrl, wasmPath, defaultUrl } = getWasmUrl()
const { source } = await fetchWasm({ wasmUrl, wasmPath, defaultUrl })
// set SUFFIX env variable in GoSDK
go.env = { SUFFIX: suffix }
// Initiate WASM
const result = await WebAssembly.instantiateStreaming(source, go.importObject)
setTimeout(() => {
const bridge = getBridge()
if (bridge?.__wasm_initialized__ !== true) {
console.warn(
'wasm window.__zcn_wasm__ (zcn.__wasm_initialized__) still not true after max time'
)
}
}, MAX_LOAD_TIMOUT)
go.run(result.instance)
let hasInitialized = false
while (!hasInitialized) {
await sleep(300)
const bridge = getBridge()
if (bridge?.__wasm_initialized__ === true) {
hasInitialized = true
setIsWasmLoaded(true)
}
}
}