tus-core

    Framework-neutral tus 1.0 resumable upload protocol state machine for MoonBit

    tus
    resumable-upload
    http
    protocol
    state-machine
    Download zip
    Author
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    21 hours ago
    Downloads
    3

    #MoonTusCore

    Framework-neutral tus 1.0 request validation and atomic resumable-upload state transitions for MoonBit.

    let engine = @tus.TusEngine::new().unwrap()
    let response = engine.handle(@tus.options_request())
    assert_eq!(response.status, 204)

    Persistent adapters call plan_creation and plan_append, then apply the returned plan atomically using both expected_revision and old_offset.

    AppendPlan

    pub(all) struct AppendPlan {
    identifier : String
    expected_revision : Int64
    old_offset : Int64
    new_offset : Int64
    resolved_length : UploadLength
    body : Bytes
    completes_upload : Bool
    } derive(Eq,
    Debug
    )

    Result of a validated append before it is committed to storage.

    ConformanceExpectation

    pub(all) struct ConformanceExpectation {
    status : Int
    error_code : String?
    response_offset : Int64?
    store : StoreExpectation
    } derive(Eq,
    Debug
    )

    ConformanceReport

    pub(all) struct ConformanceReport {
    results : Array[ConformanceResult]
    passed : Int
    failed : Int
    } derive(Eq,
    Debug
    )

    ConformanceResult

    pub(all) struct ConformanceResult {
    name : String
    passed : Bool
    response : TusResponse
    findings : Array[String]
    } derive(Eq,
    Debug
    )

    ConformanceVector

    pub(all) struct ConformanceVector {
    name : String
    initial_records : Array[UploadRecord]
    request : TusRequest
    expected : ConformanceExpectation
    } derive(Eq,
    Debug
    )

    CreationPlan

    pub(all) struct CreationPlan {
    length : UploadLength
    metadata : Array[MetadataEntry]
    } derive(Eq,
    Debug
    )

    Minimal creation plan produced after header validation.

    HeaderField

    pub(all) struct HeaderField {
    name : String
    value : String
    } derive(Eq,
    Debug
    )

    One HTTP field line. Names are normalized to lowercase ASCII while values remain byte-for-byte equivalent as MoonBit strings.

    Headers

    pub(all) struct Headers {
    entries : Array[HeaderField]
    } derive(Eq,
    Debug
    )

    Ordered and duplicate-preserving field collection. Protocol validation can therefore reject repeated singleton fields without a framework hiding them.

    Headers::add

    fn Headers::add(self : Headers, name : String, value : String) -> Headers

    Headers::as_array

    fn Headers::as_array(self : Headers) -> Array[HeaderField]

    Headers::combined

    fn Headers::combined(self : Headers, name : String) -> String?

    Headers::contains

    fn Headers::contains(self : Headers, name : String) -> Bool

    Headers::count

    fn Headers::count(self : Headers, name : String) -> Int

    Headers::empty

    fn Headers::empty() -> Headers

    Headers::first

    fn Headers::first(self : Headers, name : String) -> String?

    Headers::from_array

    fn Headers::from_array(entries : Array[HeaderField]) -> Headers

    Headers::last

    fn Headers::last(self : Headers, name : String) -> String?

    Headers::len

    fn Headers::len(self : Headers) -> Int

    Headers::replace

    fn Headers::replace(self : Headers, name : String, value : String) -> Headers

    Headers::required_singleton

    fn Headers::required_singleton(self : Headers, name : String, missing_kind : TusErrorKind) -> Result[String, TusError]

    Headers::singleton

    fn Headers::singleton(self : Headers, name : String) -> Result[String?, TusError]

    Headers::validate

    fn Headers::validate(self : Headers, limits : TusLimits) -> Result[Unit, TusError]

    Validate generic HTTP field safety before tus-specific interpretation.

    Headers::values

    fn Headers::values(self : Headers, name : String) -> Array[String]

    Headers::wire_size

    fn Headers::wire_size(self : Headers) -> Int

    Headers::without

    fn Headers::without(self : Headers, name : String) -> Headers

    HttpMethod

    pub(all) enum HttpMethod {
    Options
    Post
    Head
    Patch
    Other(String)
    } derive(Eq,
    Debug
    )

    HttpMethod::name

    fn HttpMethod::name(self : HttpMethod) -> String

    MemoryStore

    pub struct MemoryStore {
    // private fields
    }

    Reference store for tests, examples and single-process adapters.

    The protocol engine only needs snapshot, create and compare-and-swap append operations. Production adapters can map those same operations to files, object storage or a database without importing an HTTP framework.

    MemoryStore::commit_append

    fn MemoryStore::commit_append(self : MemoryStore, plan : AppendPlan, limits : TusLimits) -> Result[UploadRecord, TusError]

    Commit a previously validated append using revision and offset as the compare-and-swap precondition. Validation happens before the record array is touched, so every failure leaves the store unchanged.

    MemoryStore::contains

    fn MemoryStore::contains(self : MemoryStore, identifier : String) -> Bool

    MemoryStore::count

    fn MemoryStore::count(self : MemoryStore) -> Int

    MemoryStore::create

    fn MemoryStore::create(self : MemoryStore, plan : CreationPlan, limits : TusLimits) -> Result[UploadRecord, TusError]

    MemoryStore::get

    fn MemoryStore::get(self : MemoryStore, identifier : String) -> Result[UploadRecord, TusError]

    MemoryStore::import_record

    fn MemoryStore::import_record(self : MemoryStore, record : UploadRecord, limits : TusLimits) -> Result[Unit, TusError]

    Insert an explicit record for recovery/import workflows. It is also useful to conformance tests that need precise revision and offset states.

    MemoryStore::is_empty

    fn MemoryStore::is_empty(self : MemoryStore) -> Bool

    MemoryStore::list

    MemoryStore::new

    fn MemoryStore::new(seed? : Int64) -> MemoryStore

    MetadataEntry

    pub(all) struct MetadataEntry {
    key : String
    value : Bytes
    encoded_value : String
    } derive(Eq,
    Debug
    )

    One decoded Upload-Metadata member. value contains decoded bytes and encoded_value retains the canonical wire representation.

    ScenarioAction

    pub(all) enum ScenarioAction {
    Discover
    CreateKnown(String, Int64, Array[MetadataEntry])
    CreateDeferred(String, Array[MetadataEntry])
    Inspect(String)
    Append(String, Int64, Bytes, Int64?)
    } derive(Eq,
    Debug
    )

    Declarative actions for deterministic protocol demonstrations and adapter conformance fixtures. Aliases avoid coupling scenarios to generated IDs.

    ScenarioEvent

    pub(all) struct ScenarioEvent {
    index : Int
    operation : String
    resource_name : String?
    identifier : String?
    status : Int
    offset : Int64?
    error_code : String?
    traces : Array[TraceStep]
    } derive(Eq,
    Debug
    )

    ScenarioReport

    pub(all) struct ScenarioReport {
    events : Array[ScenarioEvent]
    uploads : Array[UploadRecord]
    succeeded : Bool
    } derive(Eq,
    Debug
    )

    StoreExpectation

    pub(all) enum StoreExpectation {
    IgnoreStore
    ResourceMissing(String)
    ResourceState(String, Int64, UploadLength, UploadLifecycle, Bytes)
    } derive(Eq,
    Debug
    )

    Post-request storage assertion attached to a conformance vector.

    TraceStep

    pub(all) struct TraceStep {
    code : String
    message : String
    protocol_section : String
    } derive(Eq,
    Debug
    )

    Normative explanation attached to every decision.

    TusConfig

    pub(all) struct TusConfig {
    collection_path : String
    limits : TusLimits
    allow_creation : Bool
    allow_deferred_length : Bool
    } derive(Eq,
    Debug
    )

    TusConfig::default

    fn TusConfig::default() -> TusConfig

    TusEngine

    pub struct TusEngine {
    config : TusConfig
    // private fields
    }

    Framework-neutral protocol façade. It accepts plain values and owns only a reference store; network I/O, authentication and persistence stay outside.

    TusEngine::handle

    fn TusEngine::handle(self : TusEngine, request : TusRequest) -> TusResponse

    TusEngine::new

    fn TusEngine::new(config? : TusConfig, store? : MemoryStore) -> Result[TusEngine, TusError]

    TusEngine::snapshot

    fn TusEngine::snapshot(self : TusEngine, identifier : String) -> Result[UploadRecord, TusError]

    TusEngine::upload_count

    fn TusEngine::upload_count(self : TusEngine) -> Int

    TusEngine::uploads

    fn TusEngine::uploads(self : TusEngine) -> Array[UploadRecord]

    TusError

    pub(all) struct TusError {
    kind : TusErrorKind
    code : String
    message : String
    status : Int
    header_name : String?
    expected : String?
    actual : String?
    } derive(Eq,
    Debug
    )

    Machine-stable protocol failure. status is the recommended HTTP response status. It is data rather than an exception so adapters can translate it.

    TusErrorKind

    pub(all) enum TusErrorKind {
    InvalidMethod
    InvalidPath
    InvalidHeaderName
    UnsafeHeaderValue
    MissingTusResumable
    UnsupportedVersion
    DuplicateSingletonHeader
    InvalidUnsignedInteger
    IntegerOverflow
    MissingUploadOffset
    OffsetMismatch
    MissingContentType
    InvalidContentType
    MissingContentLength
    BodyLengthMismatch
    MissingUploadLength
    ConflictingUploadLength
    InvalidDeferredLength
    UploadTooLarge
    UploadNotFound
    UploadAlreadyComplete
    UploadTerminated
    InvalidMetadata
    DuplicateMetadataKey
    MetadataTooLarge
    TooManyMetadataEntries
    InvalidBase64
    HeaderCountExceeded
    HeaderBytesExceeded
    BodyTooLarge
    IdentifierExhausted
    StorageConflict
    StorageFailure
    UnsupportedExtension
    InvalidScenario
    UnsupportedScenario
    InternalInvariant
    } derive(Eq,
    Debug
    )

    Stable categories returned by protocol parsing and state transitions. Applications should branch on these values instead of matching messages.

    TusLimits

    pub(all) struct TusLimits {
    max_upload_size : Int64
    max_patch_bytes : Int
    max_header_count : Int
    max_header_bytes : Int
    max_metadata_bytes : Int
    max_metadata_entries : Int
    max_path_bytes : Int
    max_identifier_attempts : Int
    } derive(Eq,
    Debug
    )

    Limits are checked before mutation. Defaults are conservative enough for a protocol adapter while remaining useful in tests and local tools.

    TusLimits::default

    fn TusLimits::default() -> TusLimits

    TusLimits::strict_test

    fn TusLimits::strict_test() -> TusLimits

    TusRequest

    pub(all) struct TusRequest {
    http_method : HttpMethod
    path : String
    headers : Headers
    body : Bytes
    } derive(Eq,
    Debug
    )

    Immutable HTTP-shaped input accepted by the protocol engine. No socket or framework type crosses this boundary.

    TusResponse

    pub(all) struct TusResponse {
    status : Int
    headers : Headers
    body : Bytes
    trace : Array[TraceStep]
    error : TusError?
    } derive(Eq,
    Debug
    )

    HTTP-shaped output. Response bodies are small protocol diagnostics only; uploaded bytes are owned by the configured store.

    TusRoute

    pub(all) enum TusRoute {
    Collection
    Resource(String)
    } derive(Eq,
    Debug
    )

    Endpoint location resolved without depending on an HTTP framework router.

    UploadLength

    pub(all) enum UploadLength {
    Known(Int64)
    Deferred
    } derive(Eq,
    Debug
    )

    Declared total size or a promise that a later PATCH will declare it once.

    UploadLength::is_deferred

    fn UploadLength::is_deferred(self : UploadLength) -> Bool

    UploadLength::known

    fn UploadLength::known(self : UploadLength) -> Int64?

    UploadLifecycle

    pub(all) enum UploadLifecycle {
    Active
    Complete
    } derive(Eq,
    Debug
    )

    UploadRecord

    pub(all) struct UploadRecord {
    identifier : String
    offset : Int64
    length : UploadLength
    metadata : Array[MetadataEntry]
    revision : Int64
    lifecycle : UploadLifecycle
    data : Bytes
    } derive(Eq,
    Debug
    )

    Stored protocol state. The body is retained by the in-memory reference store only; external stores may persist it elsewhere behind the same seam.

    UploadRecord::is_complete

    fn UploadRecord::is_complete(self : UploadRecord) -> Bool

    UploadRecord::remaining

    fn UploadRecord::remaining(self : UploadRecord) -> Int64?

    TUS_VERSION

    let TUS_VERSION : String

    Supported tus protocol version. v0.1 intentionally exposes one version so adapters cannot accidentally negotiate behavior that is not implemented.

    append_trace

    fn append_trace(plan : AppendPlan) -> Array[TraceStep]

    apply_method_override

    fn apply_method_override(request : TusRequest) -> Result[TusRequest, TusError]

    Apply the core protocol's X-HTTP-Method-Override before routing. The returned request keeps headers and body but exposes the effective method.

    ascii_equal_ignore_case

    fn ascii_equal_ignore_case(left : String, right : String) -> Bool

    ascii_lower

    fn ascii_lower(value : String) -> String

    available_demos

    fn available_demos() -> Array[String]

    body_length_mismatch

    fn body_length_mismatch(expected : Int, actual : Int) -> TusError

    checked_add_i64

    fn checked_add_i64(left : Int64, right : Int64, field_name? : String) -> Result[Int64, TusError]

    checked_body_end

    fn checked_body_end(offset : Int64, body_length : Int) -> Result[Int64, TusError]

    classify_path

    fn classify_path(path : String, collection_path : String) -> Result[TusRoute, TusError]

    config

    fn config(collection_path? : String, limits? : TusLimits, allow_creation? : Bool, allow_deferred_length? : Bool) -> TusConfig

    conflict_demo_actions

    fn conflict_demo_actions() -> Array[ScenarioAction]

    A stale-offset flow proving that rejected data is never committed.

    core_conformance_vectors

    fn core_conformance_vectors() -> Array[ConformanceVector]

    creation_trace

    fn creation_trace(plan : CreationPlan) -> Array[TraceStep]

    decimal

    fn decimal(value : Int64) -> String

    decode_base64

    fn decode_base64(input : String, max_output : Int) -> Result[Bytes, TusError]

    Decode one strict RFC 4648 value from Upload-Metadata.

    Whitespace, URL-safe symbols, missing padding and non-zero unused bits are rejected. Strictness is intentional: adapters receive a single portable interpretation rather than framework-dependent cleanup behavior.

    deferred_demo_actions

    fn deferred_demo_actions() -> Array[ScenarioAction]

    A Creation-Defer-Length flow declaring its final size in PATCH.

    demo_help

    fn demo_help() -> String

    duplicate_header

    fn duplicate_header(name : String) -> TusError

    encode_base64

    fn encode_base64(input : Bytes) -> String

    Encode arbitrary metadata bytes using the RFC 4648 basic alphabet. The result is always padded and therefore has one canonical spelling.

    error_kind_name

    fn error_kind_name(kind : TusErrorKind) -> String

    failure_response

    fn failure_response(error : TusError, headers? : Headers, trace? : Array[TraceStep]) -> TusResponse

    handle_creation

    fn handle_creation(request : TusRequest, store : MemoryStore, config : TusConfig) -> TusResponse

    Execute a validated Creation request against the reference store.

    handle_head

    fn handle_head(request : TusRequest, store : MemoryStore, config : TusConfig) -> TusResponse

    Evaluate a HEAD request against a stable upload snapshot.

    handle_patch

    fn handle_patch(request : TusRequest, store : MemoryStore, config : TusConfig) -> TusResponse

    Execute one offset-checked PATCH as an atomic state transition.

    head_headers

    fn head_headers(record : UploadRecord) -> Headers

    head_trace

    fn head_trace(record : UploadRecord) -> Array[TraceStep]

    fn header(name : String, value : String) -> HeaderField

    integer_overflow

    fn integer_overflow(name : String, actual : String) -> TusError

    invalid_integer

    fn invalid_integer(name : String, actual : String) -> TusError

    invalid_metadata

    fn invalid_metadata(message : String) -> TusError

    invalid_method

    fn invalid_method(http_method : String) -> TusError

    invalid_path

    fn invalid_path(path : String) -> TusError

    is_canonical_decimal

    fn is_canonical_decimal(value : String) -> Bool

    is_http_token

    fn is_http_token(value : String) -> Bool

    is_metadata_key

    fn is_metadata_key(key : String) -> Bool

    is_safe_identifier

    fn is_safe_identifier(identifier : String) -> Bool

    lifecycle

    fn lifecycle(offset : Int64, length : UploadLength) -> UploadLifecycle

    lifecycle_name

    fn lifecycle_name(value : UploadLifecycle) -> String

    limit_demo_actions

    fn limit_demo_actions() -> Array[ScenarioAction]

    A size-limit flow showing deterministic rejection without allocation.

    metadata_decoded_size

    fn metadata_decoded_size(entries : Array[MetadataEntry]) -> Int

    metadata_encoded_get

    fn metadata_encoded_get(entries : Array[MetadataEntry], key : String) -> String?

    metadata_entry

    fn metadata_entry(key : String, value : Bytes) -> Result[MetadataEntry, TusError]

    metadata_get

    fn metadata_get(entries : Array[MetadataEntry], key : String) -> Bytes?

    missing_header

    fn missing_header(name : String, kind : TusErrorKind) -> TusError

    offset_mismatch

    fn offset_mismatch(expected : Int64, actual : Int64) -> TusError

    optional_defer_length

    fn optional_defer_length(headers : Headers) -> Result[Bool, TusError]

    optional_upload_length

    fn optional_upload_length(headers : Headers) -> Result[Int64?, TusError]

    options_request

    fn options_request(path? : String) -> TusRequest

    parse_http_method

    fn parse_http_method(value : String) -> HttpMethod

    parse_non_negative_i64

    fn parse_non_negative_i64(field_name : String, input : String) -> Result[Int64, TusError]

    Parse a non-negative decimal integer without accepting signs, whitespace, alternate bases, or overflow. tus integer fields use this restricted form.

    parse_non_negative_int

    fn parse_non_negative_int(field_name : String, input : String) -> Result[Int, TusError]

    parse_upload_metadata

    fn parse_upload_metadata(value : String, limits : TusLimits) -> Result[Array[MetadataEntry], TusError]

    Parse Upload-Metadata using the grammar from the tus Creation extension. Entry order is preserved because applications may use it for display.

    plan_append

    fn plan_append(request : TusRequest, record : UploadRecord, config : TusConfig) -> Result[AppendPlan, TusError]

    Validate PATCH headers and current snapshot, then produce a compare-and-swap plan. No bytes are mutated until a storage adapter commits this value.

    plan_creation

    fn plan_creation(request : TusRequest, config : TusConfig) -> Result[CreationPlan, TusError]

    Validate a collection POST and produce an immutable creation plan. v0.1 deliberately creates metadata and length state only; bytes are sent by PATCH so an adapter never needs to buffer two request semantics at once.

    render_response_text

    fn render_response_text(response : TusResponse) -> String

    render_scenario_json

    fn render_scenario_json(report : ScenarioReport) -> String

    Render self-contained JSON without requiring a JSON dependency in the core package. All dynamic strings pass through a conservative control escaper.

    render_scenario_text

    fn render_scenario_text(report : ScenarioReport) -> String

    Render a stable line-oriented report suitable for terminals and snapshots.

    request

    fn request(http_method : String, path : String, headers? : Headers, body? : Bytes) -> TusRequest

    require_content_length

    fn require_content_length(request : TusRequest) -> Result[Int, TusError]

    require_tus_version

    fn require_tus_version(headers : Headers) -> Result[Unit, TusError]

    required_upload_offset

    fn required_upload_offset(headers : Headers) -> Result[Int64, TusError]

    resource_path

    fn resource_path(collection_path : String, identifier : String) -> String

    response_error_code

    fn response_error_code(response : TusResponse) -> String?

    response_offset

    fn response_offset(response : TusResponse) -> Int64?

    run_conformance_suite

    fn run_conformance_suite(vectors : Array[ConformanceVector], config? : TusConfig) -> Result[ConformanceReport, TusError]

    run_conformance_vector

    fn run_conformance_vector(vector : ConformanceVector, config? : TusConfig) -> Result[ConformanceResult, TusError]

    run_named_demo

    fn run_named_demo(name : String, config? : TusConfig) -> Result[ScenarioReport, TusError]

    run_scenario

    fn run_scenario(actions : Array[ScenarioAction], config? : TusConfig) -> Result[ScenarioReport, TusError]

    Execute every action in order. Protocol failures are captured as events and later independent actions still run, making one report useful for teaching both successful and rejected transitions.

    serialize_upload_metadata

    fn serialize_upload_metadata(entries : Array[MetadataEntry]) -> String

    storage_conflict

    fn storage_conflict(identifier : String) -> TusError

    success_response

    fn success_response(status : Int, headers? : Headers, trace? : Array[TraceStep]) -> TusResponse

    successful_demo_actions

    fn successful_demo_actions() -> Array[ScenarioAction]

    A successful two-chunk upload used by the CLI, README and smoke tests.

    supported_extensions

    fn supported_extensions(config : TusConfig) -> Array[String]

    trace

    fn trace(code : String, message : String, protocol_section : String) -> TraceStep

    trim_ows

    fn trim_ows(value : String) -> String

    tus_error

    fn tus_error(kind : TusErrorKind, code : String, message : String, status? : Int, header_name? : String?, expected? : String?, actual? : String?) -> TusError

    unsupported_version

    fn unsupported_version(actual : String) -> TusError

    upload_complete

    fn upload_complete(identifier : String) -> TusError

    upload_length_name

    fn upload_length_name(value : UploadLength) -> String

    upload_not_found

    fn upload_not_found(identifier : String) -> TusError

    upload_record

    fn upload_record(identifier : String, length : UploadLength, metadata? : Array[MetadataEntry]) -> UploadRecord

    upload_too_large

    fn upload_too_large(limit : Int64, actual : Int64) -> TusError

    validate_append_plan

    fn validate_append_plan(current : UploadRecord, plan : AppendPlan, limits : TusLimits) -> Result[Unit, TusError]

    validate_config

    fn validate_config(config : TusConfig) -> Result[Unit, TusError]

    validate_declared_size

    fn validate_declared_size(length : Int64, limits : TusLimits) -> Result[Unit, TusError]

    validate_patch_size

    fn validate_patch_size(body : Bytes, limits : TusLimits) -> Result[Unit, TusError]

    validate_record

    fn validate_record(record : UploadRecord, limits : TusLimits) -> Result[Unit, TusError]

    validate_request

    fn validate_request(request : TusRequest, config : TusConfig) -> Result[TusRoute, TusError]

    Validate generic request constraints, protocol version and endpoint path.

    version_response_headers

    fn version_response_headers() -> Headers