Protocols

← Programming Idioms
STRVCT

Declaring interfaces and verifying conformance at runtime with fail-fast registration checks.

Overview

Protocols are STRVCT's interface system. A protocol defines a set of methods that a class promises to implement. Unlike informal duck-typing (where code checks for individual methods at call sites), protocols name the contract, support inheritance, and verify conformance at class registration time.

The defining feature of STRVCT's protocol engine is fail-fast verification: when a class declares conformance to a protocol via addProtocol(), the framework immediately verifies that all required methods exist. If any method is missing, an error is thrown at startup—moving bugs from rare runtime surprises to instant, deterministic startup failures.

Protocol Declaration SvAudioClipProtocol Extends Protocol class .initThisProtocol() Registers interface stubs Implementer Class initPrototype() this.addProtocol(Protocol) Method Implementations play(), stop(), addDelegate() Startup Conformance Check Set Intersection O(1) Verifies required methods Pass or Instant Error Fails fast at boot if missing

Defining a Protocol (`.initThisProtocol()`)

A protocol inherits from Protocol and declares empty method stubs representing the interface:

(class SvAudioClipProtocol extends Protocol {

    play () {
    }

    stop () {
    }

    addDelegate (audioClipDelegate) {
    }

    removeDelegate (audioClipDelegate) {
    }

}.initThisProtocol());

Protocol Rules:

  • Execute .initThisProtocol() at class registration.
  • Class name must end with Protocol (enforced at startup).
  • Method bodies are empty stubs defining expected parameters.

Implementing & Verifying a Protocol (`addProtocol`)

Implementers call addProtocol() inside initPrototype():

(class SvWaSound extends SvSummaryNode {

    initPrototype () {
        this.addProtocol(SvAudioClipProtocol);
    }

    play () {
        // Implementation
    }

    stop () {
        // Implementation
    }

    addDelegate (delegate) {
        // Implementation
    }

    removeDelegate (delegate) {
        // Implementation
    }

}.initThisClass());

Required and Optional Methods

addProtocol() checks only a protocol's required methods. A protocol names the ones an adopter may leave out with the static optionalMethodNames(); by default there are none, so every declared method is required.

(class SvExampleProtocol extends Protocol {

    static optionalMethodNames () {
        return ["describe"];
    }

    play () {
    }

    describe () {
    }

}.initThisProtocol());

Delegate Protocols

A delegate protocol declares the messages one kind of object sends its delegate through sendDelegateMessage(). A delegate implements only the messages it cares about, so a delegate protocol extends SvDelegateProtocol, which makes every message optional. Adopting one declares who listens; it never fails.

Name a delegate protocol after its sender (SvHtmlStreamReader → SvHtmlStreamReaderDelegateProtocol), and declare exactly what the sender sends, with the arguments it sends them with:

(class SvHtmlStreamReaderDelegateProtocol extends SvDelegateProtocol {

    onHtmlStreamReaderPushNode (reader, node) {
    }

    // ...

}.initThisProtocol());

The sender names its protocol:

delegateProtocol () {
    return SvHtmlStreamReaderDelegateProtocol;
}

and each delegate adopts it in initPrototype() with this.addProtocol(SvHtmlStreamReaderDelegateProtocol).

Two checks keep the declaration and the code from drifting apart:

  • At runtime, sendDelegateMessage() warns once per sender class and message when a sender that names a protocol sends a message the protocol does not declare.
  • In tests, tests/headless/TestDelegateProtocols.js scans the source. Every message a sender sends must be declared, every declared message must be sent (a declared message nobody sends is an invented interface), and every delegate protocol must be named by a sender.

A protocol file loads before any class that adopts it: list it first in its folder's _imports.json, or in a folder that loads earlier.

Protocol Verification API

MethodPurpose & Return Value
addProtocol(protocol)Registers the protocol; throws if a required method is missing.
conformsToProtocol(protocol)true if the class has added the protocol.
assertConformsToProtocol(protocol)Throws if the class has not added the protocol.
methodsConformToProtocol(protocol)true if the object implements every required method.
Protocol.protocolMethodNames()Every method the protocol declares, including those of ancestor protocols.
Protocol.optionalMethodNames() / requiredMethodNames()The declared methods an adopter may leave out / must implement.
Protocol.declaresMethodNamed(name)Whether the protocol declares the method (required or optional).
Protocol.allProtocols()Every protocol class.

Framework Protocols

The protocol files are the source of truth. These are the ones the framework declares:

Capabilities (every method required)

  • SvAudioClipProtocol: something that plays (play(), stop(), addDelegate(), removeDelegate()).
  • SvRecordStoreProtocol: a record store: local, memory or cloud.
  • SvProgressiveImageSourceProtocol: a node whose image arrives in stages (aspect ratio, preview, working, failed).
  • SvCollapsibleRegionProtocol: a region that expands and collapses (isExpanded(), toggleExpanded(), collapsibleAxis()).
  • SvDragSourceProtocol and SvDragDestinationProtocol: the two ends of a drag (onDragSource…, onDragDestination…, acceptsDrop…).

Delegate protocols (every message optional)

ProtocolSenderDelegates
SvAudioClipDelegateProtocolan audio clipSvAudioQueue
SvHtmlStreamReaderDelegateProtocolSvHtmlStreamReaderSvAiParsedResponseMessage
SvXhrRequestDelegateProtocolSvXhrRequestSvAiRequest, SvFileToDownload
SvAiRequestDelegateProtocolSvAiRequestSvAiResponseMessage
SvConversationMessageDelegateProtocolSvConversationMessage and its subclassesSvConversation
SvImagineProImageGenerationDelegateProtocolSvImagineProImageGeneration—
SvRzSigServerConnDelegateProtocolSvRzSigServerConn—
SvRzPeerConnDelegateProtocolSvRzPeerConn—