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 {
constattachFrame:AttachFrameFunction
Attaches FKN to an iframe the app mounted, or with { window } opens the attachment in a window
of its own. cookies picks the jar and with it the backend (AttachCookies): 'persistent', the
default, and 'ephemeral' run on the cloud render proxy whatever is installed, and never wait for
the extension; 'native' runs on the extension, waiting for it to expose itself while the page
loads (at most 10000 ms) and showing the install prompt when it does not. A blank attach is the
cloud render proxy's alone, so beside 'native' it is a TypeError. frame.backend() says which
backend serves an attachment.
A window opens before the first await, so call this directly in the click or key handler whose
activation opens it. On 'persistent' or 'ephemeral' it opens on the cloud, extension or not. On
'native' it opens as a real browser window on the extension, with no exposure wait; an extension
that does not announce attachWindow, or none at all, is refused at once with
ExtensionOperationUnsupportedError (operation 'attachWindow'), before anything opens or is
waited for, so the same click can still open one on another value.
An attached frame. What it promises across navigations is the same on both backends.
A Frame follows its FRAME, not a document. The page in it may move itself (a link, a form, a
redirect, a script), and every call runs against the document the frame holds when the call
arrives. A call made while the next document commits waits for it, within the call's own timeout.
It reads a document only while that document's host is inside the attachment: the attach url's
host, each goto target's host, and domains, each matched exactly. A document on any other host
is refused with a LocatorDeniedError ("frame: this frame no longer holds the document the app
attached it to"), which names nothing about where the frame went. The Frame stays attached, and
answers again once the frame is back inside, by the page's own move or by a goto.
What belongs to a document ends with it. A style addStyleTag added is gone after a navigation. A
videoElement handle from a document that left rejects every call that answers (play, the
picture-in-picture calls), and its fire-and-forget ones (pause, load, the setters) do nothing.
On the cloud backend an evaluate whose document leaves before it settles rejects, and is not run
again.
Not followed: a navigation the page aims out of its frame (a link or a form targeting the top
window or a new one, a popup). A document with nothing of the page's own to run it in, such as an
error page or a file that is not HTML, cannot be read, and a call on it waits to its deadline.
Elements in an attached frame, found again on every call. Playwright's members where FKN has
Playwright's meaning, plus exists, videoElement and the reason option. last() is the last
match; contentFrame() enters the iframe a trailing locator(selector) matches, as
frameLocator(selector) would, and is a TypeError on a locator that ends in anything else.
An attached frame. What it promises across navigations is the same on both backends.
A Frame follows its FRAME, not a document. The page in it may move itself (a link, a form, a
redirect, a script), and every call runs against the document the frame holds when the call
arrives. A call made while the next document commits waits for it, within the call's own timeout.
It reads a document only while that document's host is inside the attachment: the attach url's
host, each goto target's host, and domains, each matched exactly. A document on any other host
is refused with a LocatorDeniedError ("frame: this frame no longer holds the document the app
attached it to"), which names nothing about where the frame went. The Frame stays attached, and
answers again once the frame is back inside, by the page's own move or by a goto.
What belongs to a document ends with it. A style addStyleTag added is gone after a navigation. A
videoElement handle from a document that left rejects every call that answers (play, the
picture-in-picture calls), and its fire-and-forget ones (pause, load, the setters) do nothing.
On the cloud backend an evaluate whose document leaves before it settles rejects, and is not run
again.
Not followed: a navigation the page aims out of its frame (a link or a form targeting the top
window or a new one, a popup). A document with nothing of the page's own to run it in, such as an
error page or a file that is not HTML, cannot be read, and a call on it waits to its deadline.
Attaches FKN to an iframe the app mounted, or with { window } opens the attachment in a window
of its own. cookies picks the jar and with it the backend (AttachCookies): 'persistent', the
default, and 'ephemeral' run on the cloud render proxy whatever is installed, and never wait for
the extension; 'native' runs on the extension, waiting for it to expose itself while the page
loads (at most 10000 ms) and showing the install prompt when it does not. A blank attach is the
cloud render proxy's alone, so beside 'native' it is a TypeError. frame.backend() says which
backend serves an attachment.
A window opens before the first await, so call this directly in the click or key handler whose
activation opens it. On 'persistent' or 'ephemeral' it opens on the cloud, extension or not. On
'native' it opens as a real browser window on the extension, with no exposure wait; an extension
that does not announce attachWindow, or none at all, is refused at once with
ExtensionOperationUnsupportedError (operation 'attachWindow'), before anything opens or is
waited for, so the same click can still open one on another value.
attachFrame({
iframe: HTMLIFrameElement
iframe:
var document:Document
window.document returns a reference to the document contained in the window.
Elements in an attached frame, found again on every call. Playwright's members where FKN has
Playwright's meaning, plus exists, videoElement and the reason option. last() is the last
match; contentFrame() enters the iframe a trailing locator(selector) matches, as
frameLocator(selector) would, and is a TypeError on a locator that ends in anything else.
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.
An attached frame. What it promises across navigations is the same on both backends.
A Frame follows its FRAME, not a document. The page in it may move itself (a link, a form, a
redirect, a script), and every call runs against the document the frame holds when the call
arrives. A call made while the next document commits waits for it, within the call's own timeout.
It reads a document only while that document's host is inside the attachment: the attach url's
host, each goto target's host, and domains, each matched exactly. A document on any other host
is refused with a LocatorDeniedError ("frame: this frame no longer holds the document the app
attached it to"), which names nothing about where the frame went. The Frame stays attached, and
answers again once the frame is back inside, by the page's own move or by a goto.
What belongs to a document ends with it. A style addStyleTag added is gone after a navigation. A
videoElement handle from a document that left rejects every call that answers (play, the
picture-in-picture calls), and its fire-and-forget ones (pause, load, the setters) do nothing.
On the cloud backend an evaluate whose document leaves before it settles rejects, and is not run
again.
Not followed: a navigation the page aims out of its frame (a link or a form targeting the top
window or a new one, a popup). A document with nothing of the page's own to run it in, such as an
error page or a file that is not HTML, cannot be read, and a call on it waits to its deadline.
Elements in an attached frame, found again on every call. Playwright's members where FKN has
Playwright's meaning, plus exists, videoElement and the reason option. last() is the last
match; contentFrame() enters the iframe a trailing locator(selector) matches, as
frameLocator(selector) would, and is a TypeError on a locator that ends in anything else.
Elements in an attached frame, found again on every call. Playwright's members where FKN has
Playwright's meaning, plus exists, videoElement and the reason option. last() is the last
match; contentFrame() enters the iframe a trailing locator(selector) matches, as
frameLocator(selector) would, and is a TypeError on a locator that ends in anything else.
Elements in an attached frame, found again on every call. Playwright's members where FKN has
Playwright's meaning, plus exists, videoElement and the reason option. last() is the last
match; contentFrame() enters the iframe a trailing locator(selector) matches, as
frameLocator(selector) would, and is a TypeError on a locator that ends in anything else.
Locator['click']>[0]> // the same plus relativePosition?: { x?: number; y?: number }
An attached frame. What it promises across navigations is the same on both backends.
A Frame follows its FRAME, not a document. The page in it may move itself (a link, a form, a
redirect, a script), and every call runs against the document the frame holds when the call
arrives. A call made while the next document commits waits for it, within the call's own timeout.
It reads a document only while that document's host is inside the attachment: the attach url's
host, each goto target's host, and domains, each matched exactly. A document on any other host
is refused with a LocatorDeniedError ("frame: this frame no longer holds the document the app
attached it to"), which names nothing about where the frame went. The Frame stays attached, and
answers again once the frame is back inside, by the page's own move or by a goto.
What belongs to a document ends with it. A style addStyleTag added is gone after a navigation. A
videoElement handle from a document that left rejects every call that answers (play, the
picture-in-picture calls), and its fire-and-forget ones (pause, load, the setters) do nothing.
On the cloud backend an evaluate whose document leaves before it settles rejects, and is not run
again.
Not followed: a navigation the page aims out of its frame (a link or a form targeting the top
window or a new one, a popup). A document with nothing of the page's own to run it in, such as an
error page or a file that is not HTML, cannot be read, and a call on it waits to its deadline.
Elements in an attached frame, found again on every call. Playwright's members where FKN has
Playwright's meaning, plus exists, videoElement and the reason option. last() is the last
match; contentFrame() enters the iframe a trailing locator(selector) matches, as
frameLocator(selector) would, and is a TypeError on a locator that ends in anything else.
Locator,
options: OperationOptions & {
reason?: string;
}
options:
typeReadOptions=OperationOptions& {
reason?:string;
}
ReadOptions= {}):
interfacePromise<T>
Represents the completion of an asynchronous operation
Elements in an attached frame, found again on every call. Playwright's members where FKN has
Playwright's meaning, plus exists, videoElement and the reason option. last() is the last
match; contentFrame() enters the iframe a trailing locator(selector) matches, as
frameLocator(selector) would, and is a TypeError on a locator that ends in anything else.
Where the pointer lands, as a 0..1 fraction of the element's box from its top-left corner: x
across, y down, each 0.5 when left out, so the centre when absent. FKN's name, since
Playwright's position is in pixels, which no backend serves yet: passing position is a
TypeError, before anything is sent.
relativePosition: {
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
consttext: (result:FrameFetchResult) =>string
text= (
result: FrameFetchResult
result:
typeFrameFetchResult= {
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.
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 a union of two shapes, both from the root and from @fkn/lib/extension. A category ask is { category, hosts, reason? }, or { key, hosts, reason? } where the key stands for its own category, and hosts is what makes an item one. The pre-category ask is { key, scope?, reason? }, which carries no hosts and is forwarded to any extension version. PermissionGrant mirrors the split: { category, key?, hosts, allow } for the first, { key, scope, site, allow } for the second. 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.
PermissionCategory is exported. The union of the twenty keys is not, and PermissionRequest['key'] no longer reaches it, since the category member carries no key, so extract the member that does:
The four categories a grant is asked and stored under. Every descriptor names one, so a key
without a category does not compile. Category ids carry no dot and registry keys always do, so the
two can share the grant key column without colliding.
key and scope are set only for a legacy answer, category and hosts only for a category one.
A legacy answer also carries site, the host its scope named, empty where the scope names none
(a locator selector), which is the case that is always refused.
The four categories a grant is asked and stored under. Every descriptor names one, so a key
without a category does not compile. Category ids carry no dot and registry keys always do, so the
two can share the grant key column without colliding.
Calls a defined callback function on each element of an array, and returns an array that contains the results.
@param ― callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.
@param ― thisArg 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(
category: PermissionCategory
category=> ({
category: PermissionCategory
category,
hosts: string[]
hosts: ['example.org'],
reason: string
reason: 'Read the results and press Search for you' }))
Array<"read.text"|"read.info"|"read.visible"|"read.check"|"read.count"|"act.click"|"act.type"|"act.hover"|"media.video"|"media.appear"|"media.appearU"|...10 more ...|"network.modifyRequestHeaders">.map<{
Calls a defined callback function on each element of an array, and returns an array that contains the results.
@param ― callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.
@param ― thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
key and scope are set only for a legacy answer, category and hosts only for a category one.
A legacy answer also carries site, the host its scope named, empty where the scope names none
(a locator selector), which is the case that is always refused.
Asks for permissions on one sheet (extension) or card (cloud), in the order given, and answers
one grant per request.
A category ask needs hosts, the sites the grant applies to, and is answered allow: true only
where every one of them was granted. It needs an extension that serves categories and throws
ExtensionOutdatedError below that; a legacy { key, scope } item is forwarded to any version.
An ask naming a category or key the registry cannot place on a row is a TypeError, thrown
before anything is sent: its answer would have to name a category the caller never asked for.
A category ask naming an FKN platform host is an Error, also before anything is sent; the
extension answers allow: false for any item, legacy ones included, that names one.
requests) // one grant per request, in the order asked
A category ask needs an extension that serves categories and rejects with ExtensionOutdatedError below ABI 2, which is why the branch reads the handshake first. The pre-category form needs no check. 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.
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:
uploaded, 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:
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.
@param ― text A valid JSON string.
@param ― reviver 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.
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:
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:
Error ts(2614) ― Module '"@fkn/lib/http"' has no exported member 'OutgoingMessage'. Did you mean to use 'import OutgoingMessage from "@fkn/lib/http"' instead?
OutgoingMessage> // the base both ClientRequest and ServerResponse extend
const
constsent: (message:Out) =>boolean
sent= (
message: OutgoingMessage
message:
typeOut=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:
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:
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.
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.
Connect to an installed package. Throws a PackagesError with code 'not-installed' when it is not.
connect<
typeSourceApi= {
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
constfound:string[]
found=await
constconnection: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 {
constcode:packages.PackagesErrorCode
code } =
var error:unknown
erroras
typePackagesError=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.
{ signal?, members?, defaults?, key? } for the first two, and { signal? }
ClaimOptions
{ description?, temporary?, mailbox? }: what the account reads beside the room in its fkn.app settings, when the claim ends by itself, and whether it keeps messages
@param ― start The index to the beginning of the specified portion of stringObj.
@param ― end 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
constroom:rooms.Room
room.
on: (listener: (event:RoomEvent) =>void) =>Promise<() =>void>
Await the returned unsubscribe in cleanup, the account.onChange shape.
on(
constrender: (event:RoomEvent) =>string
render)
} catch (
var error:unknown
error) {
const {
constcode:rooms.RoomsErrorCode
code } =
var error:unknown
erroras
typeRoomsError=Error& {
code:rooms.RoomsErrorCode;
}
Thrown by every member of this namespace. Match on code, never on the message.
RoomsError// 'bad-key' when the room exists under another key
}
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.
An object is a file stored under a key your app holds and read by anyone given its url and that key. @fkn/lib/storage exports every shape the surface uses:
the rejection every member of the entry throws, and its eight codes
app.ts
try {
const
constblob:storage.StoredBlob
blob:
typeStoredBlob= {
size:number;
stream: () =>ReadableStream<Uint8Array>;
arrayBuffer: () =>Promise<ArrayBuffer>;
slice: (start?:number, end?:number) =>StoredBlob;
}
The bytes get opened, shaped like a Blob: size, stream, arrayBuffer and slice. Every read
is by range and each 1 MiB record is checked before it is handed over, so a slice near the end of
a large object fetches only that end.
Opens an object any app stored, with no account: the broker reads its size from the first ranged
answer and fetches the sealed bytes by range. Refused invalid for a url that names no stored object
or a key that is not 32 bytes base64url, not-found for an object that was never made, was deleted
or is still uploading (one answer for all of them). A wrong key shows on the first read, as
integrity. A broker replaced while the object is held is asked to open it again on the next read.
get(
consturl:string
url,
constkey:string
key)
await
constblob:storage.StoredBlob
blob.
arrayBuffer: () =>Promise<ArrayBuffer>
arrayBuffer()
} catch (
var error:unknown
error) {
const {
constcode:storage.StorageErrorCode
code } =
var error:unknown
erroras
typeStorageError=Error& {
code:storage.StorageErrorCode;
}
Thrown by every member of this namespace except on an aborted signal. Match on code, never on the message.
StorageError// 'integrity' when the key is not the one this url was stored with
}
StorageError is a type, never a class, so you test error.code on a rejection rather than instanceof, as for RoomsError. The rest is on object storage.
Resolvers is the whole surface the broker exposes over osra: the namespaces cloud, overlay, installPrompt, frameConsent, connect, relay, packages, rooms, storage, account and shell, plus flat legacy members kept so an older published @fkn/lib keeps working. It lives on @fkn/lib/contract, an entry whose .js carries only a few constants, such as ROOM_TEMPORARY_MS and STORAGE_REFUSALS, 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:
The broker api. Settles when the first connection exists and 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.
A window realm that relays its channel to a worker (relayWorker) moves its own calls to a channel
of their own, never back to the relayed one. There it also settles once the realm relays, a call in
flight on the relayed channel then rejects as on a replacement, and a call waits for the realm's own
channel within the deadline below, rejecting with OwnChannelUnavailableError when the broker never
answers there.
The broker api. Settles when the first connection exists and 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.
A window realm that relays its channel to a worker (relayWorker) moves its own calls to a channel
of their own, never back to the relayed one. There it also settles once the realm relays, a call in
flight on the relayed channel then rejects as on a replacement, and a call waits for the realm's own
channel within the deadline below, rejecting with OwnChannelUnavailableError when the broker never
answers there.
apiPromise> // Remote<Resolvers>, osra's mapping, with every resolver an async function
The broker api. Settles when the first connection exists and 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.
A window realm that relays its channel to a worker (relayWorker) moves its own calls to a channel
of their own, never back to the relayed one. There it also settles once the realm relays, a call in
flight on the relayed channel then rejects as on a replacement, and a call waits for the realm's own
channel within the deadline below, rejecting with OwnChannelUnavailableError when the broker never
answers there.
apiPromise// every resolver on the newest broker, from a promise that never rejects
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:
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:
instanceof or error.name === 'TimeoutError', minted in your realm for every call that ran out of time: a locator action, goto, evaluate, addScriptTag and clearCookies, with the last attempt’s error as cause on a locator action
error.name === BACKGROUND_STOPPED or isBackgroundStopped(), both from the root, on a call into the extension that Chrome’s stop of its worker cut off, raised from the next extension store release, the first after 0.1.54, see a call the extension never finished
error.name, or from the root isLocatorDenied, isLocatorUnsupported, isLocatorInvalid and isTerminalError, which matches all three, see the three terminal names
LocatorError
error.name or LOCATOR_ERROR from the root, with no guard, the one locator name an action retries rather than stops on, met as a TimeoutError’s cause
the 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 {
constAPI_DEADLINE_MS:8000
The deadline of apiWithin, for call sites that own a socket, a timer or a UI and must not park.
apiPromise NEVER REJECTS: a broker frame that never bridges leaves it pending for the realm's
life. In a worker the transport {receive: self, emit: self} is inert until the page bridges it,
so an unbridged worker parks every socket call with no listening, error or rejection, which
presents as a transport fault invisible from every counter.
Once one deadline is missed, later calls wait only the retry deadline (as account-storage.ts does):
net.ts listens with bind('::').catch(() => bind('0.0.0.0')) and would otherwise pay it twice.
The deadline of apiWithin, for call sites that own a socket, a timer or a UI and must not park.
apiPromise NEVER REJECTS: a broker frame that never bridges leaves it pending for the realm's
life. In a worker the transport {receive: self, emit: self} is inert until the page bridges it,
so an unbridged worker parks every socket call with no listening, error or rejection, which
presents as a transport fault invisible from every counter.
Once one deadline is missed, later calls wait only the retry deadline (as account-storage.ts does):
net.ts listens with bind('::').catch(() => bind('0.0.0.0')) and would otherwise pay it twice.
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.