| <script lang="ts"> |
| import Button from '../Button.svelte'; |
| import Select from '../Select.svelte'; |
| import { decoderRegistry } from './decoders/utils/decoderRegistry'; |
| import { decodeBase64 } from '$lib/utils/base64Utils'; |
| |
| interface Props { |
| payload?: string | undefined; |
| codec?: string | undefined | null; |
| } |
| |
| let { payload = undefined, codec = undefined }: Props = $props(); |
| |
| type ErrorState = { title: string; message: string }; |
| type DecodedState = |
| | { status: 'idle'; forceChangeDecoder: boolean } |
| | { status: 'error'; error: ErrorState } |
| | { status: 'success'; content: string }; |
| |
| let decodeState = $state<DecodedState>({ status: 'idle', forceChangeDecoder: false }); |
| let showRaw = $state(false); |
| let selectedDecoder = $state(codec || 'string'); |
| |
| function getError(type: 'no_payload' | 'unsupported_codec' | 'invalid_format'): ErrorState { |
| switch (type) { |
| case 'no_payload': |
| return { |
| title: 'No payload', |
| message: 'This message has no payload to decode' |
| }; |
| case 'unsupported_codec': |
| return { |
| title: 'Unsupported codec', |
| message: `The specified codec ${codec} is not supported. Select another decoder from the dropdown below.` |
| }; |
| case 'invalid_format': |
| return { |
| title: 'Invalid format', |
| message: `The payload is not valid ${selectedDecoder.toUpperCase()}` |
| }; |
| } |
| } |
| |
| function decode() { |
| if (!payload) { |
| decodeState = { status: 'error', error: getError('no_payload') }; |
| return; |
| } |
| |
| const decodedPayload = decodeBase64(payload); |
| if (!decodedPayload) { |
| decodeState = { status: 'error', error: getError('invalid_format') }; |
| return; |
| } |
| |
| const decoder = decoderRegistry.get(selectedDecoder); |
| if (!decoder) { |
| decodeState = { status: 'error', error: getError('unsupported_codec') }; |
| return; |
| } |
| |
| const content = decoder.decode(decodedPayload); |
| if (!content) { |
| decodeState = { status: 'error', error: getError('invalid_format') }; |
| return; |
| } |
| |
| decodeState = { status: 'success', content }; |
| } |
| </script> |
| |
| {#if decodeState.status === 'idle'} |
| {#if !codec || !decoderRegistry.get(codec) || decodeState.forceChangeDecoder} |
| <div class="mb-4"> |
| <Select |
| label="Select decoder" |
| name="decoder" |
| options={decoderRegistry.getNames()} |
| bind:value={selectedDecoder} |
| /> |
| </div> |
| {/if} |
| <Button variant="contained" onclick={decode}>Decode</Button> |
| {:else} |
| {#if decodeState.status === 'error'} |
| <div class="bg-red-100 dark:bg-red-900 p-4 rounded-md w-full"> |
| <p class="font-bold">{decodeState.error.title}</p> |
| <p>{decodeState.error.message}</p> |
| </div> |
| {/if} |
| |
| {#if decodeState.status === 'success'} |
| <div class="flex items-center gap-3"> |
| <p class="font-bold">Codec: {selectedDecoder}</p> |
| <Button variant="text" onclick={() => (decodeState = { status: 'idle', forceChangeDecoder: true })}>Change decoder</Button> |
| </div> |
| <div class="w-full mt-2"> |
| <pre class="bg-gray-100 dark:bg-gray-800 p-1 rounded-md overflow-auto"><code |
| >{decodeState.content}</code |
| ></pre> |
| </div> |
| {/if} |
| |
| <div class="mt-4 mb-2"> |
| <Button variant="text" onclick={() => (showRaw = !showRaw)}> |
| {showRaw ? 'Hide' : 'Show'} raw payload (base64) |
| </Button> |
| </div> |
| |
| {#if showRaw && payload} |
| <div class="w-full"> |
| <pre |
| class="bg-gray-100 dark:bg-gray-800 p-4 rounded-md overflow-auto whitespace-pre-wrap break-all"><code |
| >{payload}</code |
| ></pre> |
| </div> |
| {/if} |
| {/if} |