Zum Inhalt springen

SDK: App und Verbindung

Als Markdown ansehen

Eine App ruft einmal connect() auf und arbeitet danach mit dem zurückgegebenen PithyxApp. Diese Seite listet dieses Objekt mit jeder Methode, der Berechtigung, die sie braucht, und den Fehlercodes, mit denen sie ablehnt, dazu die Protokollnachrichten, die Formatierer und die Typen des Manifests.

Beim Build aus den TSDoc-Kommentaren von @pithyx/sdk 1.0.0-rc.9 erzeugt; der Text unter den Überschriften ist der Kommentar im Quelltext und bleibt Englisch.

The API an app works with. The same object for core apps and sandboxed apps; only the transport differs. Every method that talks to the desktop returns a promise and rejects with a PithyxError.

Every such call can also reject with sdk.request.timeout (no answer within 15 seconds, see REQUEST_TIMEOUT), sdk.transport.closed (the window closed or the desktop signed out), sdk.request.invalid (parameters outside the limits the method states) and sdk.method.unknown (the desktop does not serve the method to this app; check PithyxApp.supports first). The permissions are the manifest’s permissions that an administrator granted; context.permissions lists the ones the app holds now.

readonly context: AppContext;

What the desktop told the app at start. The SDK keeps it current with the events: theme, language, locale, timeZone, sizeClass, permissions, visible, online, logLevel, backend.status and storage. Needs no permission. Reading it after the connection closed throws sdk.transport.closed.

readonly format: Formatters;

Dates, numbers, sizes and lists in the user’s language and time zone. Needs no permission.

readonly window: object;

The app’s own window; every call acts on the window that makes it. Needs no permission.

setTitle(title): Promise<void>;

Sets the title in the title bar and the taskbar, at most 200 characters.

Parameter Typ Beschreibung
title string The new title, plain text.

Promise<void>

sdk.request.invalid when the title is longer than 200 characters.

close(): Promise<void>;

Closes the window without asking beforeClose.

Promise<void>

setSize(size): Promise<{
width: number;
height: number;
}>;

Asks for a new free size; resolves with the size the desktop gave the window.

Parameter Typ Beschreibung
size { width: number; height: number; } The wished size in CSS pixels; the desktop may clamp it.
size.width number Width in CSS pixels.
size.height number Height in CSS pixels.

Promise<{ width: number; height: number; }>

The size the window got.

sdk.request.invalid when a value is not a finite number.

setDirty(dirty): Promise<void>;

Marks the window as having unsaved changes or not. Once called, beforeClose is asked only while the window is dirty.

Parameter Typ Beschreibung
dirty boolean Whether the window has unsaved changes.

Promise<void>

requestAttention(): Promise<void>;

Makes the taskbar entry pulse until the user looks; never takes the focus.

Promise<void>

ready(): Promise<void>;

Ends the desktop’s loading placeholder, for an app whose manifest sets window.loading.untilReady because it first loads its data. Without that setting the placeholder goes when the app connects, and this does nothing.

Promise<void>

readonly log: object;

The app’s log in the Log Center, where administrators see it next to the system’s logs (source app/<id>, with the signed-in user). Every entry also appears in the browser console. info, warn and error always go to the daemon, debug only while an administrator switched debug logging on for the app (context.logLevel). Calls return at once and never throw: the SDK collects entries for a moment and sends them together, and drops entries when the app logs faster than its limit (bursts of 200, then 20 a second). Never log secrets such as passwords or tokens. Messages are cut at 4000 characters, data at 20 fields of 1000 characters. Needs no permission.

debug(message, data?): void;

Details only needed while looking for a fault; reaches the daemon only while debug logging is on.

Parameter Typ Beschreibung
message string What happened, in English.
data? LogData Fields that describe it, for example { file: 'a.jpg' }.

void

info(message, data?): void;

Something worth knowing happened.

Parameter Typ Beschreibung
message string What happened, in English.
data? LogData Fields that describe it.

void

warn(message, data?): void;

Something went wrong but the app carries on.

Parameter Typ Beschreibung
message string What happened, in English.
data? LogData Fields that describe it.

void

error(message, data?): void;

Something failed.

Parameter Typ Beschreibung
message string What happened, in English.
data? LogData Fields that describe it.

void

log(message, data?): void;

The same as info, for code written against console.log.

Parameter Typ Beschreibung
message string What happened, in English.
data? LogData Fields that describe it.

void

app.log.warn('Upload retried', { file: name, attempt: 2 });
readonly session: object;

Small state of this window that survives a reload and moves with “Get desktop from …”. Needs no permission.

save(state): Promise<void>;

Saves the window’s state with the desktop layout, at most 16 KB as JSON.

Parameter Typ Beschreibung
state unknown Any JSON value, for example the open folder and the scroll position.

Promise<void>

app.storage.too_large when the state is larger than 16 KB as JSON.

restore(): Promise<unknown>;

The state saved before the last reload or on another device, or null. Never rejects.

Promise<unknown>

The saved state, or null.

readonly storage: object;

Values the app keeps per user on the box; sandboxed apps have no localStorage. Keys have at most 128 characters, values at most 64 KB as JSON, and an app keeps at most 1 MB per user. Needs no permission.

get<T>(key): Promise<T | null>;

The value stored under key, or null.

Typenparameter Standardtyp
T unknown
Parameter Typ Beschreibung
key string 1 to 128 characters.

Promise<T | null>

The stored value, or null when there is none.

app.storage.key_invalid when the key is empty or longer than 128 characters.

set(key, value): Promise<void>;

Stores a value under key, replacing an earlier one.

Parameter Typ Beschreibung
key string 1 to 128 characters.
value unknown Any JSON value, at most 64 KB as JSON.

Promise<void>

app.storage.key_invalid when the key is empty or longer than 128 characters.

app.storage.too_large when the value is larger than 64 KB or the app would keep more than 1 MB for the user.

remove(key): Promise<void>;

Removes the value under key, if there is one; a missing key is no error.

Parameter Typ Beschreibung
key string 1 to 128 characters.

Promise<void>

app.storage.key_invalid when the key is empty or longer than 128 characters.

keys(): Promise<string[]>;

Every key the app stored for this user.

Promise<string[]>

The keys, in no particular order.

readonly backend: object;

The app’s own backend, through the daemon’s proxy. Paths are relative to an endpoint: the default api at /apps/<id>/api/, any other endpoint <name> at /apps/<id>/api-<name>/. The SDK adds a token for the signed-in user; the backend learns the user from the proxy’s X-Pithyx-* headers. A name the manifest does not declare fails with sdk.backend.endpoint_unknown. Needs no permission; an app without a backend has no endpoint.

url(path, options?): Promise<string>;

A full URL with the token as ?pithyx-token=, for <img>, <video> or a download link. It is valid as long as its token, at least 30 seconds and at most 5 minutes; ask again for a new one.

Parameter Typ Beschreibung
path string The path below the endpoint, with an optional query; it may not leave the endpoint with ...
options? { endpoint?: string; } The endpoint; api when left out.
options.endpoint? string The name of an endpoint the manifest declares; api when left out.

Promise<string>

The absolute URL with the token.

sdk.backend.endpoint_unknown when the app has no backend or the manifest declares no such endpoint.

sdk.request.invalid when the path leaves the endpoint.

app.backend.missing when the app has no backend.

app.app.not_found when the user may no longer open the app.

fetch(path, init?): Promise<Response>;

fetch against the backend with Authorization: Bearer <token> and without cookies. Resolves with the response whatever its status; a request that does not reach the box fails with sdk.backend.unreachable.

Parameter Typ Beschreibung
path string The path below the endpoint, with an optional query; it may not leave the endpoint with ...
init? RequestInit & object What fetch takes, plus the endpoint.

Promise<Response>

The backend’s response, also for 4xx and 5xx; the proxy answers 503 app.backend.unavailable while the backend is updating or unavailable.

sdk.backend.endpoint_unknown when the app has no backend or the manifest declares no such endpoint.

sdk.backend.unreachable when the request gets no answer.

sdk.request.invalid when the path leaves the endpoint.

socket(path, options?): Promise<WebSocket>;

Opens a WebSocket to the backend with a one-time ticket (?pithyx-ticket=, valid 30 seconds).

Parameter Typ Beschreibung
path string The path below the endpoint; it may not leave the endpoint with ...
options? { endpoint?: string; protocols?: string | string[]; } The endpoint and the subprotocols.
options.endpoint? string The name of an endpoint the manifest declares; api when left out.
options.protocols? string | string[] WebSocket subprotocols, as new WebSocket takes them.

Promise<WebSocket>

The WebSocket, still connecting; wss: on HTTPS and ws: on plain HTTP.

sdk.backend.endpoint_unknown when the app has no backend or the manifest declares no such endpoint.

sdk.request.invalid when the path leaves the endpoint.

token(): Promise<BackendToken>;

The current token, for clients that build their own requests. The SDK keeps it until 30 seconds before it expires and fetches a new one after that or after permissionsChange.

Promise<BackendToken>

The token and when it expires.

app.backend.missing when the app has no backend.

app.app.not_found when the user may no longer open the app.

const messages = await (await app.backend.fetch('/messages')).json();
const stats = await app.backend.fetch('/stats', { endpoint: 'metrics' });
avatar.src = await app.backend.url('/avatar/1');
const live = await app.backend.socket('/live');
readonly directory: object;

The users and groups of the box. Needs directory.read; the app’s own user is in context.user without it.

users(): Promise<DirectoryUser[]>;

Every active user of the box.

Promise<DirectoryUser[]>

The users, without deactivated ones.

app.permission.denied without the directory.read permission.

groups(): Promise<DirectoryGroup[]>;

Every group of the box, without the built-in group of all users.

Promise<DirectoryGroup[]>

The groups.

app.permission.denied without the directory.read permission.

readonly contextMenu: object;

A context menu drawn by the desktop, also over the edge of the app’s window. Needs no permission.

show(at, items): Promise<string | null>;

Shows a menu drawn by the desktop at at (CSS pixels in the app’s window) and resolves with the id of the picked entry, or null when the user dismissed it. Call it from a contextmenu or click handler.

Parameter Typ Beschreibung
at Point Where the menu opens, relative to the window’s content.
items ContextMenuItem[] 1 to 50 entries and separators.

Promise<string | null>

The id of the picked entry, or null.

sdk.request.invalid when there are no entries, more than 50, or an entry exceeds its limits.

readonly clipboard: object;

The clipboard, through the desktop. Needs no permission.

writeText(text): Promise<void>;

Copies text, also over plain HTTP where the frame has no Clipboard API. Only for a user action.

Parameter Typ Beschreibung
text string At most 1 MB of text.

Promise<void>

sdk.request.invalid when the text is longer than 1 MB.

readonly files: object;

Files. The first group works on files the user handed to the app (files.read, files.write); the second is the file manager API over shares and named storage (files.shares for shares). See Methods for every call.

A FileLocation is { storage: id } for a storage request of the manifest or { share: id } for a share; versions, restoreVersion and trash take only a share (ShareLocation). The daemon checks the app’s permissions and the user’s own rights on every call: a share needs files.shares, its content also files.read or files.write, and changes need write access to the share (fs.share.read_only otherwise). Common failures: app.permission.denied, fs.share.not_found, fs.share.unavailable (its pool is missing or locked), app.storage.not_found (no such storage request or not mapped), fs.path.invalid, fs.entry.not_found and fs.entry.access_denied. A desktop may lack a method; check PithyxApp.supports.

pick(options?): Promise<FileRef[]>;

Lets the user pick files in the desktop’s file dialog. Resolves with an empty list when the user cancels. Needs files.read; must follow a user action in the window (otherwise it resolves with an empty list). The app can read the chosen files, and write them with files.write, without files.shares.

Parameter Typ Beschreibung
options? { multiple?: boolean; extensions?: string[]; } Whether several files may be picked and which extensions are offered.
options.multiple? boolean Whether the user may pick more than one file.
options.extensions? string[] Extensions without dot, for example ['jpg', 'png']; every file when left out.

Promise<FileRef[]>

The picked files, empty when the user cancelled.

app.permission.denied without the files.read permission.

saveAs(suggestedName, data): Promise<FileRef | null>;

Lets the user choose where to save data in the desktop’s save dialog. Resolves with null when the user cancels. Needs files.write; must follow a user action in the window (otherwise it resolves with null).

Parameter Typ Beschreibung
suggestedName string The name the dialog proposes.
data Blob The content to save.

Promise<FileRef | null>

The saved file, or null when the user cancelled.

app.permission.denied without the files.write permission.

read(file): Promise<Blob>;

The content of a file the app was handed. Needs files.read.

Parameter Typ Beschreibung
file FileRef A handle from pick, open, launch.file or openFile.

Promise<Blob>

The content.

app.permission.denied without the files.read permission.

fs.entry.not_found when the file is gone.

write(file, data): Promise<FileRef>;

Replaces the content of a writable file the app was handed. Needs files.write.

Parameter Typ Beschreibung
file FileRef A handle whose writable is true.
data Blob The new content.

Promise<FileRef>

The handle with the new size and change time.

app.permission.denied without the files.write permission.

fs.share.read_only when the user may only read the share.

fs.volume.full when the pool has no space left.

url(file): Promise<{
url: string;
expiresAt: string;
}>;

A short-lived URL for <img>, <video> or fetch with range requests, valid 5 minutes and only for this file. Needs files.read.

Parameter Typ Beschreibung
file FileRef A handle from pick, open, launch.file or openFile.

Promise<{ url: string; expiresAt: string; }>

The URL and when it expires.

app.permission.denied without the files.read permission.

open(location, path): Promise<FileRef>;

A handle for a file in a named storage or a share, for read, write and url. A share needs files.shares.

Parameter Typ Beschreibung
location FileLocation A storage request or a share.
path string The file, relative to the location.

Promise<FileRef>

The handle; writable says whether the app may write it.

app.permission.denied without files.shares for a share.

fs.entry.not_found when there is no such file.

shares(): Promise<ShareInfo[]>;

The shares the user can use, with the user’s access to each. Needs files.shares. The sharesChanged event says when the list may have changed.

Promise<ShareInfo[]>

The shares, without their place on the host.

app.permission.denied without the files.shares permission.

list(
location,
path,
page?
): Promise<FolderPage>;

One page of a folder (default 100 entries, at most 1000), in natural name order unless sort says otherwise. Pass the page’s nextCursor as cursor for the next one; cursors stay valid while the folder changes. A share needs files.shares.

Parameter Typ Beschreibung
location FileLocation A storage request or a share.
path string The folder, relative to the location; "" for its root.
page? { cursor?: string; limit?: number; sort?: SortKey; order?: SortOrder; } Cursor, size and order of the page.
page.cursor? string The nextCursor of the previous page.
page.limit? number Entries per page, 100 when left out, at most 1000.
page.sort? SortKey What to sort by; name when left out.
page.order? SortOrder Ascending or descending; asc when left out.

Promise<FolderPage>

One page of entries.

app.permission.denied without files.shares for a share.

fs.entry.not_found or fs.entry.not_folder when the path is no folder.

fs.cursor.invalid when the cursor belongs to another listing.

watch(location, path): Promise<string>;

Starts live changes of a folder and resolves with the id that its folderChanged events carry. Watch only folders on screen and call unwatch when leaving them; a watch never wakes a sleeping disk. Needs files.shares.

Parameter Typ Beschreibung
location FileLocation A share.
path string The folder, relative to the share.

Promise<string>

The watch id.

app.permission.denied without the files.shares permission.

unwatch(watch): Promise<void>;

Ends a watch; no more folderChanged events come for it.

Parameter Typ Beschreibung
watch string The id watch resolved with.

Promise<void>

upload(
location,
folder,
name,
data,
options?
): Promise<FileEntry>;

Uploads data as name into folder. The file appears only when it is complete. An existing name fails with fs.entry.exists unless overwrite; with an upload id, uploadProgress events report the bytes sent. The call has no time limit, since it lasts as long as the transfer. A share needs files.shares and write access.

Parameter Typ Beschreibung
location FileLocation A storage request or a share.
folder string The folder, relative to the location.
name string The file name, 1 to 255 characters without /.
data Blob The content.
options? { overwrite?: boolean; upload?: string; } Whether to replace an existing file and the id for progress events.
options.overwrite? boolean Replace a file of the same name. Set it only when the user asked for it.
options.upload? string An id of the app’s choice; uploadProgress events carry it.

Promise<FileEntry>

The new entry.

fs.entry.exists when the name is taken and overwrite is not set.

fs.share.read_only when the user may only read the share.

fs.volume.full when the pool has no space left.

fs.upload.incomplete when the transfer broke off.

sdk.transfer.cancelled when the user cancelled the upload in the taskbar.

download(location, path): Promise<void>;

Downloads a file to the user’s device through the browser. Needs files.shares.

Parameter Typ Beschreibung
location FileLocation A share.
path string The file, relative to the share.

Promise<void>

app.permission.denied without the files.shares permission.

createFolder(location, path): Promise<FileEntry>;

Creates a folder at path; its parent must exist and the name must be free (fs.entry.exists). A share needs files.shares and write access.

Parameter Typ Beschreibung
location FileLocation A storage request or a share.
path string The new folder, relative to the location.

Promise<FileEntry>

The new entry.

fs.entry.exists when the name is taken.

fs.entry.not_found when the parent folder does not exist.

fs.share.read_only when the user may only read the share.

rename(
location,
path,
name
): Promise<FileEntry>;

Renames the entry at path to name within its folder; an existing name is never replaced. Needs files.shares and write access.

Parameter Typ Beschreibung
location FileLocation A share.
path string The entry, relative to the share.
name string The new name, 1 to 255 characters without /.

Promise<FileEntry>

The renamed entry.

fs.entry.exists when the name is taken.

fs.name.invalid when the name is not allowed.

run(operation): Promise<JobInfo>;

Starts a copy, move or delete as a job and resolves with it at once; jobChanged events follow its progress until it finishes. A whole folder is named with { all: true, except }, never with every name. Deleting moves to the share’s recycle bin unless permanent, which only an explicit request of the user may set. Source and target may each be a share or a storage request: a share needs files.shares, files.read where entries are copied or moved from and files.write where anything changes; a storage request needs its mapping, for changes one for reading and writing, and never reaches beyond the mapped folder. The user needs the access too.

Parameter Typ Beschreibung
operation FileOperation What to do with which entries, and where to.

Promise<JobInfo>

The job as it started; failures of single entries end up in its problem.

fs.operation.invalid when the operation does not fit (a target for delete, none for copy).

fs.operation.into_itself when a folder would be copied or moved into itself.

versions(location, path): Promise<FileVersion[]>;

Earlier versions of the entry at path from the snapshots of the share’s dataset, newest first; also of an entry deleted since. Empty when the share has no snapshots. Needs files.shares and read access to the share.

Parameter Typ Beschreibung
location ShareLocation A share.
path string The entry, relative to the share; not its root.

Promise<FileVersion[]>

The versions, newest first.

restoreVersion(
location,
path,
snapshot,
options?
): Promise<JobInfo>;

Copies a version back as a job: next to the current entry as “name (version date)”, or over it with overwrite (files are replaced, folders merged), which only an explicit request of the user may set. Needs files.shares and write access.

Parameter Typ Beschreibung
location ShareLocation A share.
path string The entry, relative to the share; not its root.
snapshot string The snapshot of a version from versions.
options? { overwrite?: boolean; } Whether to replace the current entry.
options.overwrite? boolean Replace the current entry instead of restoring next to it.

Promise<JobInfo>

The job as it started.

fs.version.not_found when the snapshot or the entry in it is gone.

readonly jobs: object;

The jobs the app started with files.run, files.restoreVersion and trash.*.

cancel(job): Promise<void>;

Cancels a job the app started; a job that already finished stays as it is.

Parameter Typ Beschreibung
job number The id of the job.

Promise<void>

jobs.job.not_found when there is no such job of the user.

readonly trash: object;

The recycle bin of a share. Needs files.shares; reading it needs read access to the share, changing it write access.

list(location, page?): Promise<TrashPage>;

One page of the bin, newest first, with the share’s retention.

Parameter Typ Beschreibung
location ShareLocation A share.
page? { cursor?: string; limit?: number; } Cursor and size of the page.
page.cursor? string The nextCursor of the previous page.
page.limit? number Items per page.

Promise<TrashPage>

One page of items.

app.permission.denied without the files.shares permission.

restore(
location,
ids,
onConflict?
): Promise<JobInfo>;

Puts items back where they were, recreating missing folders, as a job. A taken name gets a free one (“name (2)”) unless onConflict says otherwise.

Parameter Typ Beschreibung
location ShareLocation A share.
ids number[] 1 to 1000 item ids from list.
onConflict? ConflictPolicy What to do with a taken name; rename when left out.

Promise<JobInfo>

The job as it started.

delete(location, ids): Promise<JobInfo>;

Deletes items of the bin for good, as a job. Only after the user confirmed it.

Parameter Typ Beschreibung
location ShareLocation A share.
ids number[] 1 to 1000 item ids from list.

Promise<JobInfo>

The job as it started.

empty(location): Promise<JobInfo>;

Deletes everything in the bin for good, as a job. Only after the user confirmed it.

Parameter Typ Beschreibung
location ShareLocation A share.

Promise<JobInfo>

The job as it started.

readonly ui: object;

The desktop’s own dialogs. Needs no permission.

showError(error): Promise<void>;

Shows the desktop’s error dialog for a failure, with the translated text of its code, so the app needs no texts of its own for daemon and SDK errors. A code the desktop does not know (the app’s own) shows the error’s detail instead, so give such an error a detail in the user’s language. The request id goes to the browser console, never into the dialog.

Parameter Typ Beschreibung
error unknown A PithyxError, a problem, or anything else, which shows as an unexpected error.

Promise<void>

notify(notification): Promise<{
id: string;
}>;

Shows a notification and resolves with its id. Needs notifications. A click on one of its actions (or on the notification) comes back as notificationAction, or as launch.notification when no window was open.

Parameter Typ Beschreibung
notification NotificationRequest Title, body, level, buttons, data and tag; the limits are on NotificationRequest.

Promise<{ id: string; }>

The id of the notification, which notificationAction carries.

app.permission.denied without the notifications permission.

sdk.request.invalid when a text, the buttons or the data exceed their limits.

setBadge(count): Promise<void>;

A counter on the app’s taskbar entry; 0 or null removes it. Needs no permission.

Parameter Typ Beschreibung
count number | null A whole number of at least 0, or null.

Promise<void>

sdk.request.invalid when the count is negative or not a whole number.

openUrl(url): Promise<void>;

Opens an https:, http: or mailto: URL in a new browser tab, without a reference to the desktop. Needs no permission.

Parameter Typ Beschreibung
url string An absolute URL of at most 2048 characters.

Promise<void>

sdk.request.invalid for any other scheme (javascript:, data:) or a longer URL.

supports(method): boolean;

Whether the desktop supports a method. Methods added after 1.0 may be missing on an older desktop; a call to a method it does not support fails with sdk.method.unknown.

Parameter Typ Beschreibung
method keyof Methods A method name of Methods, such as files.watch.

boolean

Whether context.capabilities lists it.

on<E>(event, handler): () => void;

Listens to an event of the desktop (see Events). Returns a function that stops listening.

Typenparameter
E extends keyof Events
Parameter Typ Beschreibung
event E The event name.
handler EventHandler<E> Called with the event’s data each time it arrives.

A function that removes the handler.

() => void

answer<Q>(query, handler): void;

Answers a question of the desktop (beforeClose, beforeUnload). One handler per question; the last one wins. The answer to beforeUnload is ignored: it cannot be refused, only used to save.

Typenparameter
Q extends keyof Queries
Parameter Typ Beschreibung
query Q The question.
handler QueryHandler<Q> Returns the answer, also asynchronously within the question’s time limit.

void

call<M>(method, params): Promise<Methods[M]["result"]>;

Low-level call of any method, for methods newer than this SDK’s typed helpers. It needs what the method needs and fails like it.

Typenparameter
M extends keyof Methods
Parameter Typ Beschreibung
method M A method name of Methods.
params Methods[M]["params"] The method’s parameters.

Promise<Methods[M]["result"]>

The method’s result.

sdk.method.unknown when the desktop does not serve the method.


How createApp and connect set up the app.

optional themeRoot?: HTMLElement | null;

Where the theme tokens go; the SDK sets them on document.documentElement by default. Null to skip.

optional timeout?: number;

Time limit of a call in milliseconds; REQUEST_TIMEOUT when left out.

optional origin?: string;

The box’s address for backend URLs; by default taken from the window’s own URL.


Formatters in the user’s language and time zone, as app.format has them. None of them throws or needs a permission.

date(value, options?): string;

A date, for example “Sep 27, 2026”.

Parameter Typ Beschreibung
value string | Date An ISO 8601 string or a Date.
options? DateTimeFormatOptions Intl.DateTimeFormat options; { dateStyle: 'medium' } when left out.

string

The date in the user’s time zone.

time(value, options?): string;

A time of day, for example “14:05”.

Parameter Typ Beschreibung
value string | Date An ISO 8601 string or a Date.
options? DateTimeFormatOptions Intl.DateTimeFormat options; { timeStyle: 'short' } when left out.

string

The time in the user’s time zone.

dateTime(value, options?): string;

Date and time.

Parameter Typ Beschreibung
value string | Date An ISO 8601 string or a Date.
options? DateTimeFormatOptions Intl.DateTimeFormat options; medium date and short time when left out.

string

Date and time in the user’s time zone.

relative(value, now?): string;

How long ago or how far ahead, for example “5 minutes ago”, in the largest unit that fits.

Parameter Typ Beschreibung
value string | Date An ISO 8601 string or a Date.
now? Date The moment to compare with; the current time when left out.

string

The distance in words, such as “yesterday” or “in 3 hours”.

number(value, options?): string;

A number with the user’s separators, for example “1,234.5”.

Parameter Typ Beschreibung
value number The number.
options? NumberFormatOptions Intl.NumberFormat options.

string

The formatted number.

bytes(value): string;

A byte count with decimal units, as the desktop shows sizes (10 GB, 4 TB).

Parameter Typ Beschreibung
value number The size in bytes.

string

The size with one decimal below 100 of its unit.

list(values, options?): string;

A list, for example “Anna, Ben and Chris”.

Parameter Typ Beschreibung
values readonly string[] The items, already as text.
options? ListFormatOptions Intl.ListFormat options; a conjunction when left out.

string

The joined list.


The app manifest (pithyx.json at the root of an app package). It tells the daemon and the desktop what the app is, what it may do and how its window and backend behave. The icon is always icon.svg next to it. This schema is a stable contract: a new optional field is a minor change, anything else a major one. Unknown fields are errors.

optional $schema?: string;

Path or URL of this schema, for editors. Ignored by the box.

manifestVersion: 1;

The manifest schema this file follows.

id: string;
version: string;

SemVer 2.0 version of the app, without build metadata, for example 1.2.0 or 2.0.0-beta.1.

name: LocalizedName;
optional description?: LocalizedText;
author: AppAuthor;
optional license?: string;

SPDX license expression of the app, for example MIT or MIT OR Apache-2.0.

optional homepage?: string;

The app’s website or source repository.

entry: string;

Path of the HTML page the desktop loads in the sandboxed window, inside frontend/ of the package, for example frontend/index.html. Segments never start with a dot.

sdk: string;

SemVer range of @pithyx/sdk the app was built against, for example ^1.0.0.

optional pithyx?: string;

SemVer range of the Pithyx versions the app runs on, for example >=0.1.0. Optional; without it the app claims to run on every version. A box installs a version only when its own version is in the range, and lists a catalog version only then. Unlike npm, a box running a prerelease matches by plain SemVer precedence, so >=0.1.0 includes 0.2.0-beta.1, while ^0.1.0 does not (it ends below 0.2.0-0). Catalogs require it.

optional dataVersion?: number;

The version of the format of the app’s stored data (volumes, its stored values and the files of its storage requests), an integer from 1. Optional. Raise it with every release that converts data in a way the previous version cannot read. A box goes back to an older version from a catalog only when both declare the same dataVersion; going back to the version before an update with its snapshot works without it.

permissions: Permission[];

The permissions the app asks for. Each one appears in the install dialog and can be revoked later.

window: WindowManifest;
optional storage?: StorageRequest[];

Places for files the app needs, each mapped by an administrator to a folder in a shared folder.

16

optional fileHandlers?: FileHandler[];

Files the app can open.

32

optional backend?: BackendManifest;

A short text in several languages, keyed by BCP 47 language tag, for example the app’s name or a storage request’s name. en is required and used when nothing else matches. One line, at most 60 characters per language.

[k: string]: string

The text in the language of the key, a BCP 47 language tag such as de or pt-BR. One line, at most 60 characters.

This interface was referenced by LocalizedName’s JSON-Schema definition via the patternProperty “^[a-z]{2,3}(-[A-Za-z0-9]{1,8})*$”.

en: string;

The English text, shown when no other language matches. One line, at most 60 characters.


A text in several languages, keyed by BCP 47 language tag, for example the app’s description of one or two sentences for the store and the install dialog. en is required and used when nothing else matches. At most 500 characters per language; line breaks are allowed.

[k: string]: string

The text in the language of the key, a BCP 47 language tag such as de or pt-BR. At most 500 characters; line breaks are allowed.

This interface was referenced by LocalizedText’s JSON-Schema definition via the patternProperty “^[a-z]{2,3}(-[A-Za-z0-9]{1,8})*$”.

en: string;

The English text, shown when no other language matches. At most 500 characters; line breaks are allowed.


Who makes the app.

name: string;

A person or an organization.

optional url?: string;

The author’s website.


How the app’s windows behave. defaultSize is the size of a new window when the user has no remembered size for the app; minSize is the smallest size on screens that can fit it, at most defaultSize in both dimensions.

instances: 'single' | 'multi';

single: opening the app again focuses the user’s existing window on this device and passes the launch arguments to it (launch event). multi: every launch opens a new window.

defaultSize: WindowSize;
minSize: WindowSize;
optional loading?: WindowLoading;

A size in CSS pixels.

width: number;

The width in CSS pixels.

height: number;

The height in CSS pixels.


What a window shows before the app is ready. The desktop always draws the app’s icon and name in a new window and an indicator after 300 ms, until the app connected (or, with untilReady, until it called app.window.ready()). While the backend starts, a line of text says so; without text it is the desktop’s own, such as “Starting Lists …”. text is the line shown while the backend starts, in the user’s language, for example “Opening your lists …”: say what happens in plain words, without technical terms.

optional text?: LocalizedName;
optional untilReady?: boolean;

Whether the window keeps the placeholder after the app connected until it calls app.window.ready(), for an app that first loads its data. The desktop removes it after 30 seconds in any case. Default false.


A button of a notification.

id: string;

Comes back in notificationAction; 1 to 64 characters.

label: string;

Plain text, 1 to 40 characters, translated by the app.


What the app learns when the user clicks one of its notifications.

notification: string;

The id notify resolved with.

action: string | null;

The id of the button the user clicked, or null for a click on the notification itself.

data: unknown;

The data the app passed to notify, or null.


A notification the app shows. The app translates the texts; it knows the locale from the context. It appears as a toast with the app’s icon and stays in the user’s notification history (at most 200 entries, 30 days).

title: string;

Plain text, 1 to 200 characters.

optional body?: string;

Plain text, at most 1000 characters.

optional level?: "info" | "success" | "warning" | "error";

How the toast looks; info when left out.

optional actions?: NotificationAction[];

At most 2 buttons. A click sends notificationAction, or starts the app with launch.notification.

optional data?: unknown;

Data that comes back with notificationAction, at most 4 KB as JSON.

optional tag?: string;

Replaces the app’s earlier notification with the same tag for this user, for example one per chat. 1 to 64 characters.


A user of the box, as directory.users lists it. Deactivated users are left out.

id: number;

Stable id of the user: the Linux uid, also X-Pithyx-User-Id for the backend; never given to another user.

name: string;

The user name, lower case.

displayName: string | null;

The name to show, if the user set one.

groups: number[];

Ids of the user’s groups (see directory.groups); the built-in group every user belongs to is left out.


A group of the box, as directory.groups lists it. The built-in group every user belongs to is left out.

id: number;

Stable id (the group’s Linux gid).

name: string;

The group’s name.

description: string;

Free text, may be empty.


A point in CSS pixels, relative to the top left corner of the app’s window content.

x: number;

Distance from the left edge.

y: number;

Distance from the top edge.


One entry of the app’s log, as the SDK sends it (see app.log).

level: LogLevel;

How serious the entry is.

message: string;

At most 4000 characters; the SDK cuts longer ones.

optional data?: Record<string, string>;

At most 20 fields with texts of at most 1000 characters; the SDK cuts the rest.


Every method an app can call: parameters and result. A method name is part of the stable API; a new method is a minor change and appears in AppContext.capabilities.

window.setTitle: object;

Sets the window title (shown in the title bar and the taskbar). At most 200 characters.

params: object;
title: string;
result: undefined;
window.close: object;

Closes the window. beforeClose is not sent for a close the app asked for.

params: Record<string, never>;
result: undefined;
window.setDirty: object;

Marks the window as having unsaved changes or not. Once an app called it, the desktop asks beforeClose only while the window is dirty; an app that never calls it is asked on every close.

params: object;
dirty: boolean;
result: undefined;
window.requestAttention: object;

Makes the window’s taskbar entry pulse until the user looks at it. It never brings the window to the front or takes the focus.

params: Record<string, never>;
result: undefined;
window.ready: object;

Ends the desktop’s loading placeholder of the window. Only needed with the manifest’s window.loading.untilReady; otherwise the placeholder goes when the app connects. Calling it again does nothing.

params: Record<string, never>;
result: undefined;
window.setSize: object;

Asks for a new free size. The desktop may clamp it; the result is the size the window got.

params: object;
width: number;
height: number;
result: object;
width: number;
height: number;
log.write: object;

Writes entries to the app’s log in the Log Center (source app/<id>, with the signed-in user). The SDK batches app.log calls into this method; apps do not call it directly. At most 100 entries per call; the daemon keeps each app within a rate limit and drops the rest. No permission needed.

params: object;
entries: LogRecord[];
result: undefined;
notify: object;

Shows a notification and resolves with its id. Needs the notifications permission.

params: NotificationRequest;
result: object;
id: string;
setBadge: object;

A counter on the app’s taskbar entry: a positive integer shows it, 0 or null removes it.

params: object;
count: number | null;
result: undefined;
session.save: object;

Saves small state of this window (the current folder, a scroll position) with the desktop layout, at most 16 KB as JSON. It comes back in the init context after a reload and with “Get desktop from …”.

params: object;
state: unknown;
result: undefined;
storage.get: object;

Reads a value the app stored for this user, or null.

params: object;
key: string;
result: unknown;
storage.set: object;

Stores a value for this user, on the daemon (sandboxed apps have no localStorage). Keys have at most 128 characters, values at most 64 KB as JSON, and an app keeps at most 1 MB per user.

params: object;
key: string;
value: unknown;
result: undefined;
storage.remove: object;

Removes a value the app stored for this user; a missing key is no error.

params: object;
key: string;
result: undefined;
storage.keys: object;

Every key the app stored for this user.

params: Record<string, never>;
result: string[];
backend.token: object;

A token for the app’s backend, valid 5 minutes and for every endpoint of the app. app.backend.fetch and app.backend.url send it for the app (as Authorization: Bearer or ?pithyx-token=). The daemon’s proxy checks it and hands the backend the user as X-Pithyx-User, X-Pithyx-User-Id, X-Pithyx-Groups and X-Pithyx-Admin; those headers are the only identity a backend may trust. With renew the desktop drops the token it holds and issues a new one: the SDK asks so after the proxy refused a token as app.token.invalid, for example because the box restarted.

params: object;
optional renew?: boolean;
result: BackendToken;
backend.ticket: object;

A one-time ticket for one WebSocket to an endpoint of the app’s backend (app.backend.socket uses it).

params: object;
endpoint: string;
result: BackendTicket;
directory.users: object;

Every user of the box. Needs directory.read.

params: Record<string, never>;
result: DirectoryUser[];
directory.groups: object;

Every group of the box. Needs directory.read.

params: Record<string, never>;
result: DirectoryGroup[];
contextMenu.show: object;

Shows a context menu that the desktop draws at at (at most 50 entries) and resolves with the id of the entry the user picked, or null when the menu was dismissed.

params: object;
at: Point;
items: ContextMenuItem[];
result: string | null;
clipboard.writeText: object;

Copies text to the clipboard, at most 1 MB. Call it only for a user action, such as a click on Copy.

params: object;
text: string;
result: undefined;
openUrl: object;

Opens an https:, http: or mailto: URL in a new browser tab, without a reference to the desktop.

params: object;
url: string;
result: undefined;
files.pick: object;

The file API (served from step 6 on; check supports). The only way an app reaches files: sandboxed apps have no file system, and the daemon checks the app’s permissions and the user’s rights on every call.

Opens the desktop’s file dialog; resolves with the files the user picked, empty when cancelled. Needs files.read.

params: object;
optional multiple?: boolean;
optional extensions?: string[];
result: FileRef[];
files.saveAs: object;

Opens the desktop’s save dialog; resolves with the new file, or null when cancelled. Needs files.write.

params: object;
suggestedName: string;
data: Blob;
result: FileRef | null;
files.read: object;

Reads a file the app was given. Needs files.read.

params: object;
file: FileRef;
result: Blob;
files.write: object;

Replaces the contents of a writable file. Needs files.write.

params: object;
file: FileRef;
data: Blob;
result: FileRef;
files.url: object;

A URL for <img>, <video> or fetch that streams the file (with range requests) without the desktop session, valid 5 minutes and only for this file. Needs files.read.

params: object;
file: FileRef;
result: object;
url: string;
expiresAt: string;
files.list: object;

Lists a folder one page at a time (default 100 entries, at most 1000), in natural name order unless sort says otherwise. Pass the page’s nextCursor for the next one. Needs files.shares for a share.

params: object;
location: FileLocation;
path: string;
optional cursor?: string;
optional limit?: number;
optional sort?: SortKey;
optional order?: SortOrder;
result: FolderPage;
files.open: object;

A handle for a file in a named storage or a share, for files.read, files.write and files.url.

params: object;
location: FileLocation;
path: string;
result: FileRef;
files.shares: object;

The shares the user can use, with the user’s access to each; sharesChanged says when to ask again.

params: Record<string, never>;
result: ShareInfo[];
files.watch: object;

Starts live changes of a folder: folderChanged events with the returned watch id arrive while the watch is open. Watch only folders on screen and call files.unwatch when leaving them; a watch never wakes a disk.

params: object;
location: FileLocation;
path: string;
result: object;
watch: string;
files.unwatch: object;

Ends a watch started with files.watch.

params: object;
watch: string;
result: undefined;
files.upload: object;

Uploads a file into a folder. The file appears only when it is complete; an existing name fails with fs.entry.exists unless overwrite. With an upload id, uploadProgress events report the bytes sent.

params: object;
location: FileLocation;
folder: string;
name: string;
data: Blob;
optional overwrite?: boolean;
optional upload?: string;
result: FileEntry;
files.download: object;

Downloads a file to the user’s device through the browser.

params: object;
location: FileLocation;
path: string;
result: undefined;
files.createFolder: object;

Creates a folder; its parent must exist.

params: object;
location: FileLocation;
path: string;
result: FileEntry;
files.rename: object;

Renames an entry within its folder; an existing name is never replaced (fs.entry.exists).

params: object;
location: FileLocation;
path: string;
name: string;
result: FileEntry;
files.run: object;

Starts a copy, move or delete as a job and resolves with it at once; jobChanged events follow its progress until it finishes. Deleting moves to the share’s recycle bin unless permanent. Source and target may each be a share or a storage request.

params: FileOperation;
result: JobInfo;
files.versions: object;

Earlier versions of a file or folder from the snapshots of the share’s dataset, newest first; also of an entry deleted since. A file is listed once per distinct content and not while it equals the current file. Empty when the share has no snapshots. Needs read access to the share.

params: object;
location: ShareLocation;
path: string;
result: FileVersion[];
files.restoreVersion: object;

Copies a version back as a job: next to the current entry as “name (version date)”, or over it with overwrite (files are replaced, folders merged). Needs write access to the share.

params: object;
location: ShareLocation;
path: string;
snapshot: string;
optional overwrite?: boolean;
result: JobInfo;
jobs.cancel: object;

Cancels a job the app started; a finished job stays as it is.

params: object;
job: number;
result: undefined;
trash.list: object;

One page of a share’s recycle bin, newest first. Needs read access to the share.

params: object;
location: ShareLocation;
optional cursor?: string;
optional limit?: number;
result: TrashPage;
trash.restore: object;

Puts items back where they were, recreating missing folders, as a job. A taken name gets a free one unless onConflict says otherwise.

params: object;
location: ShareLocation;
ids: number[];
optional onConflict?: ConflictPolicy;
result: JobInfo;
trash.delete: object;

Deletes items of the recycle bin for good, as a job. Only after the user confirmed it.

params: object;
location: ShareLocation;
ids: number[];
result: JobInfo;
trash.empty: object;

Deletes everything in the recycle bin for good, as a job. Only after the user confirmed it.

params: object;
location: ShareLocation;
result: JobInfo;
ui.showError: object;

Shows the desktop’s error dialog for a problem, with its translated text and the request id to copy. Use it for failures the user has to know about; the app does not need its own error texts for daemon codes.

params: object;
problem: Problem;
result: undefined;

What the desktop posts to the iframe window, with the app’s port transferred.

type: 'pithyx:connect';

Always pithyx:connect.


One end of a connection.

post(message): void;

Sends a message to the other end.

Parameter Typ Beschreibung
message unknown Structured-clone data.

void

listen(handler): void;

Registers the single message handler of this end; a second call replaces the first handler.

Parameter Typ Beschreibung
handler (message) => void Called with each message from the other end.

void

close(): void;

Closes this end; nothing is sent or received after it.

void

type AppId = string;

The author’s domain reversed plus the app name: 3 to 5 parts of lowercase letters, digits and dashes, at most 100 characters, for example com.mueller.chat or io.github.jan.chat. Unique per box. org.pithyx.* is reserved for the system apps and the official catalog.


type Permission =
| 'files.read'
| 'files.write'
| 'files.shares'
| 'notifications'
| 'directory.read'
| 'network.internet'
| 'network.lan';

A permission an app declares. Nothing is granted by using an API: the install dialog lists every declared permission, an administrator confirms them for the app, and each one can be revoked later (the app gets permissionsChange). Which users may use the app at all is a separate grant per user and group. The daemon checks every permission on every call that reaches data or leaves the window. An update that declares a new permission waits until an administrator confirms it; the old version keeps running. files.read: read files the user opens with the app or picks in a desktop file dialog. files.write: write those files, and save new files where the user chooses in a desktop save dialog. files.shares: browse every shared folder the user may access, with the user’s own rights. notifications: show notifications. directory.read: list all users and groups of the box. network.internet: outgoing connections to the internet, from the window (connect-src https:) and from the backend. network.lan: outgoing connections from the backend to the local network (private, shared and link-local addresses such as 192.168.0.0/16); without it the backend reaches no other device in the LAN, also with network.internet. The box itself is never reachable.


type ElevatedPermission =
| 'docker.socket'
| 'host.network'
| 'host.pid'
| 'host.full'
| `device:/dev/${string}`
| `capability:${string}`;

A right beyond the sandbox, for the backend only (D71): host.network (the host’s network namespace), host.pid (the host’s process namespace), device:<path under /dev> (one device node, for example device:/dev/dri/renderD128), capability:<NAME> (one Linux capability without the CAP_ prefix, for example capability:NET_ADMIN), docker.socket (the Docker Engine API through the daemon’s filtering proxy) and host.full (a privileged container: root on the host). The schema describes the planned rights; Pithyx 1.0.0 refuses every one of them with app.elevated.unsupported, and a later version grants them to administrators only, after a confirmation with password and TOTP.


type ContextMenuItem =
| {
id: string;
label: string;
disabled?: boolean;
checked?: boolean;
danger?: boolean;
shortcut?: string;
}
| {
separator: true;
};

An entry of a context menu the desktop draws for the app; the app translates the labels.

{
id: string;
label: string;
disabled?: boolean;
checked?: boolean;
danger?: boolean;
shortcut?: string;
}
id: string;

Comes back from contextMenu.show when the user picks the entry; 1 to 64 characters.

label: string;

Plain text, 1 to 100 characters.

optional disabled?: boolean;

Shows the entry greyed out; it cannot be picked.

optional checked?: boolean;

Shows a check mark.

optional danger?: boolean;

Marks a destructive entry, such as Delete.

optional shortcut?: string;

A key hint shown next to the label, for example Ctrl+C. The app handles the keys itself.


{
separator: true;
}
separator: true;

A line between groups of entries.


type LogLevel = 'debug' | 'info' | 'warn' | 'error';

How serious a log entry is. The Log Center filters by it; debug is for details only needed while looking for a fault.


type LogData = Record<string, string | number | boolean | null>;

Fields that describe a log entry beyond its message, for example { file: 'a.jpg', attempt: 2 }.


type MethodName = keyof Methods;

The name of a method an app can call.


type AppMessage =
| {
type: 'hello';
protocol: number;
sdk: string;
}
| {
type: 'request';
id: number;
method: string;
params: unknown;
}
| {
type: 'reply';
id: number;
answer: unknown;
};

A message from the app to the desktop.

{
type: 'hello';
protocol: number;
sdk: string;
}
type: 'hello';

The first message on the port.

protocol: number;

The protocol version the app speaks.

sdk: string;

The SDK version the app was built with.


{
type: 'request';
id: number;
method: string;
params: unknown;
}
type: 'request';

A call of a method.

id: number;

Unique per connection; the response carries it back.

method: string;

A method name of Methods.

params: unknown;

The method’s parameters.


{
type: 'reply';
id: number;
answer: unknown;
}
type: 'reply';

The answer to a query.

id: number;

The id of the query.

answer: unknown;

The answer the question expects.


type HostMessage =
| {
type: 'init';
protocol: number;
context: AppContext;
}
| {
type: 'refuse';
problem: Problem;
}
| {
type: 'response';
id: number;
ok: true;
result: unknown;
}
| {
type: 'response';
id: number;
ok: false;
problem: Problem;
}
| {
type: 'event';
event: string;
data: unknown;
}
| {
type: 'query';
id: number;
query: string;
data: unknown;
};

A message from the desktop to the app.

{
type: 'init';
protocol: number;
context: AppContext;
}
type: 'init';

The answer to hello: the app may start.

protocol: number;

The protocol version both sides use.

context: AppContext;

Everything the app needs to start.


{
type: 'refuse';
problem: Problem;
}
type: 'refuse';

The answer to hello when the desktop cannot serve the app; the port closes after it.

problem: Problem;

Why, for example sdk.protocol.unsupported.


{
type: 'response';
id: number;
ok: true;
result: unknown;
}
type: 'response';

The result of a request.

id: number;

The id of the request.

ok: true;

The call succeeded.

result: unknown;

The method’s result.


{
type: 'response';
id: number;
ok: false;
problem: Problem;
}
type: 'response';

The failure of a request.

id: number;

The id of the request.

ok: false;

The call failed.

problem: Problem;

Why it failed.


{
type: 'event';
event: string;
data: unknown;
}
type: 'event';

An event of Events.

event: string;

The event name.

data: unknown;

The event’s data.


{
type: 'query';
id: number;
query: string;
data: unknown;
}
type: 'query';

A question of Queries the app answers with reply.

id: number;

Unique per connection; the reply carries it back.

query: string;

The question’s name.

data: unknown;

What the question carries.

const REQUEST_TIMEOUT: 15000 = 15_000;

An app waits this long for a response before a call fails with sdk.request.timeout. Calls that move a whole file (files.upload, files.read, files.write) take as long as the transfer, and the dialogs of files.pick and files.saveAs as long as the user; none of them has a time limit.


const UNTIMED_METHODS: readonly string[];

Calls without a time limit, because they last as long as a transfer or as the user takes.


const SDK_VERSION: string = version;

The version of this SDK package, from its package.json. The npm packages @pithyx/sdk, @pithyx/ui and @pithyx/cli share one SemVer line of their own, separate from the Pithyx product version: a new method, event or optional field is a minor release, a removed or changed one a major release.

Deprecations last one major version: anything marked @deprecated in major N keeps working through all of major N + 1 and goes away in N + 2 at the earliest. The desktop of major N + 1 still serves apps built against SDK N (see MIN_PROTOCOL_VERSION).


const PROTOCOL_VERSION: 1 = 1;

The version of the wire protocol between an app and the desktop. It changes only when a message changes in a way an older side cannot ignore. Additions (new methods, events, optional fields) keep the protocol version; apps find them through AppContext.capabilities.

The desktop supports every protocol version from MIN_PROTOCOL_VERSION up to this one, so an app built against an older SDK keeps working after a desktop update.


const MIN_PROTOCOL_VERSION: 1 = 1;

The oldest protocol version this SDK’s host side still accepts.

function createApp(endpoint, options?): Promise<PithyxApp>;

Connects an app over a transport end: sends hello, waits for init, and returns the app API. Rejects with sdk.protocol.unsupported when the desktop refuses the app’s protocol version. Core apps use it with inProcessPair; sandboxed apps call connect instead.

Parameter Typ Beschreibung
endpoint Endpoint The app’s end of the transport.
options CreateAppOptions Theme root, call time limit and the box’s address.

Promise<PithyxApp>

The app API, once the desktop sent init.

sdk.protocol.unsupported when the desktop does not speak the SDK’s protocol version.


function createFormatters(current): Formatters;

Formatters that follow the locale and time zone current returns at each call. app.format is built with it; an app needs it only to format outside the desktop, for example in tests.

Parameter Typ Beschreibung
current () => object Returns the locale and the time zone to use now.

Formatters

The formatters.


function connect(options?): Promise<PithyxApp>;

Connects a sandboxed app to the desktop. It accepts exactly one pithyx:connect message, only from the parent window (the desktop that framed the app) and only with exactly one transferred port; every other window message is ignored, before and after. Rejects with sdk.transport.closed when no desktop connects in time, for example when the page was opened outside the desktop. From then on a right-click in the app never opens the browser’s own menu, except in text fields; an app shows its own menu by listening for contextmenu.

Parameter Typ Beschreibung
options CreateAppOptions & object Theme root, call time limit and box address as for createApp, plus how long to wait for the desktop.

Promise<PithyxApp>

The app API, once the desktop accepted the app.

sdk.transport.closed when no desktop connects within the time limit.

sdk.protocol.unsupported when the desktop does not speak the SDK’s protocol version.


function portEndpoint(port): Endpoint;

An end backed by a MessagePort (third-party apps in sandboxed iframes).

Parameter Typ Beschreibung
port MessagePort The port the desktop transferred with pithyx:connect.

Endpoint

The transport end; closing it closes the port.


function inProcessPair(): [Endpoint, Endpoint];

A pair of connected ends inside one page (core apps). Messages are cloned with structuredClone and delivered in a later microtask, exactly like a MessagePort would, so nothing works in-process that would fail in an iframe.

[Endpoint, Endpoint]

The two connected ends: one for the app, one for the host. Closing either closes both.