Skip to content

TypeScript

Most of the types you will reach for are the types of values you already hold: Frame is what attachFrame resolves to, Locator is what locator() returns, and QuotaStatus is what cloud.quota() resolves to. This page covers which entry point exports each name, how to spell the shapes @fkn/lib declares but does not export, what the broker’s own vocabulary in @fkn/lib/contract is for (the broker is the connection your app holds into FKN), and how to tell an error you can test with instanceof from one you match by message.

The three you will meet first, with the entry each comes from:

app.ts
import {
const attachFrame: (options: AttachFrameOptions) => Promise<Frame>
attachFrame
,
(alias) namespace cloud
import cloud
cloud
} from '@fkn/lib'
import type {
type Frame = {
readonly locator: (selector: string) => Locator$1<Extended<{
readonly element: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly getByRole: {
readonly to: "element";
readonly resolve: (context: LocatorContext, role: string) => Element[];
readonly render: (role: unknown) => {
fragment: string;
};
};
readonly getByText: {
readonly to: "element";
readonly resolve: (context: LocatorContext, text: string) => Element[];
readonly render: (text: unknown) => {
fragment: string;
};
};
readonly getByTestId: {
readonly to: "element";
readonly resolve: (context: LocatorContext, testId: string) => Element[];
readonly render: (testId: unknown) => {
fragment: string;
};
};
readonly first: {
readonly to: "element";
readonly resolve: (context: LocatorContext) => Element[];
readonly render: () => {
fragment: string;
};
};
readonly nth: {
readonly to: "element";
readonly resolve: (context: LocatorContext, index: number) => Element[];
readonly render: (index: unknown) => {
fragment: string;
};
};
};
readonly operations: {
readonly click: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly fill: {
readonly resolve: (context: LocatorContext, value: string, _options?: OperationOptions) => void;
};
readonly hover: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly textContent: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => string;
};
readonly getAttribute: {
readonly resolve: (context: LocatorContext, name: string, _options?: OperationOptions) => string | null;
};
readonly isVisible: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
readonly count: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => number;
};
readonly exists: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
};
};
readonly frame: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly owner: {
readonly to: "frame";
readonly barrier: "up";
readonly resolve ...
Frame
,
type Locator = {
readonly locator: (selector: string) => Locator$1<Extended<{
readonly element: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly getByRole: {
readonly to: "element";
readonly resolve: (context: LocatorContext, role: string) => Element[];
readonly render: (role: unknown) => {
fragment: string;
};
};
readonly getByText: {
readonly to: "element";
readonly resolve: (context: LocatorContext, text: string) => Element[];
readonly render: (text: unknown) => {
fragment: string;
};
};
readonly getByTestId: {
readonly to: "element";
readonly resolve: (context: LocatorContext, testId: string) => Element[];
readonly render: (testId: unknown) => {
fragment: string;
};
};
readonly first: {
readonly to: "element";
readonly resolve: (context: LocatorContext) => Element[];
readonly render: () => {
fragment: string;
};
};
readonly nth: {
readonly to: "element";
readonly resolve: (context: LocatorContext, index: number) => Element[];
readonly render: (index: unknown) => {
fragment: string;
};
};
};
readonly operations: {
readonly click: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly fill: {
readonly resolve: (context: LocatorContext, value: string, _options?: OperationOptions) => void;
};
readonly hover: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly textContent: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => string;
};
readonly getAttribute: {
readonly resolve: (context: LocatorContext, name: string, _options?: OperationOptions) => string | null;
};
readonly isVisible: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
readonly count: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => number;
};
readonly exists: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
};
};
readonly frame: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly owner: {
readonly to: "frame";
readonly barrier: "up";
readonly resolve ...
Locator
} from '@fkn/lib'
import type {
type QuotaStatus = {
premium: boolean;
overQuota: boolean;
throttled: boolean;
usedBytes: number;
limitBytes: number;
remainingBytes: number;
bytesPerSecond: number;
bitsPerSecond: number;
}
QuotaStatus
} from '@fkn/lib/cloud'
const
const frame: Frame
frame
:
type Frame = {
readonly locator: (selector: string) => Locator$1<Extended<{
readonly element: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly getByRole: {
readonly to: "element";
readonly resolve: (context: LocatorContext, role: string) => Element[];
readonly render: (role: unknown) => {
fragment: string;
};
};
readonly getByText: {
readonly to: "element";
readonly resolve: (context: LocatorContext, text: string) => Element[];
readonly render: (text: unknown) => {
fragment: string;
};
};
readonly getByTestId: {
readonly to: "element";
readonly resolve: (context: LocatorContext, testId: string) => Element[];
readonly render: (testId: unknown) => {
fragment: string;
};
};
readonly first: {
readonly to: "element";
readonly resolve: (context: LocatorContext) => Element[];
readonly render: () => {
fragment: string;
};
};
readonly nth: {
readonly to: "element";
readonly resolve: (context: LocatorContext, index: number) => Element[];
readonly render: (index: unknown) => {
fragment: string;
};
};
};
readonly operations: {
readonly click: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly fill: {
readonly resolve: (context: LocatorContext, value: string, _options?: OperationOptions) => void;
};
readonly hover: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly textContent: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => string;
};
readonly getAttribute: {
readonly resolve: (context: LocatorContext, name: string, _options?: OperationOptions) => string | null;
};
readonly isVisible: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
readonly count: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => number;
};
readonly exists: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
};
};
readonly frame: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly owner: {
readonly to: "frame";
readonly barrier: "up";
readonly resolve ...
Frame
= await
function attachFrame(options: AttachFrameOptions): Promise<Frame>
attachFrame
({
iframe: HTMLIFrameElement
iframe
:
var document: Document

window.document returns a reference to the document contained in the window.

MDN Reference

document
.
ParentNode.querySelector<"iframe">(selectors: "iframe"): HTMLIFrameElement | null (+4 overloads)

Returns the first element that is a descendant of node that matches selectors.

MDN Reference

querySelector
('iframe')! }) // the extension backend when it is exposed, the cloud backend otherwise
const
const rows: Locator
rows
:
type Locator = {
readonly locator: (selector: string) => Locator$1<Extended<{
readonly element: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly getByRole: {
readonly to: "element";
readonly resolve: (context: LocatorContext, role: string) => Element[];
readonly render: (role: unknown) => {
fragment: string;
};
};
readonly getByText: {
readonly to: "element";
readonly resolve: (context: LocatorContext, text: string) => Element[];
readonly render: (text: unknown) => {
fragment: string;
};
};
readonly getByTestId: {
readonly to: "element";
readonly resolve: (context: LocatorContext, testId: string) => Element[];
readonly render: (testId: unknown) => {
fragment: string;
};
};
readonly first: {
readonly to: "element";
readonly resolve: (context: LocatorContext) => Element[];
readonly render: () => {
fragment: string;
};
};
readonly nth: {
readonly to: "element";
readonly resolve: (context: LocatorContext, index: number) => Element[];
readonly render: (index: unknown) => {
fragment: string;
};
};
};
readonly operations: {
readonly click: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly fill: {
readonly resolve: (context: LocatorContext, value: string, _options?: OperationOptions) => void;
};
readonly hover: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly textContent: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => string;
};
readonly getAttribute: {
readonly resolve: (context: LocatorContext, name: string, _options?: OperationOptions) => string | null;
};
readonly isVisible: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
readonly count: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => number;
};
readonly exists: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
};
};
readonly frame: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly owner: {
readonly to: "frame";
readonly barrier: "up";
readonly resolve ...
Locator
=
const frame: Frame
frame
.
locator: (selector: string) => Locator$1<Extended<{
readonly element: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly getByRole: {
readonly to: "element";
readonly resolve: (context: LocatorContext, role: string) => Element[];
readonly render: (role: unknown) => {
fragment: string;
};
};
readonly getByText: {
readonly to: "element";
readonly resolve: (context: LocatorContext, text: string) => Element[];
readonly render: (text: unknown) => {
fragment: string;
};
};
readonly getByTestId: {
readonly to: "element";
readonly resolve: (context: LocatorContext, testId: string) => Element[];
readonly render: (testId: unknown) => {
fragment: string;
};
};
readonly first: {
readonly to: "element";
readonly resolve: (context: LocatorContext) => Element[];
readonly render: () => {
fragment: string;
};
};
readonly nth: {
readonly to: "element";
readonly resolve: (context: LocatorContext, index: number) => Element[];
readonly render: (index: unknown) => {
fragment: string;
};
};
};
readonly operations: {
readonly click: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly fill: {
readonly resolve: (context: LocatorContext, value: string, _options?: OperationOptions) => void;
};
readonly hover: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly textContent: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => string;
};
readonly getAttribute: {
readonly resolve: (context: LocatorContext, name: string, _options?: OperationOptions) => string | null;
};
readonly isVisible: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
readonly count: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => number;
};
readonly exists: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
};
};
readonly frame: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly owner: {
readonly to: "frame";
readonly barrier: "up";
readonly resolve: (_context: LocatorContext) => Element[];
readonly render: () => {
fragment: string;
separator: "up";
};
};
};
readonly operations: {
readonly addStyleTag: {
readonly resolve: (context: LocatorContext, options: AddStyleTagOptions$1) => void;
};
readonly fetch: {
readonly kind: ChainKind;
readonly ...
locator
('.result') // a chain, nothing touches the page yet
const
const quota: cloud.QuotaStatus
quota
:
type QuotaStatus = {
premium: boolean;
overQuota: boolean;
throttled: boolean;
usedBytes: number;
limitBytes: number;
remainingBytes: number;
bytesPerSecond: number;
bitsPerSecond: number;
}
QuotaStatus
= await
(alias) namespace cloud
import cloud
cloud
.
cloud_d_exports.quota(): Promise<QuotaStatus>
export cloud_d_exports.quota
quota
()
const
const total: number
total
= await
const rows: Locator
rows
.
count: (_options?: LocatorOptions | undefined) => Promise<number>
count
() // the first call that touches the page
const quota: cloud.QuotaStatus
quota
.
throttled: boolean

transfers are actually being rate-limited right now (overQuota and not premium)

throttled
// false while today's free volume is left

Each annotation names a type the call already infers, so the imports are the only new thing. The namespaces carry their types too, so cloud.QuotaStatus, fs.Stats and packages.PackagesError all resolve from a root import. A named export usually has an entry in the generated API reference, the pages built from the declarations @fkn/lib ships. A shape declared under no exported name never does, and this page shows how to derive each of those.

Frame and Locator come from the root and from @fkn/lib/extension, with AttachFrameOptions, GotoOptions, RemoteVideoElement and VideoElementState beside them. The option and result shapes are declared but not exported by name, so derive them from the members that take or return them:

app.ts
import type {
type Frame = {
readonly locator: (selector: string) => Locator$1<Extended<{
readonly element: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly getByRole: {
readonly to: "element";
readonly resolve: (context: LocatorContext, role: string) => Element[];
readonly render: (role: unknown) => {
fragment: string;
};
};
readonly getByText: {
readonly to: "element";
readonly resolve: (context: LocatorContext, text: string) => Element[];
readonly render: (text: unknown) => {
fragment: string;
};
};
readonly getByTestId: {
readonly to: "element";
readonly resolve: (context: LocatorContext, testId: string) => Element[];
readonly render: (testId: unknown) => {
fragment: string;
};
};
readonly first: {
readonly to: "element";
readonly resolve: (context: LocatorContext) => Element[];
readonly render: () => {
fragment: string;
};
};
readonly nth: {
readonly to: "element";
readonly resolve: (context: LocatorContext, index: number) => Element[];
readonly render: (index: unknown) => {
fragment: string;
};
};
};
readonly operations: {
readonly click: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly fill: {
readonly resolve: (context: LocatorContext, value: string, _options?: OperationOptions) => void;
};
readonly hover: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly textContent: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => string;
};
readonly getAttribute: {
readonly resolve: (context: LocatorContext, name: string, _options?: OperationOptions) => string | null;
};
readonly isVisible: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
readonly count: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => number;
};
readonly exists: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
};
};
readonly frame: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly owner: {
readonly to: "frame";
readonly barrier: "up";
readonly resolve ...
Frame
,
type Locator = {
readonly locator: (selector: string) => Locator$1<Extended<{
readonly element: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly getByRole: {
readonly to: "element";
readonly resolve: (context: LocatorContext, role: string) => Element[];
readonly render: (role: unknown) => {
fragment: string;
};
};
readonly getByText: {
readonly to: "element";
readonly resolve: (context: LocatorContext, text: string) => Element[];
readonly render: (text: unknown) => {
fragment: string;
};
};
readonly getByTestId: {
readonly to: "element";
readonly resolve: (context: LocatorContext, testId: string) => Element[];
readonly render: (testId: unknown) => {
fragment: string;
};
};
readonly first: {
readonly to: "element";
readonly resolve: (context: LocatorContext) => Element[];
readonly render: () => {
fragment: string;
};
};
readonly nth: {
readonly to: "element";
readonly resolve: (context: LocatorContext, index: number) => Element[];
readonly render: (index: unknown) => {
fragment: string;
};
};
};
readonly operations: {
readonly click: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly fill: {
readonly resolve: (context: LocatorContext, value: string, _options?: OperationOptions) => void;
};
readonly hover: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly textContent: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => string;
};
readonly getAttribute: {
readonly resolve: (context: LocatorContext, name: string, _options?: OperationOptions) => string | null;
};
readonly isVisible: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
readonly count: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => number;
};
readonly exists: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
};
};
readonly frame: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly owner: {
readonly to: "frame";
readonly barrier: "up";
readonly resolve ...
Locator
} from '@fkn/lib'
type
type ReadOptions = OperationOptions & {
reason?: string;
}
ReadOptions
=
type NonNullable<T> = T & {}

Exclude null and undefined from T

NonNullable
<
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never

Obtain the parameters of a function type in a tuple

Parameters
<
type Locator = {
readonly locator: (selector: string) => Locator$1<Extended<{
readonly element: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly getByRole: {
readonly to: "element";
readonly resolve: (context: LocatorContext, role: string) => Element[];
readonly render: (role: unknown) => {
fragment: string;
};
};
readonly getByText: {
readonly to: "element";
readonly resolve: (context: LocatorContext, text: string) => Element[];
readonly render: (text: unknown) => {
fragment: string;
};
};
readonly getByTestId: {
readonly to: "element";
readonly resolve: (context: LocatorContext, testId: string) => Element[];
readonly render: (testId: unknown) => {
fragment: string;
};
};
readonly first: {
readonly to: "element";
readonly resolve: (context: LocatorContext) => Element[];
readonly render: () => {
fragment: string;
};
};
readonly nth: {
readonly to: "element";
readonly resolve: (context: LocatorContext, index: number) => Element[];
readonly render: (index: unknown) => {
fragment: string;
};
};
};
readonly operations: {
readonly click: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly fill: {
readonly resolve: (context: LocatorContext, value: string, _options?: OperationOptions) => void;
};
readonly hover: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly textContent: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => string;
};
readonly getAttribute: {
readonly resolve: (context: LocatorContext, name: string, _options?: OperationOptions) => string | null;
};
readonly isVisible: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
readonly count: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => number;
};
readonly exists: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
};
};
readonly frame: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly owner: {
readonly to: "frame";
readonly barrier: "up";
readonly resolve ...
Locator
['textContent']>[0]> // { timeout?: number; reason?: string }
type
type ClickOptions = OperationOptions & {
reason?: string;
} & {
position?: {
x?: number;
y?: number;
};
}
ClickOptions
=
type NonNullable<T> = T & {}

Exclude null and undefined from T

NonNullable
<
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never

Obtain the parameters of a function type in a tuple

Parameters
<
type Locator = {
readonly locator: (selector: string) => Locator$1<Extended<{
readonly element: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly getByRole: {
readonly to: "element";
readonly resolve: (context: LocatorContext, role: string) => Element[];
readonly render: (role: unknown) => {
fragment: string;
};
};
readonly getByText: {
readonly to: "element";
readonly resolve: (context: LocatorContext, text: string) => Element[];
readonly render: (text: unknown) => {
fragment: string;
};
};
readonly getByTestId: {
readonly to: "element";
readonly resolve: (context: LocatorContext, testId: string) => Element[];
readonly render: (testId: unknown) => {
fragment: string;
};
};
readonly first: {
readonly to: "element";
readonly resolve: (context: LocatorContext) => Element[];
readonly render: () => {
fragment: string;
};
};
readonly nth: {
readonly to: "element";
readonly resolve: (context: LocatorContext, index: number) => Element[];
readonly render: (index: unknown) => {
fragment: string;
};
};
};
readonly operations: {
readonly click: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly fill: {
readonly resolve: (context: LocatorContext, value: string, _options?: OperationOptions) => void;
};
readonly hover: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly textContent: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => string;
};
readonly getAttribute: {
readonly resolve: (context: LocatorContext, name: string, _options?: OperationOptions) => string | null;
};
readonly isVisible: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
readonly count: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => number;
};
readonly exists: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
};
};
readonly frame: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly owner: {
readonly to: "frame";
readonly barrier: "up";
readonly resolve ...
Locator
['click']>[0]> // the same plus position?: { x?: number; y?: number }
type
type FrameFetchResult = {
status: number;
statusText: string;
ok: boolean;
url: string;
redirected: boolean;
type: ResponseType;
headers: [string, string][];
body: ArrayBuffer;
}
FrameFetchResult
=
type Awaited<T> = T extends null | undefined ? T : T extends object & {
then(onfulfilled: infer F, ...args: infer _): any;
} ? F extends (value: infer V, ...args: infer _) => any ? Awaited<V> : never : T

Recursively unwraps the "awaited type" of a type. Non-promise "thenables" should resolve to never. This emulates the behavior of await.

Awaited
<
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any

Obtain the return type of a function type

ReturnType
<
type Frame = {
readonly locator: (selector: string) => Locator$1<Extended<{
readonly element: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly getByRole: {
readonly to: "element";
readonly resolve: (context: LocatorContext, role: string) => Element[];
readonly render: (role: unknown) => {
fragment: string;
};
};
readonly getByText: {
readonly to: "element";
readonly resolve: (context: LocatorContext, text: string) => Element[];
readonly render: (text: unknown) => {
fragment: string;
};
};
readonly getByTestId: {
readonly to: "element";
readonly resolve: (context: LocatorContext, testId: string) => Element[];
readonly render: (testId: unknown) => {
fragment: string;
};
};
readonly first: {
readonly to: "element";
readonly resolve: (context: LocatorContext) => Element[];
readonly render: () => {
fragment: string;
};
};
readonly nth: {
readonly to: "element";
readonly resolve: (context: LocatorContext, index: number) => Element[];
readonly render: (index: unknown) => {
fragment: string;
};
};
};
readonly operations: {
readonly click: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly fill: {
readonly resolve: (context: LocatorContext, value: string, _options?: OperationOptions) => void;
};
readonly hover: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly textContent: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => string;
};
readonly getAttribute: {
readonly resolve: (context: LocatorContext, name: string, _options?: OperationOptions) => string | null;
};
readonly isVisible: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
readonly count: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => number;
};
readonly exists: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
};
};
readonly frame: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly owner: {
readonly to: "frame";
readonly barrier: "up";
readonly resolve ...
Frame
['fetch']>> // { status, statusText, ok, url, redirected, type, headers: [string, string][], body: ArrayBuffer }
const
const readAll: (list: Locator, options?: ReadOptions) => Promise<string[]>
readAll
= async (
list: Locator
list
:
type Locator = {
readonly locator: (selector: string) => Locator$1<Extended<{
readonly element: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly getByRole: {
readonly to: "element";
readonly resolve: (context: LocatorContext, role: string) => Element[];
readonly render: (role: unknown) => {
fragment: string;
};
};
readonly getByText: {
readonly to: "element";
readonly resolve: (context: LocatorContext, text: string) => Element[];
readonly render: (text: unknown) => {
fragment: string;
};
};
readonly getByTestId: {
readonly to: "element";
readonly resolve: (context: LocatorContext, testId: string) => Element[];
readonly render: (testId: unknown) => {
fragment: string;
};
};
readonly first: {
readonly to: "element";
readonly resolve: (context: LocatorContext) => Element[];
readonly render: () => {
fragment: string;
};
};
readonly nth: {
readonly to: "element";
readonly resolve: (context: LocatorContext, index: number) => Element[];
readonly render: (index: unknown) => {
fragment: string;
};
};
};
readonly operations: {
readonly click: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly fill: {
readonly resolve: (context: LocatorContext, value: string, _options?: OperationOptions) => void;
};
readonly hover: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly textContent: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => string;
};
readonly getAttribute: {
readonly resolve: (context: LocatorContext, name: string, _options?: OperationOptions) => string | null;
};
readonly isVisible: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
readonly count: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => number;
};
readonly exists: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
};
};
readonly frame: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly owner: {
readonly to: "frame";
readonly barrier: "up";
readonly resolve ...
Locator
,
options: OperationOptions & {
reason?: string;
}
options
:
type ReadOptions = OperationOptions & {
reason?: string;
}
ReadOptions
= {}):
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<string[]> => {
const
const total: number
total
= await
list: Locator
list
.
count: (_options?: LocatorOptions | undefined) => Promise<number>
count
()
const
const out: string[]
out
: string[] = []
for (let
let i: number
i
= 0;
let i: number
i
<
const total: number
total
;
let i: number
i
++)
const out: string[]
out
.
Array<string>.push(...items: string[]): number

Appends new elements to the end of an array, and returns the new length of the array.

@paramitems New elements to add to the array.

push
(await
list: Locator
list
.
nth: (index: number) => Locator$1<Extended<{
readonly element: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly getByRole: {
readonly to: "element";
readonly resolve: (context: LocatorContext, role: string) => Element[];
readonly render: (role: unknown) => {
fragment: string;
};
};
readonly getByText: {
readonly to: "element";
readonly resolve: (context: LocatorContext, text: string) => Element[];
readonly render: (text: unknown) => {
fragment: string;
};
};
readonly getByTestId: {
readonly to: "element";
readonly resolve: (context: LocatorContext, testId: string) => Element[];
readonly render: (testId: unknown) => {
fragment: string;
};
};
readonly first: {
readonly to: "element";
readonly resolve: (context: LocatorContext) => Element[];
readonly render: () => {
fragment: string;
};
};
readonly nth: {
readonly to: "element";
readonly resolve: (context: LocatorContext, index: number) => Element[];
readonly render: (index: unknown) => {
fragment: string;
};
};
};
readonly operations: {
readonly click: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly fill: {
readonly resolve: (context: LocatorContext, value: string, _options?: OperationOptions) => void;
};
readonly hover: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly textContent: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => string;
};
readonly getAttribute: {
readonly resolve: (context: LocatorContext, name: string, _options?: OperationOptions) => string | null;
};
readonly isVisible: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
readonly count: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => number;
};
readonly exists: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
};
};
readonly frame: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly owner: {
readonly to: "frame";
readonly barrier: "up";
readonly resolve: (_context: LocatorContext) => Element[];
readonly render: () => {
fragment: string;
separator: "up";
};
};
};
readonly operations: {
readonly addStyleTag: {
readonly resolve: (context: LocatorContext, options: AddStyleTagOptions$1) => void;
};
readonly fetch: {
readonly kind: ChainKind;
readonly ...
nth
(
let i: number
i
).
textContent: (_options?: LocatorOptions | undefined) => Promise<string>
textContent
(
options: OperationOptions & {
reason?: string;
}
options
))
return
const out: string[]
out
}
const
const pressRight: (button: Locator, options?: ClickOptions) => Promise<void>
pressRight
= (
button: Locator
button
:
type Locator = {
readonly locator: (selector: string) => Locator$1<Extended<{
readonly element: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly getByRole: {
readonly to: "element";
readonly resolve: (context: LocatorContext, role: string) => Element[];
readonly render: (role: unknown) => {
fragment: string;
};
};
readonly getByText: {
readonly to: "element";
readonly resolve: (context: LocatorContext, text: string) => Element[];
readonly render: (text: unknown) => {
fragment: string;
};
};
readonly getByTestId: {
readonly to: "element";
readonly resolve: (context: LocatorContext, testId: string) => Element[];
readonly render: (testId: unknown) => {
fragment: string;
};
};
readonly first: {
readonly to: "element";
readonly resolve: (context: LocatorContext) => Element[];
readonly render: () => {
fragment: string;
};
};
readonly nth: {
readonly to: "element";
readonly resolve: (context: LocatorContext, index: number) => Element[];
readonly render: (index: unknown) => {
fragment: string;
};
};
};
readonly operations: {
readonly click: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly fill: {
readonly resolve: (context: LocatorContext, value: string, _options?: OperationOptions) => void;
};
readonly hover: {
readonly resolve: (context: LocatorContext, options?: PositionOptions$1) => void;
};
readonly textContent: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => string;
};
readonly getAttribute: {
readonly resolve: (context: LocatorContext, name: string, _options?: OperationOptions) => string | null;
};
readonly isVisible: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
readonly count: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => number;
};
readonly exists: {
readonly resolve: (context: LocatorContext, _options?: OperationOptions) => boolean;
};
};
};
readonly frame: {
readonly selectors: {
readonly locator: {
readonly to: "element";
readonly css: true;
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "descend";
};
};
readonly frameLocator: {
readonly to: "frame";
readonly css: true;
readonly barrier: "down";
readonly resolve: (context: LocatorContext, selector: string) => Element[];
readonly render: (selector: unknown) => {
fragment: string;
separator: "down";
};
};
readonly owner: {
readonly to: "frame";
readonly barrier: "up";
readonly resolve ...
Locator
,
options: OperationOptions & {
reason?: string;
} & {
position?: {
x?: number;
y?: number;
};
}
options
:
type ClickOptions = OperationOptions & {
reason?: string;
} & {
position?: {
x?: number;
y?: number;
};
}
ClickOptions
= {}) =>
button: Locator
button
.
click: (options?: PositionOptions | undefined) => Promise<void>
click
({ ...
options: OperationOptions & {
reason?: string;
} & {
position?: {
x?: number;
y?: number;
};
}
options
,
position?: {
x?: number;
y?: number;
} | undefined
position
: {
x?: number | undefined
x
: 0.9,
y?: number | undefined
y
: 0.5 } }) // fractions of the element's box, so this lands near its right edge
const
const text: (result: FrameFetchResult) => string
text
= (
result: FrameFetchResult
result
:
type FrameFetchResult = {
status: number;
statusText: string;
ok: boolean;
url: string;
redirected: boolean;
type: ResponseType;
headers: [string, string][];
body: ArrayBuffer;
}
FrameFetchResult
): string => new
var TextDecoder: new (label?: string, options?: TextDecoderOptions) => TextDecoder

The TextDecoder interface represents a decoder for a specific text encoding, such as UTF-8, ISO-8859-2, KOI8-R, GBK, etc.

MDN Reference

TextDecoder
().
TextDecoder.decode(input?: AllowSharedBufferSource, options?: TextDecodeOptions): string

The TextDecoder.decode() method returns a string containing text decoded from the buffer passed as a parameter.

MDN Reference

decode
(
result: FrameFetchResult
result
.
body: ArrayBuffer
body
) // the whole body arrived as one ArrayBuffer

Spelling a type is worth it when the type goes back into a signature, and each of the three does here. textContent() is typed Promise<string>, never string | null. An Element’s textContent is never null at runtime either, unlike a Node’s.

The root Frame has no click, getByRole or first, since those are element members and appear only after the first locator(). Those members, and Locator itself, which has no entry of its own in the generated API reference, are on locators and actions. What attachFrame takes is on frames.

PermissionRequest is { key, scope?, reason? } and PermissionGrant is { key, scope, allow }, both from the root and from @fkn/lib/extension. permissions runs on the extension backend: the call is carried out by the FKN browser extension, not by the cloud. Every permissions.request therefore waits for the extension to expose itself.

On a page without the extension, the missing-extension handler runs first (the install card by default), and then the call rejects with The FKN WebExtension is not installed, enabled or not exposed on this page.. setMissingExtensionHandler(null) removes that card. The rejection then lands after the waitForExtensionExposure wait instead of after a dismissal, so the call is bounded.

The union of the twenty keys is declared but not exported, so index it off the request type:

app.ts
import {
const permissions: {
request: (requests: PermissionRequest[]) => Promise<PermissionGrant[]>;
}
permissions
,
const setMissingExtensionHandler: (handler: MissingExtensionHandler | null) => void
setMissingExtensionHandler
} from '@fkn/lib'
import type {
type PermissionRequest = {
key: "read.text" | "read.info" | "read.visible" | "read.check" | "read.count" | "act.click" | "act.type" | "act.hover" | "media.video" | "media.appear" | "media.appearU" | "embed.iframe" | "embed.open" | "frame.fetchRead" | "frame.fetchWrite" | "network.fetch" | "network.fetchCredentialed" | "network.fetchLocal" | "network.readCookie" | "network.modifyRequestHeaders";
scope?: string;
reason?: string;
}
PermissionRequest
,
type PermissionGrant = {
key: "read.text" | "read.info" | "read.visible" | "read.check" | "read.count" | "act.click" | "act.type" | "act.hover" | "media.video" | "media.appear" | "media.appearU" | "embed.iframe" | "embed.open" | "frame.fetchRead" | "frame.fetchWrite" | "network.fetch" | "network.fetchCredentialed" | "network.fetchLocal" | "network.readCookie" | "network.modifyRequestHeaders";
scope: string;
allow: boolean;
}
PermissionGrant
} from '@fkn/lib'
type
type PermissionKey = "read.text" | "read.info" | "read.visible" | "read.check" | "read.count" | "act.click" | "act.type" | "act.hover" | "media.video" | "media.appear" | "media.appearU" | "embed.iframe" | "embed.open" | "frame.fetchRead" | "frame.fetchWrite" | "network.fetch" | "network.fetchCredentialed" | "network.fetchLocal" | "network.readCookie" | "network.modifyRequestHeaders"
PermissionKey
=
type PermissionRequest = {
key: "read.text" | "read.info" | "read.visible" | "read.check" | "read.count" | "act.click" | "act.type" | "act.hover" | "media.video" | "media.appear" | "media.appearU" | "embed.iframe" | "embed.open" | "frame.fetchRead" | "frame.fetchWrite" | "network.fetch" | "network.fetchCredentialed" | "network.fetchLocal" | "network.readCookie" | "network.modifyRequestHeaders";
scope?: string;
reason?: string;
}
PermissionRequest
['key'] // 'read.text' | 'read.info' | 'read.visible' | ... twenty keys
const
const wanted: ("read.text" | "read.info" | "read.visible" | "read.check" | "read.count" | "act.click" | "act.type" | "act.hover" | "media.video" | "media.appear" | "media.appearU" | "embed.iframe" | "embed.open" | "frame.fetchRead" | "frame.fetchWrite" | "network.fetch" | "network.fetchCredentialed" | "network.fetchLocal" | "network.readCookie" | "network.modifyRequestHeaders")[]
wanted
:
type PermissionKey = "read.text" | "read.info" | "read.visible" | "read.check" | "read.count" | "act.click" | "act.type" | "act.hover" | "media.video" | "media.appear" | "media.appearU" | "embed.iframe" | "embed.open" | "frame.fetchRead" | "frame.fetchWrite" | "network.fetch" | "network.fetchCredentialed" | "network.fetchLocal" | "network.readCookie" | "network.modifyRequestHeaders"
PermissionKey
[] = ['read.text', 'act.click']
const
const requests: PermissionRequest[]
requests
:
type PermissionRequest = {
key: "read.text" | "read.info" | "read.visible" | "read.check" | "read.count" | "act.click" | "act.type" | "act.hover" | "media.video" | "media.appear" | "media.appearU" | "embed.iframe" | "embed.open" | "frame.fetchRead" | "frame.fetchWrite" | "network.fetch" | "network.fetchCredentialed" | "network.fetchLocal" | "network.readCookie" | "network.modifyRequestHeaders";
scope?: string;
reason?: string;
}
PermissionRequest
[] =
const wanted: ("read.text" | "read.info" | "read.visible" | "read.check" | "read.count" | "act.click" | "act.type" | "act.hover" | "media.video" | "media.appear" | "media.appearU" | "embed.iframe" | "embed.open" | "frame.fetchRead" | "frame.fetchWrite" | "network.fetch" | "network.fetchCredentialed" | "network.fetchLocal" | "network.readCookie" | "network.modifyRequestHeaders")[]
wanted
.
Array<"read.text" | "read.info" | "read.visible" | "read.check" | "read.count" | "act.click" | "act.type" | "act.hover" | "media.video" | "media.appear" | "media.appearU" | ... 8 more ... | "network.modifyRequestHeaders">.map<{
key: "read.text" | "read.info" | "read.visible" | "read.check" | "read.count" | "act.click" | "act.type" | "act.hover" | "media.video" | "media.appear" | "media.appearU" | "embed.iframe" | "embed.open" | "frame.fetchRead" | "frame.fetchWrite" | "network.fetch" | "network.fetchCredentialed" | "network.fetchLocal" | "network.readCookie" | "network.modifyRequestHeaders";
scope: string;
reason: string;
}>(callbackfn: (value: "read.text" | "read.info" | "read.visible" | "read.check" | "read.count" | "act.click" | "act.type" | "act.hover" | "media.video" | ... 10 more ... | "network.modifyRequestHeaders", index: number, array: ("read.text" | ... 18 more ... | "network.modifyRequestHeaders")[]) => {
key: "read.text" | "read.info" | "read.visible" | "read.check" | "read.count" | "act.click" | "act.type" | "act.hover" | "media.video" | "media.appear" | "media.appearU" | "embed.iframe" | "embed.open" | "frame.fetchRead" | "frame.fetchWrite" | "network.fetch" | "network.fetchCredentialed" | "network.fetchLocal" | "network.readCookie" | "network.modifyRequestHeaders";
scope: string;
reason: string;
}, thisArg?: any): {
key: "read.text" | "read.info" | "read.visible" | "read.check" | "read.count" | "act.click" | "act.type" | "act.hover" | "media.video" | "media.appear" | "media.appearU" | "embed.iframe" | "embed.open" | "frame.fetchRead" | "frame.fetchWrite" | "network.fetch" | "network.fetchCredentialed" | "network.fetchLocal" | "network.readCookie" | "network.modifyRequestHeaders";
scope: string;
reason: string;
}[]

Calls a defined callback function on each element of an array, and returns an array that contains the results.

@paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.

@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.

map
(
key: "read.text" | "read.info" | "read.visible" | "read.check" | "read.count" | "act.click" | "act.type" | "act.hover" | "media.video" | "media.appear" | "media.appearU" | "embed.iframe" | "embed.open" | "frame.fetchRead" | "frame.fetchWrite" | "network.fetch" | "network.fetchCredentialed" | "network.fetchLocal" | "network.readCookie" | "network.modifyRequestHeaders"
key
=> ({
key: "read.text" | "read.info" | "read.visible" | "read.check" | "read.count" | "act.click" | "act.type" | "act.hover" | "media.video" | "media.appear" | "media.appearU" | "embed.iframe" | "embed.open" | "frame.fetchRead" | "frame.fetchWrite" | "network.fetch" | "network.fetchCredentialed" | "network.fetchLocal" | "network.readCookie" | "network.modifyRequestHeaders"
key
,
scope: string
scope
: '*',
reason: string
reason
: 'Read the results and press Search for you' }))
function setMissingExtensionHandler(handler: MissingExtensionHandler | null): void
setMissingExtensionHandler
(null) // a page without the extension rejects after the wait instead of opening the install card
const
const grants: PermissionGrant[]
grants
:
type PermissionGrant = {
key: "read.text" | "read.info" | "read.visible" | "read.check" | "read.count" | "act.click" | "act.type" | "act.hover" | "media.video" | "media.appear" | "media.appearU" | "embed.iframe" | "embed.open" | "frame.fetchRead" | "frame.fetchWrite" | "network.fetch" | "network.fetchCredentialed" | "network.fetchLocal" | "network.readCookie" | "network.modifyRequestHeaders";
scope: string;
allow: boolean;
}
PermissionGrant
[] = await
const permissions: {
request: (requests: PermissionRequest[]) => Promise<PermissionGrant[]>;
}
permissions
.
request: (requests: PermissionRequest[]) => Promise<PermissionGrant[]>
request
(
const requests: PermissionRequest[]
requests
) // [{ key: 'read.text', scope: '*', allow: true }, ...] once granted, in the order asked

The consent sheet is what the extension shows the user before it carries out a gated action. What each key gates, what the scope means and what the sheet shows are on permissions and consent.

An account is the FKN identity a person carries between sites. AccountInfo is what account.info() resolves to, or null when no account is connected. It comes from @fkn/lib/account and holds { name: string, image: string | null, premium: boolean, premiumUntil: number | null } and nothing else: no id and no email, by design.

QuotaStatus is what cloud.quota() resolves to, from @fkn/lib/cloud. It holds the three booleans premium, overQuota and throttled, and the five numbers usedBytes, limitBytes, remainingBytes, bytesPerSecond and bitsPerSecond, each defined on account and quota.

The example reads throttled and bitsPerSecond, the two fields a rate label needs:

app.ts
import {
(alias) namespace account
import account
account
,
(alias) namespace cloud
import cloud
cloud
} from '@fkn/lib'
import type {
type AccountInfo = {
name: string;
image: string | null;
premium: boolean;
premiumUntil: number | null;
}
AccountInfo
} from '@fkn/lib/account'
const
const label: (info: AccountInfo | null) => string
label
= (
info: account.AccountInfo | null
info
:
type AccountInfo = {
name: string;
image: string | null;
premium: boolean;
premiumUntil: number | null;
}
AccountInfo
| null): string =>
info: account.AccountInfo | null
info
? `${
info: account.AccountInfo
info
.
name: string

the account's display name (a username, not necessarily a real name)

name
}${
info: account.AccountInfo
info
.
premium: boolean

the account has an active paid subscription

premium
? ' (premium)' : ''}` : 'Not connected'
const
const who: string
who
=
const label: (info: AccountInfo | null) => string
label
(await
(alias) namespace account
import account
account
.
account_d_exports.info(): Promise<AccountInfo | null>
export account_d_exports.info
info
()) // 'Not connected' until the user connected this site
const {
const throttled: boolean

transfers are actually being rate-limited right now (overQuota and not premium)

throttled
,
const bitsPerSecond: number

current effective egress rate cap, bits per second (for display)

bitsPerSecond
} = await
(alias) namespace cloud
import cloud
cloud
.
cloud_d_exports.quota(): Promise<cloud.QuotaStatus>
export cloud_d_exports.quota
quota
() // false and the free rate while today's free volume is left
const
const rate: string
rate
=
const throttled: boolean

transfers are actually being rate-limited right now (overQuota and not premium)

throttled
? `${
var Math: Math

An intrinsic object that provides basic mathematics functionality and constants.

Math
.
Math.round(x: number): number

Returns a supplied numeric expression rounded to the nearest integer.

@paramx The value to be rounded to the nearest integer.

round
(
const bitsPerSecond: number

current effective egress rate cap, bits per second (for display)

bitsPerSecond
/ 1e6)} Mbit/s` : 'full rate' // 'full rate', since throttled is false

label takes AccountInfo | null, so the disconnected case is typed. The rest of the account surface is on account and quota.

The storage types split by entry: @fkn/lib/fs exports the shapes of the hybrid fs, @fkn/lib/cloud/fs exports the account’s, and Stats comes from all three file systems:

TypeFromWhat it holds
Statsfs, opfs, cloud/fs{ size, mtimeMs, mtime, atimeMs, ctimeMs, mode, isFile(), isDirectory(), isSymbolicLink() }, and isSymbolicLink() is always false
SealedReadfs, cloud/fs{ data: Uint8Array, sealedAt: string | null }
StorageQuotacloud/fs{ usedBytes, limitBytes, remaining, objects, maxObjects }
EncryptionStatuscloud/fs{ unlocked, enrolled, keyEpoch: number | null }
FileEntrycloud/fs{ path, size, contentType: string | null, updatedAt, encryption: string | null }, which no exported function produces
ConflictSidefs{ size, updatedAt: string | null }
FileConflictfs{ path, local, cloud, readLocal(), readCloud() }, what a resolver receives
ConflictResolutionfsBlob | ArrayBuffer | Uint8Array | string | null, what a resolver returns
ConflictResolverfs(conflict: FileConflict) => ConflictResolution | Promise<ConflictResolution>
AdoptSummaryfsuploaded, kept, unresolved and deleted as string[], resolved: { path, choice: 'local' | 'cloud' | 'merged' }[] and failed: { path, error }[], what adopt() resolves to

A read is typed Buffer | string whatever encoding you pass, because the declarations carry no overloads. Coerce the result before JSON.parse. The option types (ReadOptions, WriteOptions, WriteData, MakeOptions, Callback) are not exported by name from any storage entry, and the WriteData on @fkn/lib/contract is a different type (see the broker’s vocabulary). Derive them from the functions instead:

app.ts
import
const fs: {
available: () => Promise<boolean>;
readFileSync: (path: import("node:fs").PathLike, options?: ReadOptions) => Buffer | string;
writeFileSync: (path: import("node:fs").PathLike, data: WriteData, options?: WriteOptions) => void;
appendFileSync: (path: import("node:fs").PathLike, data: WriteData, options?: WriteOptions) => void;
existsSync: (path: import("node:fs").PathLike) => boolean;
statSync: (path: import("node:fs").PathLike) => Stats;
... 28 more ...;
onConflict: (next: ConflictResolver) => (() => void);
}
fs
from '@fkn/lib/fs'
type
type WriteOptions = BufferEncoding | {
encoding?: BufferEncoding | null;
mode?: number;
flag?: string;
contentType?: string;
} | null | undefined
WriteOptions
=
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never

Obtain the parameters of a function type in a tuple

Parameters
<typeof
const fs: {
available: () => Promise<boolean>;
readFileSync: (path: import("node:fs").PathLike, options?: ReadOptions) => Buffer | string;
writeFileSync: (path: import("node:fs").PathLike, data: WriteData, options?: WriteOptions) => void;
appendFileSync: (path: import("node:fs").PathLike, data: WriteData, options?: WriteOptions) => void;
existsSync: (path: import("node:fs").PathLike) => boolean;
statSync: (path: import("node:fs").PathLike) => Stats;
... 28 more ...;
onConflict: (next: ConflictResolver) => (() => void);
}
fs
.
promises: {
readFile: (path: import("node:fs").PathLike, options?: ReadOptions) => Promise<Buffer | string>;
writeFile: (path: import("node:fs").PathLike, data: WriteData, options?: WriteOptions) => Promise<void>;
appendFile: (path: import("node:fs").PathLike, data: WriteData, options?: WriteOptions) => Promise<void>;
stat: (path: import("node:fs").PathLike) => Promise<Stats>;
lstat: (path: import("node:fs").PathLike) => Promise<Stats>;
... 6 more ...;
access: (path: import("node:fs").PathLike) => Promise<void>;
}
promises
.
writeFile: (path: import("node:fs").PathLike, data: WriteData, options?: WriteOptions) => Promise<void>
writeFile
>[2] // BufferEncoding | { encoding?, mode?, flag?, contentType? } | null | undefined
type
type WriteData = string | ArrayBuffer | Uint8Array<ArrayBufferLike> | DataView<ArrayBufferLike>
WriteData
=
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never

Obtain the parameters of a function type in a tuple

Parameters
<typeof
const fs: {
available: () => Promise<boolean>;
readFileSync: (path: import("node:fs").PathLike, options?: ReadOptions) => Buffer | string;
writeFileSync: (path: import("node:fs").PathLike, data: WriteData, options?: WriteOptions) => void;
appendFileSync: (path: import("node:fs").PathLike, data: WriteData, options?: WriteOptions) => void;
existsSync: (path: import("node:fs").PathLike) => boolean;
statSync: (path: import("node:fs").PathLike) => Stats;
... 28 more ...;
onConflict: (next: ConflictResolver) => (() => void);
}
fs
.
promises: {
readFile: (path: import("node:fs").PathLike, options?: ReadOptions) => Promise<Buffer | string>;
writeFile: (path: import("node:fs").PathLike, data: WriteData, options?: WriteOptions) => Promise<void>;
appendFile: (path: import("node:fs").PathLike, data: WriteData, options?: WriteOptions) => Promise<void>;
stat: (path: import("node:fs").PathLike) => Promise<Stats>;
lstat: (path: import("node:fs").PathLike) => Promise<Stats>;
... 6 more ...;
access: (path: import("node:fs").PathLike) => Promise<void>;
}
promises
.
writeFile: (path: import("node:fs").PathLike, data: WriteData, options?: WriteOptions) => Promise<void>
writeFile
>[1] // string | Uint8Array | ArrayBuffer | DataView, and never a Blob
const
const options: WriteOptions
options
:
type WriteOptions = BufferEncoding | {
encoding?: BufferEncoding | null;
mode?: number;
flag?: string;
contentType?: string;
} | null | undefined
WriteOptions
= {
contentType?: string | undefined
contentType
: 'application/json' }
const
const data: WriteData
data
:
type WriteData = string | ArrayBuffer | Uint8Array<ArrayBufferLike> | DataView<ArrayBufferLike>
WriteData
=
var JSON: JSON

An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.

JSON
.
JSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)

Converts a JavaScript value to a JavaScript Object Notation (JSON) string.

@paramvalue A JavaScript value, usually an object or array, to be converted.

@paramreplacer A function that transforms the results.

@paramspace Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.

@throws{TypeError} If a circular reference or a BigInt value is found.

stringify
({
items: never[]
items
: [] })
await
const fs: {
available: () => Promise<boolean>;
readFileSync: (path: import("node:fs").PathLike, options?: ReadOptions) => Buffer | string;
writeFileSync: (path: import("node:fs").PathLike, data: WriteData, options?: WriteOptions) => void;
appendFileSync: (path: import("node:fs").PathLike, data: WriteData, options?: WriteOptions) => void;
existsSync: (path: import("node:fs").PathLike) => boolean;
statSync: (path: import("node:fs").PathLike) => Stats;
... 28 more ...;
onConflict: (next: ConflictResolver) => (() => void);
}
fs
.
promises: {
readFile: (path: import("node:fs").PathLike, options?: ReadOptions) => Promise<Buffer | string>;
writeFile: (path: import("node:fs").PathLike, data: WriteData, options?: WriteOptions) => Promise<void>;
appendFile: (path: import("node:fs").PathLike, data: WriteData, options?: WriteOptions) => Promise<void>;
stat: (path: import("node:fs").PathLike) => Promise<Stats>;
lstat: (path: import("node:fs").PathLike) => Promise<Stats>;
... 6 more ...;
access: (path: import("node:fs").PathLike) => Promise<void>;
}
promises
.
writeFile: (path: import("node:fs").PathLike, data: WriteData, options?: WriteOptions) => Promise<void>
writeFile
('library/catalog.json',
const data: string
data
,
const options: {
encoding?: BufferEncoding | null;
mode?: number;
flag?: string;
contentType?: string;
}
options
)
const
const catalog: any
catalog
=
var JSON: JSON

An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.

JSON
.
JSON.parse(text: string, reviver?: (this: any, key: string, value: any) => any): any

Converts a JavaScript Object Notation (JSON) string into an object.

@paramtext A valid JSON string.

@paramreviver A function that transforms the results. This function is called for each member of the object. If a member contains nested objects, the nested objects are transformed before the parent object is.

@throws{SyntaxError} If text is not valid JSON.

parse
(
var String: StringConstructor
(value?: any) => string

Allows manipulation and formatting of text strings and determination and location of substrings within strings.

String
(
const fs: {
available: () => Promise<boolean>;
readFileSync: (path: import("node:fs").PathLike, options?: ReadOptions) => Buffer | string;
writeFileSync: (path: import("node:fs").PathLike, data: WriteData, options?: WriteOptions) => void;
appendFileSync: (path: import("node:fs").PathLike, data: WriteData, options?: WriteOptions) => void;
existsSync: (path: import("node:fs").PathLike) => boolean;
statSync: (path: import("node:fs").PathLike) => Stats;
... 28 more ...;
onConflict: (next: ConflictResolver) => (() => void);
}
fs
.
readFileSync: (path: import("node:fs").PathLike, options?: ReadOptions) => Buffer | string
readFileSync
('library/catalog.json', 'utf8'))) // Buffer | string in the type, a string at runtime

The read still needs String(). The string is what arrives, and the union is what the declaration says.

@fkn/lib/cloud/fs has no synchronous members at all, so a synchronous call there is a compile error rather than a runtime one:

app.ts
import * as
import cloudFs
cloudFs
from '@fkn/lib/cloud/fs'
type
type CloudMember = "STORAGE_NOT_FOUND" | "StorageLockedError" | "StorageNotFoundError" | "available" | "encryption" | "isNotFound" | "mkdir" | "promises" | "quota" | "readFile" | "readFileSealed" | "readdir" | "rename" | "rm" | "stat" | "unlink" | "unlock" | "writeFile"
CloudMember
= keyof typeof
import cloudFs
cloudFs
// 'available' | 'readFile' | 'writeFile' | 'quota' | 'encryption' | 'unlock' | ... eighteen names, none ending in Sync
import cloudFs
cloudFs
.readFileSync('library/catalog.json')
Error ts(2551) ― Property 'readFileSync' does not exist on type 'typeof import("/opt/buildhome/repo/node_modules/@fkn/lib/cloud/fs")'. Did you mean 'readFile'?

The key union is the whole surface, and mount, flush and appendFile are missing from it as well. The rest is on storage, sync and conflicts and encryption.

Socket and Server from @fkn/lib/net are classes that implement Node’s net.Socket and net.Server. The option and address types around them (SocketConnectOpts, ListenOptions, AddressInfo) are Node’s own, from @types/node. @fkn/lib/dgram and @fkn/lib/http are typed the same way, so code written against Node’s declarations compiles against these, with three exceptions.

net.connect and net.createConnection hand back a Socket before the relay, the server that holds the real socket at the far end, has answered. The connect method on that Socket accepts every Node overload. The two functions accept only the options form:

app.ts
import * as
import net
net
from '@fkn/lib/net'
const
const socket: net.Socket
socket
=
import net
net
.
function connect(options: SocketConnectOpts, connectionListener?: () => void): net.Socket
export connect
connect
({
TcpSocketConnectOpts.host?: string | undefined
host
: 'example.org',
TcpSocketConnectOpts.port: number
port
: 6881 }) // a Socket, handed back before the relay answers
import net
net
.
function connect(options: SocketConnectOpts, connectionListener?: () => void): net.Socket
export connect
connect
(6881, 'example.org')
Error ts(2345) ― Argument of type 'number' is not assignable to parameter of type 'SocketConnectOpts'.

The positional call fails at the call site rather than on the wire.

The dgram.Socket constructor requires its options, where Node’s does not, so build sockets with createSocket:

app.ts
import {
class Socket
Socket
,
const createSocket: (options: SocketType | SocketOptions, callback?: (msg: Buffer, rinfo: RemoteInfo) => void) => Socket
createSocket
} from '@fkn/lib/dgram'
const
const socket: Socket
socket
=
function createSocket(options: SocketType | SocketOptions, callback?: (msg: Buffer, rinfo: RemoteInfo) => void): Socket
createSocket
('udp4') // a Socket built as new Socket({ type: 'udp4' })
new
new Socket(options: SocketOptions & EventEmitterOptions & {
connection?: ReturnType<(options: UdpSocketOptions) => Promise<UdpSocketResult>>;
}): Socket
Socket
()
Error ts(2554) ― Expected 1 arguments, but got 0.

createSocket supplies that object, so code written against Node’s createSocket needs no change.

The third exception is a name rather than a narrowing. OutgoingMessage, the base of ClientRequest and ServerResponse, sits on the default export of @fkn/lib/http only. http.OutgoingMessage resolves, and an import of it by name does not compile:

app.ts
import
const http: {
METHODS: string[];
STATUS_CODES: Record<number, string>;
Agent: typeof Agent;
globalAgent: Agent;
Server: typeof ServerImpl;
ServerResponse: typeof ServerResponse;
IncomingMessage: typeof IncomingMessage;
ClientRequest: typeof ClientRequest;
OutgoingMessage: typeof OutgoingMessage;
createServer: (options?: object | RequestListener, requestListener?: RequestListener) => Server;
request: (...args: [options: string | URL | ClientRequestArgs, callback?: (res: IncomingMessage) => void] | [url: string | URL, options: ClientRequestArgs, callback?: (res: IncomingMessage) => void]) => ClientRequest;
get: (...args: [options: string | URL | ClientRequestArgs, callback?: (res: IncomingMessage) => void] | [url: string | URL, options: ClientRequestArgs, callback?: (res: IncomingMessage) => void]) => ClientRequest;
}
http
from '@fkn/lib/http'
import { OutgoingMessage } from '@fkn/lib/http'
Error ts(2614) ― Module '"@fkn/lib/http"' has no exported member 'OutgoingMessage'. Did you mean to use 'import OutgoingMessage from "@fkn/lib/http"' instead?
type
type Out = OutgoingMessage
Out
=
type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any

Obtain the return type of a constructor function type

InstanceType
<typeof
const http: {
METHODS: string[];
STATUS_CODES: Record<number, string>;
Agent: typeof Agent;
globalAgent: Agent;
Server: typeof ServerImpl;
ServerResponse: typeof ServerResponse;
IncomingMessage: typeof IncomingMessage;
ClientRequest: typeof ClientRequest;
OutgoingMessage: typeof OutgoingMessage;
createServer: (options?: object | RequestListener, requestListener?: RequestListener) => Server;
request: (...args: [options: string | URL | ClientRequestArgs, callback?: (res: IncomingMessage) => void] | [url: string | URL, options: ClientRequestArgs, callback?: (res: IncomingMessage) => void]) => ClientRequest;
get: (...args: [options: string | URL | ClientRequestArgs, callback?: (res: IncomingMessage) => void] | [url: string | URL, options: ClientRequestArgs, callback?: (res: IncomingMessage) => void]) => ClientRequest;
}
http
.
type OutgoingMessage: typeof OutgoingMessage
OutgoingMessage
> // the base both ClientRequest and ServerResponse extend
const
const sent: (message: Out) => boolean
sent
= (
message: OutgoingMessage
message
:
type Out = OutgoingMessage
Out
): boolean =>
message: OutgoingMessage
message
.
OutgoingMessage.headersSent: boolean
headersSent

Out is the base class a signature over both a request and a response needs.

@fkn/lib/dns exports lookup, generic over its all option, and AddressLookupResult = { address: string; family: 0 | 4 | 6 }. @fkn/lib/wire exports the socket option codes as literal types and the two unions TcpSocketOption and UdpSocketOption. The rest is on TCP and UDP sockets and HTTP and DNS.

A package is an npm module FKN loads on a sandbox origin of its own. @fkn/lib/packages exports every shape the packages surface uses, so nothing there has to be derived:

TypeWhat it covers
PackageQuery, PackageResultwhat search and pick take, and one hit they answer
InstalledPackagewhat install resolves to, and one row of list
PickOptions, InstallOptions{ title?, multiple? } and { version?, noConfirm? }
ConnectOptions, AppConnectOptions{ protocol? }, and the app’s payload?, signal? and raw? on top
Connectthe overloaded call type behind packages.connect
PackageConnection, RawPackageConnection{ remote: Remote<T>, closed, port }, and the same without remote
MountOptions, MountedPackageAppConnectOptions plus your iframe, and the connection plus frame and unmount()
ShowOptions, PackageView{ element?, rect? }, and the { hide(), refresh() } that show resolves to
IncomingConnectionInfo, IncomingConnectionwho connected, and that plus the connection
ConnectPayloadthe first argument of onConnect
SurfaceRect, Radii, Placementthe geometry behind show
PackagesError, PackagesErrorCodeError & { code }, the six codes on packages

The type argument on connect, mount and attach is the payload the other side exposed, mapped through osra’s Remote<T>. Every function on it becomes async, and plain data stays itself:

package.ts
import {
const onConnect: <T = unknown>(createPayload: ConnectPayload, handler?: (connection: IncomingConnection<T>) => void) => {
unsubscribe: () => void;
}

Serve connections from apps that installed this package. The first argument is called once per incoming connection with the connection info and returns the payload exposed to that app (its remote). The latest registration receives new connections; existing connections are unaffected.

onConnect
} from '@fkn/lib/packages'
import type {
type IncomingConnection<T = unknown> = IncomingConnectionInfo & PackageConnection<T>
IncomingConnection
} from '@fkn/lib/packages'
type
type HostApi = {
appVersion: string;
}
HostApi
= {
appVersion: string
appVersion
: string }
const
const payload: {
search: (text: string) => Promise<string[]>;
}
payload
= {
search: (text: string) => Promise<string[]>
search
: async (
text: string
text
: string) => [`result for ${
text: string
text
}`] }
export type
type SourceApi = {
search: (text: string) => Promise<string[]>;
}
SourceApi
= typeof
const payload: {
search: (text: string) => Promise<string[]>;
}
payload
onConnect<HostApi>(createPayload: ConnectPayload, handler?: ((connection: IncomingConnection<HostApi>) => void) | undefined): {
unsubscribe: () => void;
}

Serve connections from apps that installed this package. The first argument is called once per incoming connection with the connection info and returns the payload exposed to that app (its remote). The latest registration receives new connections; existing connections are unaffected.

onConnect
<
type HostApi = {
appVersion: string;
}
HostApi
>(
() =>
const payload: {
search: (text: string) => Promise<string[]>;
}
payload
,
(
connection: IncomingConnection<HostApi>
connection
:
type IncomingConnection<T = unknown> = IncomingConnectionInfo & PackageConnection<T>
IncomingConnection
<
type HostApi = {
appVersion: string;
}
HostApi
>) => {
connection: IncomingConnection<HostApi>
connection
.
protocol: string | null

the contract tag the app passed to connect(), e.g. 'stub-source@1'

protocol
// 'example-source@1', the tag the app passed
connection: IncomingConnection<HostApi>
connection
.
remote: {
appVersion: string;
}

the package's exposed payload

remote
.
appVersion: string
appVersion
// '1.2.0', plain data on the app's payload, so no await
},
)
app.ts
import {
(alias) namespace packages
import packages
packages
} from '@fkn/lib'
import type {
type PackageConnection<T = unknown> = {
remote: Remote<T>;
closed: Promise<void>;
port: MessagePort;
}
PackageConnection
,
type PackagesError = Error & {
code: packages.PackagesErrorCode;
}
PackagesError
} from '@fkn/lib/packages'
import type {
type SourceApi = {
search: (text: string) => Promise<string[]>;
}
SourceApi
} from './package'
try {
const
const connection: packages.PackageConnection<{
search: (text: string) => Promise<string[]>;
}>
connection
:
type PackageConnection<T = unknown> = {
remote: Remote<T>;
closed: Promise<void>;
port: MessagePort;
}
PackageConnection
<
type SourceApi = {
search: (text: string) => Promise<string[]>;
}
SourceApi
> = await
(alias) namespace packages
import packages
packages
.
packages_d_exports.connect<{
search: (text: string) => Promise<string[]>;
}>(uri: string, options?: packages.AppConnectOptions & {
raw?: false;
}): Promise<packages.PackageConnection<{
search: (text: string) => Promise<string[]>;
}>> (+1 overload)
export packages_d_exports.connect

Connect to an installed package. Throws a PackagesError with code 'not-installed' when it is not.

connect
<
type SourceApi = {
search: (text: string) => Promise<string[]>;
}
SourceApi
>('npm:@example/subtitles-plugin', {
protocol?: string | undefined

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

protocol
: 'example-source@1',
payload?: unknown

exposed to the package as ITS remote

payload
: {
appVersion: string
appVersion
: '1.2.0' }, // what the package sees as its remote
})
const
const found: string[]
found
= await
const connection: packages.PackageConnection<{
search: (text: string) => Promise<string[]>;
}>
connection
.
remote: {
search: (text: string) => Promise<string[]>;
}

the package's exposed payload

remote
.
search: (text: string) => Promise<string[]>
search
('naruto') // ['result for naruto'], answered by the package
} catch (
var error: unknown
error
) {
const {
const code: packages.PackagesErrorCode
code
} =
var error: unknown
error
as
type PackagesError = Error & {
code: packages.PackagesErrorCode;
}
PackagesError
// 'timeout' when the package never registered onConnect
}

Each side names the other’s payload as its type argument. The app imports what the package exposes, and the package names what the app passed as payload. The rest is on packages.

A room is a realtime channel several browsers join from an invite. @fkn/lib/rooms exports every shape the surface uses:

TypeWhat it covers
Roomwhat create and join resolve to: id, key, invite, self, owner, and every method
RoomMember, RoomPermissions, RoomPermissionone member as this room sees them, the four booleans, and their names
RoomDefaultsthe room-wide send and receive defaults create takes and setDefault changes
RoomMessage, RoomEventone delivered message, and the five events on delivers
RoomEndwhat closed settles with
CreateOptions, JoinOptions{ signal?, members?, defaults? } and { signal? }
RoomsError, RoomsErrorCodethe rejection every member of the entry throws, and its ten codes
app.ts
const
const render: (event: RoomEvent) => string
render
= (
event: rooms.RoomEvent
event
:
type RoomEvent = {
type: "message";
message: rooms.RoomMessage;
} | {
type: "joined";
member: rooms.RoomMember;
} | {
type: "left";
id: string;
reason: "left" | "removed" | "blocked";
} | {
type: "permissions";
id: string;
permissions: rooms.RoomPermissions;
} | {
type: "defaults";
defaults: rooms.RoomDefaults;
}
RoomEvent
) =>
event: rooms.RoomEvent
event
.
type: "message" | "joined" | "left" | "permissions" | "defaults"
type
=== 'message' ?
event: {
type: "message";
message: rooms.RoomMessage;
}
event
.
message: rooms.RoomMessage
message
.
text: string
text
:
event: {
type: "joined";
member: rooms.RoomMember;
} | {
type: "left";
id: string;
reason: "left" | "removed" | "blocked";
} | {
type: "permissions";
id: string;
permissions: rooms.RoomPermissions;
} | {
type: "defaults";
defaults: rooms.RoomDefaults;
}
event
.
type: "joined" | "left" | "permissions" | "defaults"
type
try {
const
const room: rooms.Room
room
:
type Room = {
readonly id: string;
readonly key: string;
readonly invite: string;
readonly self: rooms.RoomMember;
readonly owner: string;
defaults: () => rooms.RoomDefaults;
members: () => Promise<rooms.RoomMember[]>;
send: (text: string) => Promise<void>;
setDefault: (permission: "send" | "receive", value: boolean) => Promise<void>;
grant: (id: string, permission: rooms.RoomPermission) => Promise<void>;
revoke: (id: string, permission: rooms.RoomPermission) => Promise<void>;
... 5 more ...;
readonly closed: Promise<rooms.RoomEnd>;
}

A joined room. The same object survives a broker replacement, so it is safe to hold for as long as the chat lasts.

Room
= await
import rooms
rooms
.
function join(invite: string, options?: rooms.JoinOptions): Promise<Room>
export join

invite is room.invite, or an id and a key joined by a dot.

join
(
var location: Location

The Window.location read-only property returns a Location object with information about the current location of the document.

MDN Reference

location
.
Location.hash: string

The hash property of the Location interface is a string containing a '#' followed by the fragment identifier of the location URL.

MDN Reference

hash
.
String.slice(start?: number, end?: number): string

Returns a section of a string.

@paramstart The index to the beginning of the specified portion of stringObj.

@paramend The index to the end of the specified portion of stringObj. The substring includes the characters up to, but not including, the character indicated by end. If this value is not specified, the substring continues to the end of stringObj.

slice
(1))
await
const room: rooms.Room
room
.
on: (listener: (event: RoomEvent) => void) => Promise<() => void>

Await the returned unsubscribe in cleanup, the account.onChange shape.

on
(
const render: (event: RoomEvent) => string
render
)
} catch (
var error: unknown
error
) {
const {
const code: rooms.RoomsErrorCode
code
} =
var error: unknown
error
as
type RoomsError = Error & {
code: rooms.RoomsErrorCode;
}

Thrown by every member of this namespace. Match on code, never on the message.

RoomsError
// 'not-found' once the room has ended
}

RoomsError is a type, never a class, so you test error.code on a rejection rather than instanceof, exactly like PackagesError. The rest is on rooms.

Resolvers is the whole surface the broker exposes over osra: the namespaces cloud, overlay, installPrompt, frameConsent, connect, relay, packages, rooms, account and shell, plus flat legacy members kept so an older published @fkn/lib keeps working. It lives on @fkn/lib/contract, a type-only entry whose .js is empty, and @fkn/lib/api re-exports it beside apiPromise. The alias Api = Remote<Resolvers> is declared inside @fkn/lib/api and not exported, so spell it through the promise:

app.ts
import {
const apiPromise: Promise<{
cloud: {
fetch: (input: ProxyFetchInput, init: ProxyFetchInit) => Promise<Response>;
quota: () => Promise<{
overQuota: boolean;
remaining: number;
usedBytes: number;
limitBytes: number;
premium: boolean;
bytesPerSecond: number;
}>;
dns: {
lookup: (hostname: string, options?: {
all?: boolean | undefined;
family?: 0 | 4 | 6;
} | undefined) => Promise<{
address: string;
family: 0 | 4 | 6;
} | {
address: string;
family: 0 | 4 | 6;
}[] | undefined>;
};
fs: {
available: () => Promise<boolean>;
... 11 more ...;
setAdoptSource: (next: AdoptState | null, run: (() => Promise<void>) | null) => Promise<...>;
};
webvpn: {
...;
};
};
... 16 more ...;
hideInstallPrompt: () => Promise<...>;
}>

The broker api. Settles when the first connection exists, exactly as before, but resolves with a stable facade that always routes to the NEWEST connection, so holding the resolved value across a broker replacement is safe. A call in flight at the moment of replacement rejects with a named error instead of hanging.

apiPromise
} from '@fkn/lib/api'
type
type Api = {
cloud: {
fetch: (input: ProxyFetchInput, init: ProxyFetchInit) => Promise<Response>;
quota: () => Promise<{
overQuota: boolean;
remaining: number;
usedBytes: number;
limitBytes: number;
premium: boolean;
bytesPerSecond: number;
}>;
dns: {
lookup: (hostname: string, options?: {
all?: boolean | undefined;
family?: 0 | 4 | 6;
} | undefined) => Promise<{
address: string;
family: 0 | 4 | 6;
} | {
address: string;
family: 0 | 4 | 6;
}[] | undefined>;
};
fs: {
available: () => Promise<boolean>;
... 11 more ...;
setAdoptSource: (next: AdoptState | null, run: (() => Promise<void>) | null) => Promise<...>;
};
webvpn: {
...;
};
};
... 16 more ...;
hideInstallPrompt: () => Promise<...>;
}
Api
=
type Awaited<T> = T extends null | undefined ? T : T extends object & {
then(onfulfilled: infer F, ...args: infer _): any;
} ? F extends (value: infer V, ...args: infer _) => any ? Awaited<V> : never : T

Recursively unwraps the "awaited type" of a type. Non-promise "thenables" should resolve to never. This emulates the behavior of await.

Awaited
<typeof
const apiPromise: Promise<{
cloud: {
fetch: (input: ProxyFetchInput, init: ProxyFetchInit) => Promise<Response>;
quota: () => Promise<{
overQuota: boolean;
remaining: number;
usedBytes: number;
limitBytes: number;
premium: boolean;
bytesPerSecond: number;
}>;
dns: {
lookup: (hostname: string, options?: {
all?: boolean | undefined;
family?: 0 | 4 | 6;
} | undefined) => Promise<{
address: string;
family: 0 | 4 | 6;
} | {
address: string;
family: 0 | 4 | 6;
}[] | undefined>;
};
fs: {
available: () => Promise<boolean>;
... 11 more ...;
setAdoptSource: (next: AdoptState | null, run: (() => Promise<void>) | null) => Promise<...>;
};
webvpn: {
...;
};
};
... 16 more ...;
hideInstallPrompt: () => Promise<...>;
}>

The broker api. Settles when the first connection exists, exactly as before, but resolves with a stable facade that always routes to the NEWEST connection, so holding the resolved value across a broker replacement is safe. A call in flight at the moment of replacement rejects with a named error instead of hanging.

apiPromise
> // Remote<Resolvers>, osra's mapping, with every resolver an async function
const
const api: {
cloud: {
fetch: (input: ProxyFetchInput, init: ProxyFetchInit) => Promise<Response>;
quota: () => Promise<{
overQuota: boolean;
remaining: number;
usedBytes: number;
limitBytes: number;
premium: boolean;
bytesPerSecond: number;
}>;
dns: {
lookup: (hostname: string, options?: {
all?: boolean | undefined;
family?: 0 | 4 | 6;
} | undefined) => Promise<{
address: string;
family: 0 | 4 | 6;
} | {
address: string;
family: 0 | 4 | 6;
}[] | undefined>;
};
fs: {
available: () => Promise<boolean>;
... 11 more ...;
setAdoptSource: (next: AdoptState | null, run: (() => Promise<void>) | null) => Promise<...>;
};
webvpn: {
...;
};
};
... 16 more ...;
hideInstallPrompt: () => Promise<...>;
}
api
:
type Api = {
cloud: {
fetch: (input: ProxyFetchInput, init: ProxyFetchInit) => Promise<Response>;
quota: () => Promise<{
overQuota: boolean;
remaining: number;
usedBytes: number;
limitBytes: number;
premium: boolean;
bytesPerSecond: number;
}>;
dns: {
lookup: (hostname: string, options?: {
all?: boolean | undefined;
family?: 0 | 4 | 6;
} | undefined) => Promise<{
address: string;
family: 0 | 4 | 6;
} | {
address: string;
family: 0 | 4 | 6;
}[] | undefined>;
};
fs: {
available: () => Promise<boolean>;
... 11 more ...;
setAdoptSource: (next: AdoptState | null, run: (() => Promise<void>) | null) => Promise<...>;
};
webvpn: {
...;
};
};
... 16 more ...;
hideInstallPrompt: () => Promise<...>;
}
Api
= await
const apiPromise: Promise<{
cloud: {
fetch: (input: ProxyFetchInput, init: ProxyFetchInit) => Promise<Response>;
quota: () => Promise<{
overQuota: boolean;
remaining: number;
usedBytes: number;
limitBytes: number;
premium: boolean;
bytesPerSecond: number;
}>;
dns: {
lookup: (hostname: string, options?: {
all?: boolean | undefined;
family?: 0 | 4 | 6;
} | undefined) => Promise<{
address: string;
family: 0 | 4 | 6;
} | {
address: string;
family: 0 | 4 | 6;
}[] | undefined>;
};
fs: {
available: () => Promise<boolean>;
... 11 more ...;
setAdoptSource: (next: AdoptState | null, run: (() => Promise<void>) | null) => Promise<...>;
};
webvpn: {
...;
};
};
... 16 more ...;
hideInstallPrompt: () => Promise<...>;
}>

The broker api. Settles when the first connection exists, exactly as before, but resolves with a stable facade that always routes to the NEWEST connection, so holding the resolved value across a broker replacement is safe. A call in flight at the moment of replacement rejects with a named error instead of hanging.

apiPromise
// every resolver on the newest broker, from a promise that never rejects
const {
const remaining: number
remaining
} = await
const api: {
cloud: {
fetch: (input: ProxyFetchInput, init: ProxyFetchInit) => Promise<Response>;
quota: () => Promise<{
overQuota: boolean;
remaining: number;
usedBytes: number;
limitBytes: number;
premium: boolean;
bytesPerSecond: number;
}>;
dns: {
lookup: (hostname: string, options?: {
all?: boolean | undefined;
family?: 0 | 4 | 6;
} | undefined) => Promise<{
address: string;
family: 0 | 4 | 6;
} | {
address: string;
family: 0 | 4 | 6;
}[] | undefined>;
};
fs: {
available: () => Promise<boolean>;
... 11 more ...;
setAdoptSource: (next: AdoptState | null, run: (() => Promise<void>) | null) => Promise<...>;
};
webvpn: {
...;
};
};
... 16 more ...;
hideInstallPrompt: () => Promise<...>;
}
api
.
cloud: {
fetch: (input: ProxyFetchInput, init: ProxyFetchInit) => Promise<Response>;
quota: () => Promise<{
overQuota: boolean;
remaining: number;
usedBytes: number;
limitBytes: number;
premium: boolean;
bytesPerSecond: number;
}>;
dns: {
lookup: (hostname: string, options?: {
all?: boolean | undefined;
family?: 0 | 4 | 6;
} | undefined) => Promise<{
address: string;
family: 0 | 4 | 6;
} | {
address: string;
family: 0 | 4 | 6;
}[] | undefined>;
};
fs: {
available: () => Promise<boolean>;
... 11 more ...;
setAdoptSource: (next: AdoptState | null, run: (() => Promise<void>) | null) => Promise<...>;
};
webvpn: {
...;
};
}
cloud
.
quota: () => Promise<{
overQuota: boolean;
remaining: number;
usedBytes: number;
limitBytes: number;
premium: boolean;
bytesPerSecond: number;
}>
quota
() // bytes of free volume left today, on the broker's Quota rather than QuotaStatus

The call comes back as the broker’s own shape: Quota has remaining where QuotaStatus has remainingBytes, and @fkn/lib owns the mapping between the two. The wrappers are what an app calls. @fkn/lib/api is for code that observes the connection itself (see entry points).

The rest of the entry is the vocabulary those resolvers exchange, one line each:

TypeWhat it holds
ConnectAvailability'connected' | 'disconnected' | 'unknown', the storage availability probe’s three answers
Account{ name, image, premium, premiumUntil: string | null }, behind AccountInfo, with the ISO string
Quota{ overQuota, remaining, usedBytes, limitBytes, premium, bytesPerSecond }, behind QuotaStatus
StorageEntrythe same shape @fkn/lib/cloud/fs exports as FileEntry, one listed object
StorageQuotathe same shape @fkn/lib/cloud/fs exports
EncryptionState{ unlocked, enrolled, keyEpoch: number | null }, behind EncryptionStatus
WriteDataArrayBuffer | Uint8Array | string, what a write carries to the broker
AdoptState, AdoptRequest{ files: number, bytes: number }, what the adopt card is told
ConflictSide, ConflictRequest{ size, updatedAt: string | null } and { path, local, cloud }, what the conflict card is told
ConflictChoice'local' | 'cloud' | null, the card’s answer
AddressLookupResult, DnsLookup{ address, family: 0 | 4 | 6 } and the generic lookup signature
FrameFetchScope, FrameConsentRequest'frame.fetchRead' | 'frame.fetchWrite' and { scope, hosts: string[] }, what the cloud frame consent card is told
OverlayState, OverlayRect, HiddenSurface, DisplayCause, DisplayLevel, SetOverlayHostwhat the broker pushes so the overlay frame clips itself to its cards, see how it works
TcpSocketOptions, TcpSocketResult, TcpSocketListenerOptions, TcpSocketListenerResult, UdpSocketOptions, UdpDatagram, UdpSocketResult, IpFamilywhat net and dgram exchange with the broker for one socket
ProxyFetch, ProxyFetchInput, ProxyFetchInitthe signature behind cloud.fetch, taking string | URL | Request and an optional RequestInit widened with render?: boolean
MountDescriptor, PackagesFailthe broker’s answer to packages.frame, which is what packages.mount calls, and the refusal @fkn/lib re-mints as a PackagesError

It also re-exports the package shapes and the two socket option unions from @fkn/lib/wire, which their own sections cover.

Read the @fkn/lib/fs name whenever your code calls fs, and the contract name only when it reads the broker directly.

An error that crosses the broker keeps only its name, message, stack and cause. instanceof therefore works for the classes @fkn/lib constructs in your realm, the JavaScript execution context your code runs in, and for nothing from the other side. See what crosses a realm.

An error is recognised by one of five things: a class, a predicate, a code, a name or a message prefix. Each family answers to just one:

ErrorRecognise it by
BrokerUnreachableError, from @fkn/lib/apiinstanceof, minted in your realm by apiWithin and on the error event of net.connect, Server.listen and dgram.bind
StorageLockedError, StorageNotFoundError, from @fkn/lib/cloud/fsinstanceof or isNotFound(), with code FKN_E2E_LOCKED or FKN_STORAGE_NOT_FOUND, re-minted on reads and writes only, see unlink() skips the re-mint
ExtensionOutdatedError, ExtensionOperationUnsupportedError, from the rootinstanceof, on The FKN WebExtension is installed but too old for this page and The FKN WebExtension does not support "<operation>", the first reachable only if the required ABI ever rises and the second constructed by nothing
ENOENT and its family, and FKN_E2E_LOCKED, on fs and opfscode, on <CODE>: <text>, <syscall> '<path>' and on storage: <path> exists but could not be read, retry once its scope is available, see storage
PackagesErrorcode, one of PackagesErrorCode, on every packages.* call and the local attach
PermissionDeniedErrorerror.name, on every gated extension call, the message starting Permission denied: <key>, matched by neither locator guard, see a refusal neither guard matches
LocatorDeniedError, LocatorUnsupportedError, LocatorInvalidErrorerror.name, or from the root isLocatorDenied, isLocatorUnsupported and isTerminalError, which matches all three, the invalid name having no constant or guard, see the name with no constant
LocatorErrorerror.name or LOCATOR_ERROR from the root, with no guard, the one locator name an action retries rather than stops on
fkn:e2e-locked, fkn:e2e-stale-epoch…, fkn:e2e-integrity…message.startsWith(...) with the three E2E_* constants from @fkn/lib/messages, the first re-minted as StorageLockedError before you see it
storage: api unreachablecode === STORAGE_UNREACHABLE in the realm that set it, else the message prefix
FKN: the broker was replaced while this call was pending; retry itthe exact message, on a call left pending across a replacement, see connection and lifecycle
getaddrinfo ENOTFOUND <hostname>, tcp connect to <address>:<port> timed out after 12000msthe message on a socket’s error event, the relay’s own refusal text included, the Node code not surviving the hop

A class test is true only for an error constructed on your side. A name or prefix test is what remains for one that crossed:

app.ts
import {
const API_DEADLINE_MS: 8000

apiPromise bounded by a deadline, for call sites that must not park forever.

apiPromise NEVER REJECTS: epochs.first settles only when a broker connection exists, so a broker frame that never bridges leaves it pending for the life of the realm. Awaiting it directly is correct wherever hanging is the honest answer, and wrong wherever the caller owns a socket, a timer or a UI that has to say something.

That distinction is not academic. In a WORKER realm the osra transport is {receive: self, emit: self}, which is inert until the page bridges it, so an unbridged worker parks every socket call here with no listening, no error and no rejection. The engine then reports a listener that neither succeeded nor failed, its reopen counters stay at 0 because reopen only runs from an error or close that never arrives, and the relay is never contacted at all. That state cost a long diagnosis: it presents as a transport fault and is invisible from every counter.

The latch mirrors storage.ts: once the broker has missed one deadline, later calls stop paying the full wait. net.ts needs it especially, because its listen path is bind('::').catch(() => bind('0.0.0.0')), so an unbounded-then-rejecting version would charge the deadline twice.

API_DEADLINE_MS
,
class BrokerUnreachableError
BrokerUnreachableError
} from '@fkn/lib/api'
const
const minted: BrokerUnreachableError
minted
= new
new BrokerUnreachableError(what: string, ms: number): BrokerUnreachableError
BrokerUnreachableError
('the quota readout',
const API_DEADLINE_MS: 8000

apiPromise bounded by a deadline, for call sites that must not park forever.

apiPromise NEVER REJECTS: epochs.first settles only when a broker connection exists, so a broker frame that never bridges leaves it pending for the life of the realm. Awaiting it directly is correct wherever hanging is the honest answer, and wrong wherever the caller owns a socket, a timer or a UI that has to say something.

That distinction is not academic. In a WORKER realm the osra transport is {receive: self, emit: self}, which is inert until the page bridges it, so an unbridged worker parks every socket call here with no listening, no error and no rejection. The engine then reports a listener that neither succeeded nor failed, its reopen counters stay at 0 because reopen only runs from an error or close that never arrives, and the relay is never contacted at all. That state cost a long diagnosis: it presents as a transport fault and is invisible from every counter.

The latch mirrors storage.ts: once the broker has missed one deadline, later calls stop paying the full wait. net.ts needs it especially, because its listen path is bind('::').catch(() => bind('0.0.0.0')), so an unbounded-then-rejecting version would charge the deadline twice.

API_DEADLINE_MS
)
const minted: BrokerUnreachableError
minted
instanceof
class BrokerUnreachableError
BrokerUnreachableError
// true, constructed in this realm
const
const crossed: Error & {
name: string;
}
crossed
=
var Object: ObjectConstructor

Provides functionality common to all JavaScript objects.

Object
.
ObjectConstructor.assign<Error, {
name: string;
}>(target: Error, source: {
name: string;
}): Error & {
name: string;
} (+3 overloads)

Copy the values of all of the enumerable own properties from one or more source objects to a target object. Returns the target object.

@paramtarget The target object to copy to.

@paramsource The source object from which to copy properties.

assign
(new
var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error
('Permission denied: act.click (#play)'), {
name: string
name
: 'PermissionDeniedError' }) // the shape a hop hands you
const crossed: Error & {
name: string;
}
crossed
instanceof
var Error: ErrorConstructor
Error
// true, and nothing narrower is left to test
const crossed: Error & {
name: string;
}
crossed
.
name: string
name
=== 'PermissionDeniedError' // true, the name survives
const crossed: Error & {
name: string;
}
crossed
.
Error.message: string
message
.
String.startsWith(searchString: string, position?: number): boolean

Returns true if the sequence of elements of searchString converted to a String is the same as the corresponding elements of this object (converted to a String) starting at position. Otherwise returns false.

startsWith
('Permission denied: ') // true, and so does the prefix

The class test holds only because the error was built on this side. The order to test a mixed handler in is on handling errors, and every message with its cause is on every error.

Every entry point ships one bundled .d.ts, so a type import needs nothing installed beyond @fkn/lib and its own dependencies. @types/node is one of those, a dependency rather than a peer, since the socket and file system surfaces are typed against Node’s own declarations. @fkn/lib/react imports its types from react, an optional peer at >=18, so that one entry needs React’s type declarations installed beside it.

Every twoslash example on this site is checked with TypeScript 5.9 under strict, with an ESNext target and module, bundler module resolution and the DOM library. Top-level await is used freely.

From here, entry points says which import path carries each name. The generated API reference carries the exact declaration of most named exports, Locator and VideoElementState among the ones it misses. The vocabulary above is under @fkn/lib/contract.