(hash: string)
| 207 | * Either manifestUrl or manifest will be set, not both. |
| 208 | */ |
| 209 | export async function decodeShareData(hash: string): Promise<DecodedShareData | null> { |
| 210 | const hashContent = hash.startsWith('#') ? hash.slice(1) : hash; |
| 211 | const params = new URLSearchParams(hashContent); |
| 212 | |
| 213 | const data = params.get('d'); |
| 214 | if (!data) return null; |
| 215 | |
| 216 | const bytes = fromBase64Url(data); |
| 217 | if (!bytes || bytes.length < 3) return null; |
| 218 | |
| 219 | const flags = bytes[0]; |
| 220 | const hasCamera = (flags & SHARE_DATA_FLAG_CAMERA) !== 0; |
| 221 | const hasConfig = (flags & SHARE_DATA_FLAG_CONFIG) !== 0; |
| 222 | const isInlineManifest = (flags & SHARE_DATA_FLAG_INLINE_MANIFEST) !== 0; |
| 223 | const isCompressed = (flags & SHARE_DATA_FLAG_COMPRESSED) !== 0; |
| 224 | const usesWideLengths = (flags & SHARE_DATA_FLAG_WIDE_LENGTHS) !== 0; |
| 225 | const lengthFieldBytes = getLengthFieldBytes(usesWideLengths); |
| 226 | |
| 227 | let payload: Uint8Array = bytes.slice(1); |
| 228 | |
| 229 | if (isCompressed) { |
| 230 | if (!inflateSync) { |
| 231 | await loadShareDataCompression(); |
| 232 | } |
| 233 | if (!inflateSync) { |
| 234 | return null; |
| 235 | } |
| 236 | try { |
| 237 | payload = new Uint8Array(inflateSync(payload)); |
| 238 | } catch { |
| 239 | return null; |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | if (payload.length < lengthFieldBytes) return null; |
| 244 | |
| 245 | const view = new DataView(payload.buffer, payload.byteOffset, payload.byteLength); |
| 246 | let offset = 0; |
| 247 | |
| 248 | const dataLength = readShareDataLength(view, offset, lengthFieldBytes); |
| 249 | offset += lengthFieldBytes; |
| 250 | |
| 251 | if (payload.length < offset + dataLength) return null; |
| 252 | |
| 253 | const dataBytes = payload.slice(offset, offset + dataLength); |
| 254 | const dataString = new TextDecoder().decode(dataBytes); |
| 255 | offset += dataLength; |
| 256 | |
| 257 | let manifestUrl: string | null = null; |
| 258 | let manifest: ColmapManifest | null = null; |
| 259 | |
| 260 | if (isInlineManifest) { |
| 261 | try { |
| 262 | const parsedManifest: unknown = JSON.parse(dataString); |
| 263 | const result = validateColmapManifest(parsedManifest); |
| 264 | if (!result.success) return null; |
| 265 | manifest = result.manifest; |
| 266 | } catch { |
no test coverage detected