Skip to content

@fkn/lib/contract

type Account = object;

The signed-in account, or null when nobody is.

image: string | null;
name: string;
premium: boolean;
premiumUntil: string | null;

type AddressLookupResult = object;
address: string;
family: 0 | 4 | 6;

type AdoptRequest = object;
bytes: number;
files: number;

type AdoptState = object;
bytes: number;
files: number;

type ConflictChoice = "local" | "cloud" | null;

type ConflictRequest = object;
cloud: ConflictSide;
local: ConflictSide;
path: string;

type ConflictSide = object;
size: number;
updatedAt: string | null;

type ConnectAvailability = "connected" | "disconnected" | "unknown";

Three answers where available() has two. disconnected is an ANSWER and unknown is nobody having been asked; the library’s local-first queue drops its cloud obligation only on the first and keeps the write queued on the second.


type ConnectOptions = object;
optional protocol?: string;

opaque contract tag delivered to the package’s onConnect, e.g. ‘stub-source@1’


type DisplayCause = "not-rendered" | "clipped" | "not-shown";

type DisplayLevel = "full" | "liveness";

full means this engine reported a boolean isVisible; liveness means we only know it paints


type DnsLookup = <T>(hostname, options?) => Promise<T extends true ? AddressLookupResult[] : AddressLookupResult | undefined>;

T extends boolean = false

string

T

0 | 4 | 6

Promise<T extends true ? AddressLookupResult[] : AddressLookupResult | undefined>


type EncryptionState = object;

Encryption state of the account’s cloud storage.

enrolled: boolean;
keyEpoch: number | null;
unlocked: boolean;

type FrameConsentRequest = object;
hosts: string[];
scope: FrameFetchScope;

type FrameFetchScope = "frame.fetchRead" | "frame.fetchWrite";

type HiddenSurface = object;
cause: DisplayCause;
kind: string;
level: DisplayLevel;

type InstalledPackage = object;
installedAt: number;
name: string;
uri: string;
version: string | null;

null when the handler addresses code directly and has no version to pin, e.g. a dev server


type InstallOptions = object;
optional noConfirm?: boolean;

skip the confirm prompt and report the install with a notice instead; the record stays scoped to the calling app

optional version?: string;

exact version to pin; defaults to the packument’s latest dist-tag


type IpFamily = "IPv4" | "IPv6";

type MountDescriptor = object;

Everything an app needs to mount a package’s tenant in its OWN document.

from: string;

the connecting app, as the BROKER knows it

name: string;
origin: string;

the tenant origin alone, which is what a postMessage to that frame must target

uri: string;

the version-free identity, echoed to the package in the port message

url: string;

where to point the iframe: the tenant origin plus the package’s path

version: string | null;

type OverlayRect = object;

Viewport rect plus optional corner radii, as the overlay host reports them.

height: number;
optional radius?: [number, number, number, number];
width: number;
x: number;
y: number;

type OverlayState = object;

What the host page pushes to the broker frame so its UI stays clickable through app chrome.

hidden: HiddenSurface[];
inset: object;
top: number;
modal: boolean;
rects: OverlayRect[];
view: object;
height: number;
width: number;

type PackageQuery = object;
optional id?: string;

host app scope, e.g. ‘stub’ - becomes the keyword fkn-<type>--<id>

optional origin?: "npm";

package source; npm is the only origin implemented

optional size?: number;

result count, clamped to 1..100

optional text?: string;

free text mixed into the registry query

type: string;

package kind, e.g. ‘plugin’ - becomes the keyword fkn-type:<type>


type PackageResult = object;
description: string;
downloadsMonthly: number | null;
installed: boolean;

installed by the calling app

keywords: string[];
links: object;
optional homepage?: string;
optional npm?: string;
optional repository?: string;
name: string;
origin: "npm";
publisher: string | null;
uri: string;

normalized version-free uri, e.g. ‘npm:@banou/stub-plugin-foo’

version: string;

latest version per the search index - display only, install re-resolves from the packument


type PackagesErrorCode =
| "invalid"
| "not-installed"
| "unaddressable"
| "timeout"
| "unavailable"
| "denied";

type PackagesFail = object;
error: PackagesErrorCode;
message: string;

type PickOptions = object;
optional multiple?: boolean;
optional title?: string;

untrusted, rendered as text in the picker header


type Placement = object;
optional clip?: SurfaceRect;

the part of rect still visible after the placeholder’s scroll ancestors clip it

optional radius?: Radii;

the placeholder’s corner radii, so the frame follows a rounded container

rect: SurfaceRect;

where the package frame sits, so its own layout gets the full box


type ProxyFetch = (input, init) => Promise<Response>;

ProxyFetchInput

ProxyFetchInit

Promise<Response>


type ProxyFetchInit = RequestInit & object | undefined;

type ProxyFetchInput = string | URL | Request;

type Quota = object;

Metered proxy usage for the account behind the broker.

bytesPerSecond: number;
limitBytes: number;
overQuota: boolean;
premium: boolean;
remaining: number;
usedBytes: number;

type Radii = [number, number, number, number];

corner radii in css order: top-left, top-right, bottom-right, bottom-left


type Resolvers = object;

Everything the broker exposes over osra.

The flat members at the bottom duplicate members of cloud and overlay. They predate the namespaced form and are kept because a published consumer may still be calling them: they are contract, not dead code.

account: object;
info: () => Promise<Account | null>;

Promise<Account | null>

login: (consumerOrigin) => Promise<boolean>;

string

Promise<boolean>

logout: () => void;

void

onChange: (listener) => () => void;

() => void

() => void

cloud: object;
dns: object;
lookup: DnsLookup;
fetch: ProxyFetch;
fs: object;
availability: () => Promise<ConnectAvailability>;

Promise<ConnectAvailability>

available: () => Promise<boolean>;

Promise<boolean>

encryption: () => Promise<EncryptionState>;

Promise<EncryptionState>

list: () => Promise<StorageEntry[]>;

Promise<StorageEntry[]>

promptAdopt: (request) => Promise<boolean>;

AdoptRequest

Promise<boolean>

promptConflict: (request) => Promise<ConflictChoice>;

ConflictRequest

Promise<ConflictChoice>

quota: () => Promise<StorageQuota>;

Promise<StorageQuota>

readFile: (path) => Promise<Uint8Array>;

string

Promise<Uint8Array>

readFileSealed: (path) => Promise<{
bytes: Uint8Array;
sealedAt: number | null;
}>;

The same read as readFile, plus the seal time the envelope authenticates, in milliseconds, or null for an envelope that carries no such field.

Authenticated means the server can neither forge nor alter it, because it is covered by the envelope’s AAD; it is still the WRITER’s own claim about when it sealed, so on its own it does not prove that this copy is the newest one.

string

Promise<{ bytes: Uint8Array; sealedAt: number | null; }>

remove: (path) => Promise<void>;

string

Promise<void>

setAdoptSource: (next, run) => void;

AdoptState | null

(() => Promise<void>) | null

void

unlock: () => Promise<boolean>;

Promise<boolean>

writeFile: (path, data, contentType) => Promise<void>;

string

WriteData

string | null

Promise<void>

quota: () => Promise<Quota>;

Promise<Quota>

webvpn: object;
tcpSocket: (options) => Promise<TcpSocketResult>;

TcpSocketOptions

Promise<TcpSocketResult>

tcpSocketListener: (options) => Promise<TcpSocketListenerResult>;

TcpSocketListenerOptions

Promise<TcpSocketListenerResult>

udpSocket: (options) => Promise<UdpSocketResult>;

UdpSocketOptions

Promise<UdpSocketResult>

connect: object;
prompt: (consumerOrigin) => Promise<boolean>;

string

Promise<boolean>

dnsLookup: DnsLookup;
frameConsent: object;
ensure: (request) => Promise<boolean>;

FrameConsentRequest

Promise<boolean>

hideInstallPrompt: () => void;

void

installPrompt: object;
hide: () => void;

void

show: (reason?) => Promise<void>;

string

Promise<void>

overlay: object;
setHost: SetOverlayHost;
packages: object;
connect: (uri, options?) => Promise<
| PackagesFail
| {
closed: Promise<void>;
port: MessagePort;
}>;

string

ConnectOptions

Promise< | PackagesFail | { closed: Promise<void>; port: MessagePort; }>

frame: (uri) => Promise<PackagesFail | MountDescriptor>;

string

Promise<PackagesFail | MountDescriptor>

hide: (uri) => Promise<
| PackagesFail
| {
ok: true;
}>;

string

Promise< | PackagesFail | { ok: true; }>

install: (uri, options?) => Promise<
| PackagesFail
| {
installed: InstalledPackage;
}
| {
declined: true;
}>;

string

InstallOptions

Promise< | PackagesFail | { installed: InstalledPackage; } | { declined: true; }>

list: () => Promise<{
results: InstalledPackage[];
}>;

Promise<{ results: InstalledPackage[]; }>

pick: (query, options?) => Promise<
| PackagesFail
| {
failed: string[];
results: PackageResult[];
}
| {
declined: true;
}>;

PackageQuery

PickOptions

Promise< | PackagesFail | { failed: string[]; results: PackageResult[]; } | { declined: true; }>

search: (query) => Promise<
| PackagesFail
| {
results: PackageResult[];
}>;

PackageQuery

Promise< | PackagesFail | { results: PackageResult[]; }>

show: (uri, placement) => Promise<
| PackagesFail
| {
ok: true;
}>;

string

Placement

Promise< | PackagesFail | { ok: true; }>

uninstall: (uri) => Promise<
| PackagesFail
| {
ok: true;
}>;

string

Promise< | PackagesFail | { ok: true; }>

proxyFetch: ProxyFetch;
relay: object;
prompt: () => Promise<string>;

Promise<string>

rooms: object;
available: () => Promise<boolean>;

Promise<boolean>

create: (options?) => Promise<RoomsFail | RoomHandle>;

RoomCreateOptions

Promise<RoomsFail | RoomHandle>

join: (invite, options?) => Promise<RoomsFail | RoomHandle>;

string

RoomJoinOptions

Promise<RoomsFail | RoomHandle>

setOverlayHost: SetOverlayHost;
shell: object;
applyUpdate: () => Promise<boolean>;

Promise<boolean>

onUpdate: (callback) => () => void;

() => void

() => void

onUpdateTaken: (callback) => () => void;

() => void

() => void

updateReady: () => Promise<boolean>;

Promise<boolean>

showInstallPrompt: (reason?) => Promise<void>;

string

Promise<void>

webVpnTcpSocket: (options) => Promise<TcpSocketResult>;

TcpSocketOptions

Promise<TcpSocketResult>

webVpnTcpSocketListener: (options) => Promise<TcpSocketListenerResult>;

TcpSocketListenerOptions

Promise<TcpSocketListenerResult>

webVpnUdpSocket: (options) => Promise<UdpSocketResult>;

UdpSocketOptions

Promise<UdpSocketResult>


type RoomCreateOptions = object;
optional defaults?: Partial<RoomDefaults>;
optional members?: number;
optional signal?: AbortSignal;

aborting it leaves the room; it never bounds the wait for the broker


type RoomDefaults = Readonly<{
receive: boolean;
send: boolean;
}>;

type RoomEnd = object;

Why the room ended for this app. It ends once.

reason: "left" | "removed" | "blocked" | "ended" | "unavailable";

type RoomEvent =
| {
message: RoomMessage;
type: "message";
}
| {
member: RoomMember;
type: "joined";
}
| {
id: string;
reason: "left" | "removed" | "blocked";
type: "left";
}
| {
id: string;
permissions: RoomPermissions;
type: "permissions";
}
| {
defaults: RoomDefaults;
type: "defaults";
};

type RoomHandle = object;

A joined room, held by the broker: it owns the socket, the room key and the derived message key, and hands the app plaintext and member ids only.

Every member that can be refused answers with a RoomsFail rather than throwing, so a refusal survives the osra hop as data. leave and on cannot be refused.

block: (id) => Promise<
| RoomsFail
| {
ok: true;
}>;

string

Promise< | RoomsFail | { ok: true; }>

closed: Promise<RoomEnd>;

settles once, when the room ends for this app. Never rejects.

defaults: () => Promise<RoomDefaults>;

Promise<RoomDefaults>

grant: (id, permission) => Promise<
| RoomsFail
| {
ok: true;
}>;

string

RoomPermission

Promise< | RoomsFail | { ok: true; }>

id: string;

the room’s uuid

invite: string;

id and key as one string, the thing to put in a link

key: string;

the room key, base64url. The api never sees it. Anyone holding it and the id can join.

leave: () => Promise<void>;

Promise<void>

members: () => Promise<RoomMember[]>;

Promise<RoomMember[]>

on: (listener) => () => void;

the account.onChange shape: one broker-side registration, unsubscribed by the returned function

(event) => void

() => void

owner: string;
remove: (id) => Promise<
| RoomsFail
| {
ok: true;
}>;

string

Promise< | RoomsFail | { ok: true; }>

revoke: (id, permission) => Promise<
| RoomsFail
| {
ok: true;
}>;

string

RoomPermission

Promise< | RoomsFail | { ok: true; }>

self: RoomMember;
send: (text) => Promise<
| RoomsFail
| {
ok: true;
}>;

at most 4,096 bytes of UTF-8, sealed before it leaves the browser; never truncated

string

Promise< | RoomsFail | { ok: true; }>

setDefault: (permission, value) => Promise<
| RoomsFail
| {
ok: true;
}>;

"send" | "receive"

boolean

Promise< | RoomsFail | { ok: true; }>

unblock: (id) => Promise<
| RoomsFail
| {
ok: true;
}>;

string

Promise< | RoomsFail | { ok: true; }>


type RoomJoinOptions = object;
optional signal?: AbortSignal;

type RoomMember = object;

A participant as this room sees them. The id is fresh in every room.

id: string;
permissions: RoomPermissions;

type RoomMessage = object;
at: number;
from: string;
seq: number;
text: string;

type RoomPermission = "send" | "receive" | "remove" | "block";

type RoomPermissions = Readonly<Record<RoomPermission, boolean>>;

type RoomsErrorCode =
| "invalid"
| "not-found"
| "bad-key"
| "full"
| "blocked"
| "denied"
| "rate-limited"
| "too-large"
| "unavailable"
| "closed";

type RoomsFail = object;

A refusal, as data. packages crosses the hop the same way, and for the same reason.

error: RoomsErrorCode;
message: string;

type SetOverlayHost = (push, options?) => void;

(state) => unknown

boolean

void


type StorageEntry = object;
contentType: string | null;
encryption: string | null;
path: string;
size: number;
updatedAt: string;

type StorageQuota = object;
limitBytes: number;
maxObjects: number;
objects: number;
remaining: number;
usedBytes: number;

type SurfaceRect = object;

viewport coordinates, the space both the app and the broker frame measure in

height: number;
width: number;
x: number;
y: number;

type TcpSocketListenerOptions = object;
localAddress: string;
localPort: number;
optional onClose?: (error?) => void | Promise<void>;

Error

void | Promise<void>

onConnection: (connection) => void | Promise<void>;

TcpSocketResult

void | Promise<void>


type TcpSocketListenerResult = object;
close: () => Promise<void>;

Promise<void>

localAddress: string;
localFamily: IpFamily;
localPort: number;

type TcpSocketOptions = object;
remoteAddress: string;
remotePort: number;

type TcpSocketResult = object;
dataReadableStream: ReadableStream<Uint8Array>;
dataWritableStream: WritableStream<Uint8Array>;
destroy: () => Promise<void>;

Promise<void>

destroySoon: () => Promise<void>;

Promise<void>

end: () => Promise<void>;

Promise<void>

localAddress: string;
localFamily: IpFamily;
localPort: number;
remoteAddress: string;
remoteFamily: IpFamily;
remotePort: number;
resetAndDestroy: () => Promise<void>;

Promise<void>

setOption: (option) => Promise<void>;

TcpSocketOption

Promise<void>


type UdpDatagram = object;
address: string;
data: ArrayBuffer;
family: IpFamily;
port: number;
size: number;

type UdpSocketOptions = object;
address: string;
optional dataPort?: boolean;
port: number;
type: "udp4" | "udp6";

type UdpSocketResult = object;
close: () => Promise<void>;

Promise<void>

closed: Promise<{
reason: string;
}>;
connect: (options) => Promise<{
address: string;
family: IpFamily;
local: boolean;
port: number;
}>;

string

number

Promise<{ address: string; family: IpFamily; local: boolean; port: number; }>

dataPort: MessagePort | undefined;
dataPortAcks: true | undefined;
dataReadableStream: ReadableStream<UdpDatagram>;
disconnect: () => Promise<void>;

Promise<void>

localAddress: string;
localFamily: IpFamily;
localPort: number;
send: (options) => Promise<void>;

string

ArrayBuffer

number

Promise<void>

setOption: (option) => Promise<void>;

UdpSocketOption

Promise<void>

socketId: number;

type WriteData = ArrayBuffer | Uint8Array | string;

Re-exports TcpSocketOption


Re-exports UdpSocketOption