LCOV - code coverage report
Current view: top level - dashql-app/src/shell_app - shell_share.ts Coverage Total Hit
Test: DashQL 6e74948 Lines: 100.0 % 44 44
Test Date: 2026-11-06 17:46:50

            Line data    Source code
       1              : import { BASE64URL_CODEC } from '../utils/base64.js';
       2              : 
       3              : /// The hash parameter carrying a shared script.
       4              : /// The hash never reaches the server and does not collide with the ?data= setup links of the app.
       5            1 : export const SHARE_SCRIPT_HASH_PARAMETER = 'script';
       6              : 
       7              : /// Pipe bytes through a (de)compression stream.
       8              : /// Reads the stream directly, Firefox logs corrupt input as console error when reading it through a Response.
       9            1 : async function pipeBytes(bytes: Uint8Array, transform: CompressionStream | DecompressionStream): Promise<ArrayBuffer> {
      10            1 :     const writer = transform.writable.getWriter();
      11            1 :     const written = writer.write(bytes as Uint8Array<ArrayBuffer>).then(() => writer.close());
      12              :     // A failed write also fails the read below, report the read error
      13            1 :     written.catch(() => {});
      14            1 :     const reader = transform.readable.getReader();
      15            1 :     const chunks: Uint8Array[] = [];
      16            1 :     let length = 0;
      17            1 :     for (let chunk = await reader.read(); !chunk.done; chunk = await reader.read()) {
      18            1 :         chunks.push(chunk.value);
      19            1 :         length += chunk.value.byteLength;
      20            1 :     }
      21            1 :     await written;
      22            1 :     const out = new Uint8Array(length);
      23            1 :     let offset = 0;
      24            1 :     for (const chunk of chunks) {
      25            1 :         out.set(chunk, offset);
      26            1 :         offset += chunk.byteLength;
      27            1 :     }
      28            1 :     return out.buffer;
      29            1 : }
      30              : 
      31              : /// Encode a script as base64url of its deflated UTF-8 bytes
      32            1 : export async function encodeShareScript(script: string): Promise<string> {
      33            1 :     const deflated = await pipeBytes(new TextEncoder().encode(script), new CompressionStream('deflate-raw'));
      34            1 :     return BASE64URL_CODEC.encode(deflated);
      35            1 : }
      36              : 
      37              : /// Decode a script encoded with encodeShareScript
      38            1 : export async function decodeShareScript(payload: string): Promise<string> {
      39            1 :     if (payload.length === 0 || !BASE64URL_CODEC.isValidBase64(payload)) {
      40            1 :         throw new Error('shared script is not valid base64url');
      41              :     }
      42            1 :     const deflated = new Uint8Array(BASE64URL_CODEC.decode(payload));
      43            1 :     const inflated = await pipeBytes(deflated, new DecompressionStream('deflate-raw'));
      44            1 :     return new TextDecoder('utf-8', { fatal: true }).decode(inflated);
      45            1 : }
      46              : 
      47              : /// Build the URL that opens the shell with a shared script
      48            1 : export function buildShareUrl(payload: string, location: Pick<Location, 'origin' | 'pathname' | 'search'> = window.location): string {
      49            1 :     return `${location.origin}${location.pathname}${location.search}#${SHARE_SCRIPT_HASH_PARAMETER}=${payload}`;
      50            1 : }
      51              : 
      52              : /// Read a shared script payload from the hash of the current URL and remove it from the address bar
      53            1 : export function readShareScriptFromLocation(): string | null {
      54            1 :     const params = new URLSearchParams(window.location.hash.slice(1));
      55            1 :     const payload = params.get(SHARE_SCRIPT_HASH_PARAMETER);
      56            1 :     if (payload == null) return null;
      57            1 :     params.delete(SHARE_SCRIPT_HASH_PARAMETER);
      58            1 :     const hash = params.toString();
      59            1 :     window.history.replaceState(window.history.state, '', `${window.location.pathname}${window.location.search}${hash.length > 0 ? `#${hash}` : ''}`);
      60            1 :     return payload;
      61            1 : }
        

Generated by: LCOV version 2.0-1