index ↓
doc
client.doc Daemon -> webapp doc replies and events. Changed broadcasts whenever
the companion writes or deletes a doc value for the active webapp.
requests
you ask, the daemon answers. await the tagged result and check .ok
get(DocGet): Promise<TypedRequestResult<DocGetReply, never>>
push form: onGet (subscribe instead of awaiting)
Webapp request: read one doc value for the currently active webapp.
const res = await client.doc.get({ key: '...' });
if (res.ok) {
console.log(res.response.key);
} type Result =
| { ok: true; response: DocGetReply }
| { ok: false; kind: 'domain'; error: never }
| { ok: false; kind: 'protocol'; error: WireError }; list(): Promise<TypedRequestResult<DocListReply, never>>
push form: onList (subscribe instead of awaiting)
Marker request: read every doc entry for the currently active webapp.
const res = await client.doc.list();
if (res.ok) {
console.log(res.response.entries);
} type Result =
| { ok: true; response: DocListReply }
| { ok: false; kind: 'domain'; error: never }
| { ok: false; kind: 'protocol'; error: WireError }; set(DocSet): Promise<TypedRequestResult<DocAck, WebappError>>
push form: onAck (subscribe instead of awaiting)
Webapp request: write a doc value. Last write wins against companion
writes on the same key; the companion hears the change as a gateway
webapp.docChanged event.
const res = await client.doc.set({ key: '...', value: '...' });
if (res.ok) {
console.log(res.response.key);
} else {
console.warn(res.kind, res.error);
} type Result =
| { ok: true; response: DocAck }
| { ok: false; kind: 'domain'; error: WebappError }
| { ok: false; kind: 'protocol'; error: WireError }; delete(DocDelete): Promise<TypedRequestResult<DocAck, never>>
push form: onAck (subscribe instead of awaiting)
Webapp request: delete the doc entry at key.
const res = await client.doc.delete({ key: '...' });
if (res.ok) {
console.log(res.response.key);
} type Result =
| { ok: true; response: DocAck }
| { ok: false; kind: 'domain'; error: never }
| { ok: false; kind: 'protocol'; error: WireError }; events
the daemon pushes these unprompted. subscribing returns an unsubscribe function
onChanged(handler: (DocChanged) => void): () => void
const off = client.doc.onChanged((docChanged) => {
console.log(docChanged.key);
});
// call off() to unsubscribe types
shapes referenced above, as the sdk types them
Broadcast when the COMPANION writes the active webapp's doc namespace. Webapp-origin writes are not echoed back (the writer already holds the ack).
type DocChanged = {
key: string;
value?: string; // `None` means the entry was deleted.
}; Webapp request: read one doc value for the currently active webapp.
type DocGet = {
key: string;
}; Reply to DocGet.
type DocGetReply = {
key: string;
value?: string; // `None` when the key has never been written.
}; Reply to DocList.
type DocListReply = {
entries: DocEntry[];
}; Webapp request: write a doc value. Last write wins against companion
writes on the same key; the companion hears the change as a gateway
webapp.docChanged event.
type DocSet = {
key: string;
value: string;
}; Ack for DocSet / DocDelete; echoes what's now stored.
type DocAck = {
key: string;
value?: string;
}; Domain errors emitted by any webapp surface (gateway- or client-side). Single catalog: both protocols speak the same variant set.
type WebappError =
| 'webappNotFound' // No installed webapp matches this id (uninstall / activate / icon / config target).
| 'cannotUninstallBuiltin' // Built-in webapps cannot be uninstalled.
| 'idReserved' // Install rejected: the manifest's id is in the reserved-uuid set (stock, hub, launcher, etc).
| 'extractedTooLarge' // Extracted bundle exceeds the 1 GiB disk-protection cap.
| 'provenanceTooLong' // Install carried a provenance string over `WEBAPP_PROVENANCE_MAX_LEN`.
| 'zipMalformed' // Zip extraction failed: corrupt archive, unsafe entry names, etc.
| 'missingIndexHtml' // Bundle has no index.html at its root.
| 'invalidManifest' // manifest.json missing, unparseable, or failed schema validation.
| 'resourceNotAvailable' // The requested resource (icon / settings page / overlay) isn't declared by the webapp's manifest or its file is missing on disk.
| 'notALauncher' // Launcher slot rejected: the bundle does not declare `role: launcher`.
| 'noOverlay' // Overlay slot rejected: the bundle declares no overlay entry.
| 'unknownConfigKey' // Config key is not declared in the webapp's manifest schema.
| 'invalidConfigValue' // Value failed schema validation (out of range, regex mismatch, not in enum).
| 'invalidDocValue' // Doc value rejected (oversized).
| 'internal' // Catch-all for genuinely-unexpected failures (io errors, daemon-side bugs). Reason is human-readable; not a stable wire contract.; Webapp request: delete the doc entry at key.
type DocDelete = {
key: string;
}; One key/value pair from a webapp's doc namespace: shared structured state writable from both the companion (gateway) and the webapp itself, last write wins. Values are strings; apps encode JSON as needed.
type DocEntry = {
key: string;
value: string;
};