Skip to content
UniKVS
Esc
↑↓navigate↵open⌘Jpreview
On this page

Streams

@unikvs/core stream types: aliases of Web standard streams, chunk-type handling, and the relationship to conversion.

Overview

Stream types are aliases of Web standards. They add no implementation of their own. They appear in IStorage stream acquisition and in the source / destination of conversion contexts.

Type Entity Description
IReadableStream<T> ReadableStream<T> Readable stream
IWritableStream<T> WritableStream<T> Writable stream
ITransformStream<TIn, TOut> { readable, writable } Pair of readable: IReadableStream<TOut> and writable: IWritableStream<TIn>
import type { IReadableStream, IWritableStream, ITransformStream } from "@unikvs/core";

declare const readable: IReadableStream<Uint8Array>;
declare const writable: IWritableStream<Uint8Array>;
declare const transform: ITransformStream<ArrayBuffer, number>;

const out: IReadableStream<number> = transform.readable;
const input: IWritableStream<ArrayBuffer> = transform.writable;

Chunks

Chunk types come from the user’s type arguments. Check the type-argument positions rather than confusing them with data types.

User Chunk-deciding type args Defaults
IStorage<TWriteData, TReadData, TWriteChunk, TReadChunk> TWriteChunk / TReadChunk any / TWriteChunk
IMiddleware<TEncodeData, TDecodeData, TEncodeChunk, TDecodeChunk> TEncodeChunk / TDecodeChunk any
import type { IStorage } from "@unikvs/core";

// write chunks are ArrayBuffer, read chunks are number
type Foursome = IStorage<Uint8Array, string, ArrayBuffer, number>;

// getWritable returns IWritableStream<ArrayBuffer>,
// getReadable returns IReadableStream<number>

Omitting TReadChunk reuses TWriteChunk. Storages and middleware without chunks leave the chunk type arguments out as any.

Relation to conversion

In conversion contexts, data conversion and stream conversion differ in what source / destination hold.

Conversion kind Contexts source / destination hold
Data IEncodeContext / IDecodeContext values themselves
Stream IEncodeStreamContext / IDecodeStreamContext IReadableStream
import type { IEncodeContext, IEncodeStreamContext, IReadableStream } from "@unikvs/core";

declare const dataCtx: IEncodeContext<string, number>;
const srcValue: string = dataCtx.source; // value
const destValue: number | undefined = dataCtx.destination;

declare const streamCtx: IEncodeStreamContext<ArrayBuffer, number>;
const srcStream: IReadableStream<ArrayBuffer> = streamCtx.source; // stream
const destStream: IReadableStream<number> | undefined = streamCtx.destination;

In stream conversion, note that you swap whole streams, not chunks. For per-chunk conversion, build something like a TransformStream inside and assign it to destination.

Example

Example implementing stream acquisition on the storage side. getReadable returns a readable stream; getWritable returns a writable stream.

import type { IReadableStream, IStorage, IWritableStream } from "@unikvs/core";

export class ChunkedMemory implements IStorage<Uint8Array, Uint8Array, Uint8Array> {
  public readonly name = "ChunkedMemory";
  public isOpen = false;
  private readonly data = new Map<string, Uint8Array[]>();

  public getWritable(c: IStorage.GetWritableContext): IWritableStream<Uint8Array> {
    const chunks: Uint8Array[] = [];
    return new WritableStream<Uint8Array>({
      write: (chunk) => {
        chunks.push(chunk);
      },
      close: () => {
        this.data.set(c.key, chunks);
      },
    });
  }

  public getReadable(c: IStorage.GetReadableContext): IReadableStream<Uint8Array> {
    const chunks = this.data.get(c.key) ?? [];
    return new ReadableStream<Uint8Array>({
      start(controller) {
        for (const chunk of chunks) {
          controller.enqueue(chunk);
        }
        controller.close();
      },
    });
  }
}

API reference

ITransformStream

A pair of readable and writable streams.

PropType
readableIReadableStream<TOut>

Readable stream.

TypeIReadableStream<TOut>
writableIWritableStream<TIn>

Writable stream.

TypeIWritableStream<TIn>

Was this page helpful?