Skip to content

ReferenceLook things up as you need them.

device reference ​

Every method of the device global, with its arguments, return value, errors, where it works, and an example. The Device API guide explains where device exists and how to call it from a server kernel or a browser.

IMPORTANT

macOS and iOS only, for now. The native side is the Swift DeviceBridge in Ganymede's Mac, iPad and iPhone apps (macOS 26 and iOS 26). There is no host for Windows, Linux or Android. A notebook page in any browser can answer a few methods that need no permission with web APIs (In a browser); everything else needs one of the apps.

device is undefined on kernels the app doesn't host, so a cell checks for it first (device?.clipboard.readText(), or if (device) …). The examples skip that check, and the SoundPlayer examples use a player from device.sound.player, and the Peripheral examples a peripheral from device.bluetooth.connect.

For editor types, add this line once to a cell or a project file:

ts
/// <reference types="@tlockney/ganymede-device/globals" />

Every method takes a last options argument with a signal (CallOptions), and fails with a DeviceError. Availability says where each method works: in the app (a kernel the Mac, iPad or iPhone app runs itself), the system permission it needs there, from a remote kernel in the app, and from a kernel driven from a browser. "planned" means it doesn't yet but will; "no" means the platform has no such API.

MethodWhat it does
device.info(options?)Reads what the host is and the device's current state.
device.has(method)Whether the host answers a method.
device.on(event, handler, options?)Calls handler each time the device changes in the way event names, until it is removed: by calling the function on answers, by aborting options.signal, or by a kernel restart.
device.openURL(url, options?)Opens a link: in the browser, or the mail app for mailto.
device.share(options)Shows the system share sheet (NSSharingServicePicker on the Mac).
device.preview(files, options?)Shows files in Quick Look.
device.output(result, options?)Hands result back to whatever started this run: the share sheet's host app, or the Shortcut's next action.
device.clipboard.readText(options?)Reads the clipboard's text.
device.clipboard.writeText(text, options?)Replaces the clipboard's contents with text.
device.clipboard.readImage(options?)Reads the clipboard's image as PNG bytes, whatever format was copied.
device.clipboard.writeImage(image, options?)Replaces the clipboard's contents with an image.
device.haptics.impact(style?, options?)Plays a tap.
device.haptics.notification(type, options?)Plays the system's success, warning or error pattern.
device.haptics.selection(options?)Plays the tick of a picker moving to a new value.
device.ui.alert(message, options?)Shows a message with an OK button.
device.ui.confirm(message, options?)Asks a yes-or-no question.
device.ui.prompt(message, options?)Asks for a line of text.
device.ui.actionSheet(title, options, sheetOptions?)Asks the person to choose one of several options.
device.ui.pickDate(options?)Asks for a date, a time, or both.
device.files.pick(options?)Shows the open panel on the Mac, or the document picker on iPad and iPhone, and reads the files the person picks.
device.files.export(data, options)Asks where to save data, and saves it there: the save panel on the Mac, the document picker on iPad and iPhone.
device.photos.authorizationStatus(options?)Reads the permission's status, without asking.
device.photos.isAuthorized(options?)Whether the permission is granted, without asking.
device.photos.requestAuthorization(options?)Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is.
device.photos.pick(options?)Shows the system photo picker and reads the photos the person picks.
device.photos.save(image, options?)Adds an image to the photo library, as a new photo.
device.sound.play(data, options?)Starts playing a sound, which plays to its end on its own.
device.sound.player(data, options?)Loads a sound into a player, paused at the start, for a cell to play, pause, seek and close.
device.notifications.authorizationStatus(options?)Reads the permission's status, without asking.
device.notifications.isAuthorized(options?)Whether the permission is granted, without asking.
device.notifications.requestAuthorization(options?)Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is.
device.notifications.schedule(body, options)Schedules a notification to show after a delay, even if the notebook or the app has closed by then.
device.notifications.pending(options?)Lists this notebook's notifications that haven't been delivered yet.
device.notifications.cancel(id, options?)Takes one of this notebook's notifications off the schedule.
device.notifications.cancelAll(options?)Takes all of this notebook's notifications off the schedule.
device.location.authorizationStatus(options?)Reads the permission's status, without asking.
device.location.isAuthorized(options?)Whether the permission is granted, without asking.
device.location.requestAuthorization(options?)Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is.
device.location.current(options?)One fix, at the accuracy asked for.
device.location.watch(options?)Fixes as the device moves, for for await, until the loop ends or the cell is interrupted.
device.location.geocode(address, options?)The places an address may be.
device.location.reverseGeocode(position, options?)What is at a position, such as a fix from current().
device.motion.authorizationStatus(options?)Reads the permission's status, without asking.
device.motion.isAuthorized(options?)Whether the permission is granted, without asking.
device.motion.requestAuthorization(options?)Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is.
device.motion.watch(options?)Samples every interval seconds, for for await, until the loop ends or the cell is interrupted.
device.motion.latest(options?)The newest sample.
device.contacts.authorizationStatus(options?)Reads the permission's status, without asking.
device.contacts.isAuthorized(options?)Whether the permission is granted, without asking.
device.contacts.requestAuthorization(options?)Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is.
device.contacts.search(query, options?)Contacts whose name matches query; or whose email matches, when query is a whole email address (a part of one, such as "@example.com", matches nothing); or whose phone number matches, when it is a phone number.
device.contacts.get(id, options?)One contact, by the id search, all or add gave.
device.contacts.all(options?)Every contact the app may see, in the person's sort order.
device.contacts.add(contact, options?)Adds a contact to the default account.
device.contacts.delete(id, options?)Deletes a contact.
device.camera.authorizationStatus(options?)Reads the permission's status, without asking.
device.camera.isAuthorized(options?)Whether the permission is granted, without asking.
device.camera.requestAuthorization(options?)Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is.
device.camera.capture(options?)Shows the system camera and waits for the person to take a photo.
device.reminders.authorizationStatus(options?)Reads the permission's status, without asking.
device.reminders.isAuthorized(options?)Whether the permission is granted, without asking.
device.reminders.requestAuthorization(options?)Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is.
device.reminders.calendars(options?)The reminder lists.
device.reminders.list(options?)Reminders, from one list or all of them.
device.reminders.add(reminder, options?)Adds a reminder.
device.reminders.complete(id, options?)Marks a reminder done.
device.reminders.uncomplete(id, options?)Marks a reminder not done.
device.reminders.delete(id, options?)Deletes a reminder.
device.bluetooth.authorizationStatus(options?)Reads the permission's status, without asking.
device.bluetooth.isAuthorized(options?)Whether the permission is granted, without asking.
device.bluetooth.requestAuthorization(options?)Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is.
device.bluetooth.scan(options?)Advertisements from nearby peripherals, for for await, until the loop ends or the cell is interrupted.
device.bluetooth.known(ids, options?)Peripherals the system knows by id, such as one a past scan found, to connect again without scanning.
device.bluetooth.connect(id, options?)Connects to a peripheral.
device.folders.pick(options?)Shows the system folder picker and keeps what the person picks for this notebook.
device.folders.list(options?)The folders this notebook was given, in the order they were picked.
device.widget.update(kind, content, options?)Publishes content for this notebook's widget kind, in place of what it showed, and asks the system to redraw it.
device.shortcuts.authorizationStatus(options?)Reads the permission's status, without asking.
device.shortcuts.isAuthorized(options?)Whether the permission is granted, without asking.
device.shortcuts.requestAuthorization(options?)Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is.
device.shortcuts.run(name, options?)Runs a Shortcut by name and waits for it to finish.
device.secrets.authorizationStatus(options?)Reads the permission's status, without asking.
device.secrets.isAuthorized(options?)Whether the permission is granted, without asking.
device.secrets.requestAuthorization(options?)Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is.
device.secrets.get(name, options?)Reads a secret.
device.secrets.set(name, value, options?)Saves a secret, in place of one by the same name.
device.secrets.delete(name, options?)Deletes a secret.
device.secrets.list(options?)The names of this notebook's secrets, never their values.
player.play(options?)Plays from the current time.
player.pause(options?)Pauses at the current time.
player.stop(options?)Pauses and goes back to the start.
player.seek(seconds, options?)Moves to a time, playing or not.
player.setVolume(volume, options?)Sets the volume.
player.state(options?)Reads the player's state, changing nothing.
player.close()Stops the player and frees it; later calls fail failed.
peripheral.services(options?)Its services and their characteristics, discovered on the first call.
peripheral.read(service, characteristic, options?)Reads a characteristic's value.
peripheral.write(service, characteristic, data, options?)Writes a characteristic's value.
peripheral.notify(service, characteristic, options?)A characteristic's values as it notifies or indicates them, for for await, until the loop ends, the cell is interrupted, or the peripheral closes.
peripheral.rssi(options?)Reads the signal strength now.
folder.entries(path?, options?)Lists a directory in the folder, sorted by name.
folder.read(path, options?)Reads a file in the folder.
folder.write(path, data, options?)Writes a file in the folder, replacing what is there and making the directories on the way.
folder.remove(path, options?)Removes a file, a symlink (not what it points to), or an empty directory, in the folder.

device ​

The device global: native features of the Mac, iPad and iPhone apps. Undefined on kernels the app doesn't host, so check for it first.

device.info(options?) ​

ts
info(options?: CallOptions): Promise<DeviceInfo>;

Reads what the host is and the device's current state.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<DeviceInfo>: The host's answer to hello as it was when the kernel started, with the current screen, battery, appearance and locale where the host reads them.

Availability

In the appPermissionRemote kernelBrowser
yesnoneyesyes

A browser has no model or battery.

ts
const info = await device.info();
console.log(info.platform, info.osVersion, info.battery?.level);

device.has(method) ​

ts
has(method: string): boolean;

Whether the host answers a method. Synchronous: it reads what the host said when the kernel started, so it can't be cancelled.

ParameterTypeRequiredDescription
methodstringyesA method's name as SUPPORT lists it, such as "clipboard.readText" or "openURL".

Returns boolean: true when the host answers it; calling a method it doesn't fails unavailable.

ts
if (device.has("haptics.impact")) await device.haptics.impact();
else console.log("no haptics on", (await device.info()).platform);

device.on(event, handler, options?) ​

ts
on<E extends HostEvent>(event: E, handler: (value: HostEvents[E]) => void, options?: OnOptions): () => void;

Calls handler each time the device changes in the way event names, until it is removed: by calling the function on answers, by aborting options.signal, or by a kernel restart. Unlike other calls, an interrupt leaves it running, since it outlives the cell that added it. A handler that runs after its cell finished writes its output where the kernel sends output with no running cell, so keep handlers to updating state or a display by id. No permission; in the app only. When the host can't send events, the handler is removed and one line on stderr says why; has("on") tells beforehand.

ParameterTypeRequiredDescription
eventEyesWhat to watch: appearance, foreground, background, battery or locale.
handler(value: HostEvents[E]) => voidyesCalled with the event's value (HostEvents). An error it throws goes to stderr and doesn't remove it.
optionsOnOptionsnoThe signal that removes the handler.

Returns () => void: A function that removes the handler.

Throws DeviceError: failed when event isn't one of the five or handler isn't a function.

Availability

In the appPermissionRemote kernelBrowser
yesnonenono

Appearance, foreground, background, battery and locale changes; an interrupt doesn't remove a handler.

ts
const off = device.on("appearance", (mode) => console.log("now", mode));
// Later, in any cell:
off();

device.openURL(url, options?) ​

ts
openURL(url: string | URL, options?: OpenURLOptions): Promise<void>;

Opens a link: in the browser, or the mail app for mailto.

ParameterTypeRequiredDescription
urlstring | URLyesAn http, https or mailto URL.
optionsOpenURLOptionsnoinApp shows a web page in the app on iPhone and iPad, and the call's signal. Cancel rejects cancelled; the URL may already have opened.

Throws DeviceError: failed for any other scheme.

Availability

In the appPermissionRemote kernelBrowser
yesnoneyesafter a click

http, https and mailto only. inApp works on iPhone and iPad, not the Mac; a browser opens a tab after a click, and ignores inApp.

ts
await device.openURL("https://ganymede-server.com/docs/", { inApp: true });

device.share(options) ​

ts
share(options: ShareOptions): Promise<boolean>;

Shows the system share sheet (NSSharingServicePicker on the Mac). The sheet is its own consent, so no permission.

ParameterTypeRequiredDescription
optionsShareOptionsyestext, url and files to share (at least one; files together at most 16 MiB), and the call's signal, which closes the sheet and rejects cancelled.

Returns Promise<boolean>: true when the person picked where to send them (on iPhone and iPad once the share completes; on the Mac as soon as they pick a service), false when they dismissed it.

Throws DeviceError: failed when there is nothing to share, a file has no plain name, or the files are over 16 MiB.

Availability

In the appPermissionRemote kernelBrowser
yesnoneyesafter a click

The share sheet; NSSharingServicePicker on the Mac. A browser uses the Web Share API, after a click, and shares files only where it can.

ts
const csv = "city,temp\nOslo,4\nLima,19\n";
await device.share({
  text: "Today's temperatures",
  files: [{ name: "temps.csv", data: new TextEncoder().encode(csv) }],
});

device.preview(files, options?) ​

ts
preview(files: NamedBytes[], options?: CallOptions): Promise<void>;

Shows files in Quick Look. Quick Look picks the viewer from each name's extension, so name a PNG *.png.

ParameterTypeRequiredDescription
filesNamedBytes[]yesThe files, together at most 16 MiB.
optionsCallOptionsnoThe call's signal, which closes Quick Look and rejects cancelled.

Returns once the person closes it.

Throws DeviceError: failed when files is empty, a file has no plain name, or the files are over 16 MiB.

Availability

In the appPermissionRemote kernelBrowser
yesnoneyesno

Quick Look, which picks the viewer from each file name's extension. A browser has no Quick Look.

ts
const png = await device.clipboard.readImage();
if (png) await device.preview([{ name: "clipboard.png", data: png }]);

device.output(result, options?) ​

ts
output(result: RunOutput, options?: CallOptions): Promise<void>;

Hands result back to whatever started this run: the share sheet's host app, or the Shortcut's next action. A later call replaces it. In a notebook there is nothing to hand it to.

ParameterTypeRequiredDescription
resultRunOutputyestext, url and files (together at most 16 MiB).
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Throws DeviceError: unavailable in a notebook; failed when result has nothing in it or a file has no plain name.

Availability

In the appPermissionRemote kernelBrowser
nononenono

Only in a run the share sheet, Shortcuts or a widget started, which also has device.input; a notebook has nothing to hand a result to.

ts
const page = device.input?.url;
if (page) await device.output({ text: `Summary of ${page}: ...` });

Properties ​

PropertyTypeDescription
clipboardClipboardThe system clipboard: see Clipboard.
hapticsHapticsTaptic feedback, on iPhone: see Haptics.
uiUIDialogs over the notebook: see UI.
filesFilesThe system's file pickers: see Files.
photosPhotosThe system photo picker: see Photos.
soundSoundSound playback: see Sound.
notificationsNotificationsLocal notifications after a delay: see Notifications.
locationDeviceLocationWhere the device is, and geocoding: see DeviceLocation.
motionDeviceMotionThe motion sensors, on iPad and iPhone: see DeviceMotion.
contactsContactsThe person's contacts: see Contacts.
cameraCameraThe system camera: see Camera.
remindersRemindersThe person's reminders: see Reminders.
bluetoothBluetoothBluetooth LE peripherals: see Bluetooth.
foldersFoldersFolders the person picked for this notebook: see Folders.
widgetWidgetWidgets that show what a run published: see Widget.
inputRunInput | nullWhat this run was given, when the share sheet, Shortcuts or a widget started it; null in a notebook.
secretsSecretsStrings this notebook saved in the keychain: see Secrets.
shortcutsShortcutsThe person's Shortcuts, on iPad, iPhone and Mac: see Shortcuts.

device.clipboard ​

The system clipboard. iPadOS may show its paste prompt on a read.

device.clipboard.readText(options?) ​

ts
readText(options?: CallOptions): Promise<string | null>;

Reads the clipboard's text.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<string | null>: The text, or null when the clipboard holds none.

Availability

In the appPermissionRemote kernelBrowser
yesnoneyesafter a click

iPadOS shows its paste prompt; a browser needs a click for the Clipboard API.

ts
const text = await device.clipboard.readText();
if (text !== null) console.log(text.split(/\s+/).length, "words");

device.clipboard.writeText(text, options?) ​

ts
writeText(text: string, options?: CallOptions): Promise<void>;

Replaces the clipboard's contents with text.

ParameterTypeRequiredDescription
textstringyesThe text to copy.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled; the write may already have happened.

Availability

In the appPermissionRemote kernelBrowser
yesnoneyesafter a click

A browser needs a click for the Clipboard API.

ts
await device.clipboard.writeText(JSON.stringify({ rows: 3 }, null, 2));

device.clipboard.readImage(options?) ​

ts
readImage(options?: CallOptions): Promise<Uint8Array | null>;

Reads the clipboard's image as PNG bytes, whatever format was copied.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<Uint8Array | null>: PNG bytes, or null when the clipboard holds no image.

Availability

In the appPermissionRemote kernelBrowser
yesnoneyesafter a click

PNG. iPadOS shows its paste prompt; a browser needs a click for the Clipboard API.

ts
const png = await device.clipboard.readImage();
if (png) console.log(`${png.byteLength} bytes`);

device.clipboard.writeImage(image, options?) ​

ts
writeImage(image: Bytes, options?: CallOptions): Promise<void>;

Replaces the clipboard's contents with an image.

ParameterTypeRequiredDescription
imageBytesyesPNG, JPEG, GIF, HEIC or TIFF bytes, at most 16 MiB.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled; the write may already have happened.

Throws DeviceError: failed when the bytes aren't an image the device reads.

Availability

In the appPermissionRemote kernelBrowser
yesnoneyesafter a click

A browser needs a click for the Clipboard API, and takes PNG only.

ts
const [photo] = await device.photos.pick();
if (photo) await device.clipboard.writeImage(photo);

device.haptics ​

Taptic feedback. iPhone only: iPads and Macs have no Taptic Engine, so there device.has("haptics.impact") is false and calls fail unavailable.

device.haptics.impact(style?, options?) ​

ts
impact(style?: ImpactStyle, options?: CallOptions): Promise<void>;

Plays a tap.

ParameterTypeRequiredDescription
styleImpactStylenoHow strong a tap: "light", "medium" (the default), "heavy", "soft" or "rigid".
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled; the tap may already have played.

Availability

In the appPermissionRemote kernelBrowser
iPhonenoneyesno

iPhone only: iPads and Macs have no Taptic Engine. A browser has no haptics API Safari supports.

ts
if (device.has("haptics.impact")) await device.haptics.impact("heavy");

device.haptics.notification(type, options?) ​

ts
notification(type: NotificationFeedback, options?: CallOptions): Promise<void>;

Plays the system's success, warning or error pattern.

ParameterTypeRequiredDescription
typeNotificationFeedbackyes"success", "warning" or "error".
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Availability

In the appPermissionRemote kernelBrowser
iPhonenoneyesno

iPhone only: iPads and Macs have no Taptic Engine.

ts
await device.haptics.notification("success");

device.haptics.selection(options?) ​

ts
selection(options?: CallOptions): Promise<void>;

Plays the tick of a picker moving to a new value.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Availability

In the appPermissionRemote kernelBrowser
iPhonenoneyesno

iPhone only: iPads and Macs have no Taptic Engine.

ts
for (let i = 0; i < 3; i++) {
  await device.haptics.selection();
  await new Promise((done) => setTimeout(done, 200));
}

device.ui ​

Dialogs the app shows over the notebook: sheets on the Mac, alerts on iPhone and iPad. One shows at a time; a second call waits for the first to close. Dismissing one is not an error: it returns null or false. An interrupt or an aborted signal closes it and rejects cancelled (timeout for AbortSignal.timeout()).

device.ui.alert(message, options?) ​

ts
alert(message: string, options?: AlertOptions): Promise<void>;

Shows a message with an OK button.

ParameterTypeRequiredDescription
messagestringyesThe message.
optionsAlertOptionsnotitle is a heading above it; signal cancels: it closes the alert and rejects cancelled.

Returns once the person closes it.

Availability

In the appPermissionRemote kernelBrowser
yesnoneyesplanned

A sheet on the Mac, an alert on iPhone and iPad; a browser will show it in the cell's output.

ts
await device.ui.alert("The import finished: 1,204 rows.", { title: "Done" });

device.ui.confirm(message, options?) ​

ts
confirm(message: string, options?: ConfirmOptions): Promise<boolean>;

Asks a yes-or-no question.

ParameterTypeRequiredDescription
messagestringyesThe question.
optionsConfirmOptionsnotitle, okLabel for the confirming button, destructive to show it red, and the call's signal.

Returns Promise<boolean>: true when the person confirms, false when they cancel or dismiss it.

Availability

In the appPermissionRemote kernelBrowser
yesnoneyesplanned
ts
if (await device.ui.confirm("Delete the scratch files?", { destructive: true, okLabel: "Delete" })) {
  console.log("deleting");
}

device.ui.prompt(message, options?) ​

ts
prompt(message: string, options?: PromptOptions): Promise<string | null>;

Asks for a line of text.

ParameterTypeRequiredDescription
messagestringyesThe question.
optionsPromptOptionsnotitle, placeholder, defaultValue, secure to hide what is typed, and the call's signal.

Returns Promise<string | null>: The text entered, or null when the person cancels.

Availability

In the appPermissionRemote kernelBrowser
yesnoneyesplanned
ts
const name = await device.ui.prompt("Name the run", { defaultValue: "baseline" });
if (name !== null) console.log(`saving ${name}`);

device.ui.actionSheet(title, options, sheetOptions?) ​

ts
actionSheet(title: string, options: string[], sheetOptions?: ActionSheetOptions): Promise<number | null>;

Asks the person to choose one of several options.

ParameterTypeRequiredDescription
titlestringyesThe sheet's heading.
optionsstring[]yesThe choices' labels, at least one.
sheetOptionsActionSheetOptionsnomessage under the title, cancel for the cancel button's label, destructive for the index of an option to show red, and the call's signal.

Returns Promise<number | null>: The index of the option chosen, or null when the person cancels.

Throws DeviceError: failed when options is empty.

Availability

In the appPermissionRemote kernelBrowser
yesnoneyesplanned
ts
const formats = ["CSV", "JSON", "Parquet"];
const i = await device.ui.actionSheet("Export as", formats);
if (i !== null) console.log(`exporting ${formats[i]}`);

device.ui.pickDate(options?) ​

ts
pickDate(options?: PickDateOptions): Promise<Date | null>;

Asks for a date, a time, or both.

ParameterTypeRequiredDescription
optionsPickDateOptionsnomode ("date", "time" or "datetime", the default), title, value to start at, min and max, and the call's signal.

Returns Promise<Date | null>: The date chosen, or null when the person cancels.

Throws DeviceError: failed when value, min or max isn't a valid Date.

Availability

In the appPermissionRemote kernelBrowser
yesnoneyesplanned
ts
const start = await device.ui.pickDate({ mode: "date", title: "Report from", max: new Date() });
if (start) console.log(start.toISOString().slice(0, 10));

device.files ​

Files in and out through the system's own pickers, which are the person's consent, so no permission. A picked file is copied in by value: the notebook keeps no access to where it came from.

device.files.pick(options?) ​

ts
pick(options?: PickFilesOptions): Promise<File[]>;

Shows the open panel on the Mac, or the document picker on iPad and iPhone, and reads the files the person picks. Together they are at most 16 MiB.

ParameterTypeRequiredDescription
optionsPickFilesOptionsnotypes limits what can be picked, multiple allows more than one, and signal cancels: it closes the picker and rejects cancelled.

Returns Promise<File[]>: The picked files, with their names and MIME types, or [] when the person cancels.

Throws DeviceError: failed when types or multiple has the wrong type, or the files together are over 16 MiB.

Availability

In the appPermissionRemote kernelBrowser
yesnoneyesafter a click

The open panel on the Mac, the document picker on iPad and iPhone. Files are copied in; no access to where they came from is kept. A browser can't filter by a type identifier such as public.json, so with one it allows any file.

ts
const [file] = await device.files.pick({ types: [".csv", "text/csv"] });
if (file) {
  const rows = (await file.text()).trim().split("\n");
  console.log(file.name, rows.length, "rows");
}

device.files.export(data, options) ​

ts
export(data: Bytes, options: ExportFileOptions): Promise<boolean>;

Asks where to save data, and saves it there: the save panel on the Mac, the document picker on iPad and iPhone.

ParameterTypeRequiredDescription
dataBytesyesThe file's contents, at most 16 MiB.
optionsExportFileOptionsyesname is the file name the panel suggests; signal cancels: it closes the panel and rejects cancelled.

Returns Promise<boolean>: true when saved, false when the person cancels.

Throws DeviceError: failed when name isn't a plain file name, or data is over 16 MiB.

Availability

In the appPermissionRemote kernelBrowser
yesnoneyesafter a click

The save panel on the Mac, the document picker on iPad and iPhone. A browser downloads it after a click, and answers true then, since it can't tell whether it was saved.

ts
const csv = "city,temp\nOslo,4\nLima,19\n";
const saved = await device.files.export(new TextEncoder().encode(csv), { name: "temps.csv" });
console.log(saved ? "saved" : "cancelled");

device.photos ​

The photo library: the system photo picker to read photos, which needs no permission, and save to add one, which needs add-only access. The permission methods are about that add-only access: the first save asks (the app's prompt for this notebook, then the system's), and a refusal rejects denied. Granted, authorizationStatus() reads writeOnly: the app may add photos, never read the library.

The permission methods (authorizationStatus, isAuthorized, requestAuthorization) are about save's add-only access, and work where save does: in the app, not from a remote kernel or a browser.

device.photos.authorizationStatus(options?) ​

ts
authorizationStatus(options?: CallOptions): Promise<AuthorizationStatus>;

Reads the permission's status, without asking.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<AuthorizationStatus>: "notDetermined" until the person is asked, then a grant ("authorized", or a partial one) or "denied" or "restricted".

ts
console.log(await device.photos.authorizationStatus());

device.photos.isAuthorized(options?) ​

ts
isAuthorized(options?: CallOptions): Promise<boolean>;

Whether the permission is granted, without asking.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<boolean>: true when the status is a grant, partial ones included.

ts
if (!(await device.photos.isAuthorized())) console.log("not allowed yet");

device.photos.requestAuthorization(options?) ​

ts
requestAuthorization(options?: CallOptions): Promise<AuthorizationStatus>;

Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. The namespace's methods ask on their own, so calling this first is optional. Cancel rejects cancelled; a system prompt already up stays, and its answer still counts for later calls.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal.

Returns Promise<AuthorizationStatus>: The status after the prompt, or as it was when the person had already been asked.

ts
const status = await device.photos.requestAuthorization();
if (status === "denied") console.log("allow it in Settings to go on");

device.photos.pick(options?) ​

ts
pick(options?: PickPhotosOptions): Promise<File[]>;

Shows the system photo picker and reads the photos the person picks. The picker shows the library without the app being let in, so no permission. Together the photos are at most 16 MiB.

ParameterTypeRequiredDescription
optionsPickPhotosOptionsnolimit is how many may be picked (1 by default, 0 for no limit); signal cancels: it closes the picker and rejects cancelled.

Returns Promise<File[]>: The picked photos as image Files in their stored format (often HEIC or JPEG), or [] when the person cancels.

Throws DeviceError: failed when limit isn't a whole number of 0 or more, or the photos together are over 16 MiB.

Availability

In the appPermissionRemote kernelBrowser
yesnoneyesafter a click

The system photo picker, which needs no photo library permission. A browser shows a file picker for images, and keeps the first limit picked.

ts
const photos = await device.photos.pick({ limit: 3 });
for (const photo of photos) console.log(photo.name, photo.type, photo.size);

device.photos.save(image, options?) ​

ts
save(image: Bytes, options?: CallOptions): Promise<void>;

Adds an image to the photo library, as a new photo.

ParameterTypeRequiredDescription
imageBytesyesPNG, JPEG, GIF, HEIC or TIFF bytes, or a Blob of them, at most 16 MiB.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled; the photo may already have been added.

Throws DeviceError: denied when the notebook or the system may not add photos; failed when the bytes aren't PNG, JPEG, GIF, HEIC or TIFF, or the library refuses them.

Availability

In the appPermissionRemote kernelBrowser
yesphotosnono

Add-only access: the app may add photos, never read the library.

ts
const canvas = new OffscreenCanvas(256, 256);
const g = canvas.getContext("2d")!;
g.fillStyle = "teal";
g.fillRect(0, 0, 256, 256);
await device.photos.save(await canvas.convertToBlob({ type: "image/png" }));

device.sound ​

Sound from bytes in any format the device reads (WAV, AIFF, MP3, AAC/M4A, CAF, FLAC), at most 16 MiB. No permission. On iPhone the ring/silent switch can silence it.

device.sound.play(data, options?) ​

ts
play(data: Bytes, options?: PlaySoundOptions): Promise<void>;

Starts playing a sound, which plays to its end on its own.

ParameterTypeRequiredDescription
dataBytesyesThe sound's bytes, at most 16 MiB.
optionsPlaySoundOptionsnovolume from 0 to 1 (1 by default), and the call's signal. Cancel rejects cancelled; the sound may already be playing.

Returns once the sound has started.

Throws DeviceError: failed when data is empty, over 16 MiB, or not a sound the device reads.

Availability

In the appPermissionRemote kernelBrowser
yesnoneyesafter a click

A browser asks for a click only until the page has had one.

ts
const [file] = await device.files.pick({ types: ["audio/*"] });
if (file) await device.sound.play(file, { volume: 0.5 });

device.sound.player(data, options?) ​

ts
player(data: Bytes, options?: CallOptions): Promise<SoundPlayer>;

Loads a sound into a player, paused at the start, for a cell to play, pause, seek and close. A kernel has at most 32 open at once.

ParameterTypeRequiredDescription
dataBytesyesThe sound's bytes, at most 16 MiB.
optionsCallOptionsnoThe call's signal.

Returns Promise<SoundPlayer>: A SoundPlayer.

Throws DeviceError: failed when data is empty, over 16 MiB, or not a sound the device reads, or the kernel has 32 players open.

Availability

In the appPermissionRemote kernelBrowser
yesnonenono

Play, pause, stop, seek and volume; close it, or hold it with await using. Remote kernels and browsers: a handle wouldn't survive a disconnect.

ts
const [file] = await device.files.pick({ types: ["audio/*"] });
if (file) {
  await using player = await device.sound.player(file);
  await player.play();
  // Leaving the block closes the player, which stops it: wait for the end.
  await new Promise((done) => setTimeout(done, player.duration * 1000));
}

device.notifications ​

Local notifications after a delay: the app posts them even when the notebook or the app is closed. Delay only, no calendar times or repeats. Each notification belongs to the notebook that scheduled it: pending(), cancel() and cancelAll() see only that notebook's.

Needs the notifications permission, for schedule(): the first call asks (the app's prompt for this notebook, then the system's) and a refusal rejects denied. authorizationStatus(), isAuthorized() and requestAuthorization() read and ask for it.

The permission methods (authorizationStatus, isAuthorized, requestAuthorization) work where the rest of the namespace does: in the app, not from a remote kernel or a browser.

device.notifications.authorizationStatus(options?) ​

ts
authorizationStatus(options?: CallOptions): Promise<AuthorizationStatus>;

Reads the permission's status, without asking.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<AuthorizationStatus>: "notDetermined" until the person is asked, then a grant ("authorized", or a partial one) or "denied" or "restricted".

ts
console.log(await device.notifications.authorizationStatus());

device.notifications.isAuthorized(options?) ​

ts
isAuthorized(options?: CallOptions): Promise<boolean>;

Whether the permission is granted, without asking.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<boolean>: true when the status is a grant, partial ones included.

ts
if (!(await device.notifications.isAuthorized())) console.log("not allowed yet");

device.notifications.requestAuthorization(options?) ​

ts
requestAuthorization(options?: CallOptions): Promise<AuthorizationStatus>;

Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. The namespace's methods ask on their own, so calling this first is optional. Cancel rejects cancelled; a system prompt already up stays, and its answer still counts for later calls.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal.

Returns Promise<AuthorizationStatus>: The status after the prompt, or as it was when the person had already been asked.

ts
const status = await device.notifications.requestAuthorization();
if (status === "denied") console.log("allow it in Settings to go on");

device.notifications.schedule(body, options) ​

ts
schedule(body: string, options: ScheduleNotificationOptions): Promise<string>;

Schedules a notification to show after a delay, even if the notebook or the app has closed by then. iOS and iPadOS keep at most 64 per app, dropping the soonest beyond that.

ParameterTypeRequiredDescription
bodystringyesThe text it shows.
optionsScheduleNotificationOptionsyesdelay in seconds (more than 0), an optional title, and the call's signal. Cancel rejects cancelled; it may already be scheduled.

Returns Promise<string>: Its id, for cancel().

Throws DeviceError: denied when the notifications permission is refused; failed when body is empty, delay isn't a finite number above 0, or title isn't a string.

Availability

In the appPermissionRemote kernelBrowser
yesnotificationsnono

After delay seconds, even when the notebook is closed. iOS and iPadOS keep at most 64 per app.

ts
const id = await device.notifications.schedule("The training run should be done.", {
  title: "Check the notebook",
  delay: 30 * 60,
});

device.notifications.pending(options?) ​

ts
pending(options?: CallOptions): Promise<PendingNotification[]>;

Lists this notebook's notifications that haven't been delivered yet.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<PendingNotification[]>: This notebook's pending notifications; other notebooks' aren't listed.

Availability

In the appPermissionRemote kernelBrowser
yesnonenono

Only the calling notebook's.

ts
for (const n of await device.notifications.pending()) {
  console.log(n.id, n.body, n.date?.toLocaleTimeString());
}

device.notifications.cancel(id, options?) ​

ts
cancel(id: string, options?: CallOptions): Promise<void>;

Takes one of this notebook's notifications off the schedule. An id that's gone, or another notebook's, changes nothing.

ParameterTypeRequiredDescription
idstringyesWhat schedule() returned.
optionsCallOptionsnoThe call's signal.

Throws DeviceError: failed when id is empty.

Availability

In the appPermissionRemote kernelBrowser
yesnonenono

Only the calling notebook's.

ts
const id = await device.notifications.schedule("Stretch", { delay: 3600 });
await device.notifications.cancel(id);

device.notifications.cancelAll(options?) ​

ts
cancelAll(options?: CallOptions): Promise<void>;

Takes all of this notebook's notifications off the schedule.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal.

Availability

In the appPermissionRemote kernelBrowser
yesnonenono

Only the calling notebook's.

ts
await device.notifications.cancelAll();

device.location ​

Where the device is, over Core Location, and addresses to places and back. While in use only: the app asks for location when in use, never always, so nothing runs once the app quits.

current() and watch() need the location permission: the first call asks (the app's prompt for this notebook, then the system's) and a refusal rejects denied. With precise location turned off for the app, authorizationStatus() reads reducedAccuracy and fixes are within a few kilometres. Geocoding needs no permission but does need the network.

Over Core Location, while the app is in use only. current and watch need the location permission; geocoding needs the network but no permission. With precise location off for the app, authorizationStatus() reads reducedAccuracy and fixes are within a few kilometres. The permission methods work where the rest of the namespace does.

device.location.authorizationStatus(options?) ​

ts
authorizationStatus(options?: CallOptions): Promise<AuthorizationStatus>;

Reads the permission's status, without asking.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<AuthorizationStatus>: "notDetermined" until the person is asked, then a grant ("authorized", or a partial one) or "denied" or "restricted".

ts
console.log(await device.location.authorizationStatus());

device.location.isAuthorized(options?) ​

ts
isAuthorized(options?: CallOptions): Promise<boolean>;

Whether the permission is granted, without asking.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<boolean>: true when the status is a grant, partial ones included.

ts
if (!(await device.location.isAuthorized())) console.log("not allowed yet");

device.location.requestAuthorization(options?) ​

ts
requestAuthorization(options?: CallOptions): Promise<AuthorizationStatus>;

Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. The namespace's methods ask on their own, so calling this first is optional. Cancel rejects cancelled; a system prompt already up stays, and its answer still counts for later calls.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal.

Returns Promise<AuthorizationStatus>: The status after the prompt, or as it was when the person had already been asked.

ts
const status = await device.location.requestAuthorization();
if (status === "denied") console.log("allow it in Settings to go on");

device.location.current(options?) ​

ts
current(options?: LocationOptions): Promise<LocationFix>;

One fix, at the accuracy asked for.

ParameterTypeRequiredDescription
optionsLocationOptionsnoaccuracy: "best" (the default), "block" or "city"; and the call's signal. Cancel stops the search and rejects cancelled.

Returns Promise<LocationFix>: The fix: latitude, longitude, its accuracy in metres, and altitude, speed and course when known.

Throws DeviceError: denied when the notebook or the system may not use location, or Location Services is off; failed when no fix can be had (no signal) or accuracy isn't one of the three.

Availability

In the appPermissionRemote kernelBrowser
yeslocationnono

While the app is in use only. With precise location off, within a few kilometres.

ts
const fix = await device.location.current({ accuracy: "block" });
console.log(fix.latitude, fix.longitude, `±${fix.accuracy} m`);

device.location.watch(options?) ​

ts
watch(options?: LocationOptions): DeviceStream<LocationFix>;

Fixes as the device moves, for for await, until the loop ends or the cell is interrupted. Keeps the newest 16 when the cell reads slower than they come (dropped counts the rest).

ParameterTypeRequiredDescription
optionsLocationOptionsnoaccuracy, as for current(); and the call's signal, which ends the stream.

Returns DeviceStream<LocationFix>: A stream of fixes; close() or leaving the loop stops Core Location.

Throws DeviceError: denied when the notebook or the system may not use location, or Location Services is off; failed when accuracy isn't one of the three, or when Core Location stops sending fixes.

Availability

In the appPermissionRemote kernelBrowser
yeslocationnono

Until the loop ends or the cell is interrupted; keeps the newest 16.

ts
for await (const fix of device.location.watch({ accuracy: "block" })) {
  console.log(fix.latitude, fix.longitude);
  if (fix.speed === 0) break;
}

device.location.geocode(address, options?) ​

ts
geocode(address: string, options?: CallOptions): Promise<Place[]>;

The places an address may be. Needs the network, not the permission.

ParameterTypeRequiredDescription
addressstringyesA street address, city or landmark, as a person would type it.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<Place[]>: The places, best match first, or [] when none.

Throws DeviceError: failed when address is empty or the geocoder can't be reached.

Availability

In the appPermissionRemote kernelBrowser
yesnonenono

Needs the network, not the location permission.

ts
const [place] = await device.location.geocode("1 Infinite Loop, Cupertino");
if (place) console.log(place.latitude, place.longitude);

device.location.reverseGeocode(position, options?) ​

ts
reverseGeocode(position: { latitude: number; longitude: number }, options?: CallOptions): Promise<Place[]>;

What is at a position, such as a fix from current(). Needs the network, not the permission.

ParameterTypeRequiredDescription
position{ latitude: number; longitude: number }yeslatitude (-90 to 90) and longitude (-180 to 180), in degrees.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<Place[]>: The places there, or [] when nothing.

Throws DeviceError: failed when the position is out of range or the geocoder can't be reached.

Availability

In the appPermissionRemote kernelBrowser
yesnonenono

Needs the network, not the location permission.

ts
const [here] = await device.location.reverseGeocode(await device.location.current());
console.log(here?.city);

device.motion ​

The device's motion sensors over Core Motion: attitude, rotation rate, gravity and the person's acceleration. iPad and iPhone only; a Mac has no motion sensors, so has("motion.watch") is false there.

Two ways to read, sharing one source: watch() for a sample every interval, as when recording, and latest() for the newest sample, as in a polling loop. Both need the motion permission: the app asks for this notebook (the system has no prompt for these sensors) and a refusal rejects denied.

Over Core Motion, on iPad and iPhone; a Mac has no motion sensors. watch and latest share one source. The system has no prompt for these sensors, so the permission is the app's own, per notebook. The permission methods work where the rest of the namespace does.

device.motion.authorizationStatus(options?) ​

ts
authorizationStatus(options?: CallOptions): Promise<AuthorizationStatus>;

Reads the permission's status, without asking.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<AuthorizationStatus>: "notDetermined" until the person is asked, then a grant ("authorized", or a partial one) or "denied" or "restricted".

ts
console.log(await device.motion.authorizationStatus());

device.motion.isAuthorized(options?) ​

ts
isAuthorized(options?: CallOptions): Promise<boolean>;

Whether the permission is granted, without asking.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<boolean>: true when the status is a grant, partial ones included.

ts
if (!(await device.motion.isAuthorized())) console.log("not allowed yet");

device.motion.requestAuthorization(options?) ​

ts
requestAuthorization(options?: CallOptions): Promise<AuthorizationStatus>;

Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. The namespace's methods ask on their own, so calling this first is optional. Cancel rejects cancelled; a system prompt already up stays, and its answer still counts for later calls.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal.

Returns Promise<AuthorizationStatus>: The status after the prompt, or as it was when the person had already been asked.

ts
const status = await device.motion.requestAuthorization();
if (status === "denied") console.log("allow it in Settings to go on");

device.motion.watch(options?) ​

ts
watch(options?: MotionWatchOptions): DeviceStream<MotionSample>;

Samples every interval seconds, for for await, until the loop ends or the cell is interrupted. Keeps the newest 64 when the cell reads slower than they come (dropped counts the rest).

ParameterTypeRequiredDescription
optionsMotionWatchOptionsnointerval in seconds, 0.01 to 1 (0.1 by default); and the call's signal, which ends the stream.

Returns DeviceStream<MotionSample>: A stream of samples; close() or leaving the loop stops the sensors unless something else still reads them.

Throws DeviceError: denied when the notebook may not use motion; failed when interval is out of range, or Core Motion stops with an error.

Availability

In the appPermissionRemote kernelBrowser
iPad, iPhonemotionnono

Attitude, rotation rate, gravity and acceleration every interval seconds (0.01 to 1); keeps the newest 64. A Mac has no motion sensors.

ts
const samples = [];
for await (const s of device.motion.watch({ interval: 0.05 })) {
  samples.push(s.userAcceleration);
  if (samples.length === 100) break;
}

device.motion.latest(options?) ​

ts
latest(options?: CallOptions): Promise<MotionSample>;

The newest sample. The first call starts the sensors and waits for a reading; they keep running while calls keep coming, and stop about 10 seconds after the last.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<MotionSample>: The newest sample.

Throws DeviceError: denied when the notebook may not use motion; failed when Core Motion stops with an error.

Availability

In the appPermissionRemote kernelBrowser
iPad, iPhonemotionnono

Starts the sensors and keeps them running while calls keep coming; they stop about 10 seconds after the last.

ts
const { attitude } = await device.motion.latest();
console.log(`tilted ${((attitude.pitch * 180) / Math.PI).toFixed(0)}°`);

device.contacts ​

The person's contacts, over the Contacts framework. Every method needs the contacts permission: the first call asks (the app's prompt for this notebook, then the system's), and a refusal rejects denied. With limited access (iPhone and iPad), authorizationStatus() reads limited and the methods see only the contacts the person shared with the app.

Over the Contacts framework, on the Mac, iPad and iPhone. Every method needs the contacts permission. Contacts are plain records (Contact); groups, accounts, photos and notes aren't wrapped. The permission methods work where the rest of the namespace does.

device.contacts.authorizationStatus(options?) ​

ts
authorizationStatus(options?: CallOptions): Promise<AuthorizationStatus>;

Reads the permission's status, without asking.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<AuthorizationStatus>: "notDetermined" until the person is asked, then a grant ("authorized", or a partial one) or "denied" or "restricted".

ts
console.log(await device.contacts.authorizationStatus());

device.contacts.isAuthorized(options?) ​

ts
isAuthorized(options?: CallOptions): Promise<boolean>;

Whether the permission is granted, without asking.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<boolean>: true when the status is a grant, partial ones included.

ts
if (!(await device.contacts.isAuthorized())) console.log("not allowed yet");

device.contacts.requestAuthorization(options?) ​

ts
requestAuthorization(options?: CallOptions): Promise<AuthorizationStatus>;

Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. The namespace's methods ask on their own, so calling this first is optional. Cancel rejects cancelled; a system prompt already up stays, and its answer still counts for later calls.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal.

Returns Promise<AuthorizationStatus>: The status after the prompt, or as it was when the person had already been asked.

ts
const status = await device.contacts.requestAuthorization();
if (status === "denied") console.log("allow it in Settings to go on");

device.contacts.search(query, options?) ​

ts
search(query: string, options?: CallOptions): Promise<Contact[]>;

Contacts whose name matches query; or whose email matches, when query is a whole email address (a part of one, such as "@example.com", matches nothing); or whose phone number matches, when it is a phone number.

ParameterTypeRequiredDescription
querystringyesA name or part of one, an email address, or a phone number.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<Contact[]>: The matching contacts, or [].

Throws DeviceError: denied when the notebook or the system may not read contacts; failed when query is empty or the store can't be read.

Availability

In the appPermissionRemote kernelBrowser
yescontactsnono

By name, or by email or phone number when the query is one. With limited access, only the contacts shared with the app.

ts
for (const contact of await device.contacts.search("Ada")) {
  console.log(contact.givenName, contact.familyName, contact.emails?.[0]?.value);
}

device.contacts.get(id, options?) ​

ts
get(id: string, options?: CallOptions): Promise<Contact | null>;

One contact, by the id search, all or add gave.

ParameterTypeRequiredDescription
idstringyesThe contact's id.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<Contact | null>: The contact, or null when there is none with that id (or it isn't shared with the app).

Throws DeviceError: denied when the notebook or the system may not read contacts; failed when id is empty or the store can't be read.

Availability

In the appPermissionRemote kernelBrowser
yescontactsnono
ts
const [ada] = await device.contacts.search("Ada");
if (ada) console.log(await device.contacts.get(ada.id));

device.contacts.all(options?) ​

ts
all(options?: CallOptions): Promise<Contact[]>;

Every contact the app may see, in the person's sort order.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<Contact[]>: The contacts, or [].

Throws DeviceError: denied when the notebook or the system may not read contacts; failed when the store can't be read.

Availability

In the appPermissionRemote kernelBrowser
yescontactsnono
ts
const all = await device.contacts.all();
console.log(`${all.length} contacts`);

device.contacts.add(contact, options?) ​

ts
add(contact: Omit<Contact, "id">, options?: CallOptions): Promise<string>;

Adds a contact to the default account. Every field is optional, but it needs a name, an organization, an email or a phone number.

ParameterTypeRequiredDescription
contactOmit<Contact, "id">yesThe contact, without an id.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled; a contact already saved stays.

Returns Promise<string>: The new contact's id.

Throws DeviceError: denied when the notebook or the system may not change contacts; failed when the contact is empty, a field has the wrong type, birthday isn't YYYY-MM-DD or --MM-DD, or the store refuses it.

Availability

In the appPermissionRemote kernelBrowser
yescontactsnono

To the default account; groups and accounts aren't wrapped.

ts
const id = await device.contacts.add({
  givenName: "Ada",
  familyName: "Lovelace",
  emails: [{ label: "work", value: "ada@example.com" }],
  birthday: "1815-12-10",
});

device.contacts.delete(id, options?) ​

ts
delete(id: string, options?: CallOptions): Promise<boolean>;

Deletes a contact.

ParameterTypeRequiredDescription
idstringyesThe contact's id.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled; a contact already deleted stays deleted.

Returns Promise<boolean>: true, or false when there was no contact with that id.

Throws DeviceError: denied when the notebook or the system may not change contacts; failed when id is empty or the store refuses.

Availability

In the appPermissionRemote kernelBrowser
yescontactsnono
ts
const id = await device.contacts.add({ givenName: "Test" });
console.log(await device.contacts.delete(id)); // true

device.camera ​

The camera, through the system camera screen.

The system camera screen, on iPad and iPhone. capture needs the camera permission. The permission methods work where the rest of the namespace does.

device.camera.authorizationStatus(options?) ​

ts
authorizationStatus(options?: CallOptions): Promise<AuthorizationStatus>;

Reads the permission's status, without asking.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<AuthorizationStatus>: "notDetermined" until the person is asked, then a grant ("authorized", or a partial one) or "denied" or "restricted".

ts
console.log(await device.camera.authorizationStatus());

device.camera.isAuthorized(options?) ​

ts
isAuthorized(options?: CallOptions): Promise<boolean>;

Whether the permission is granted, without asking.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<boolean>: true when the status is a grant, partial ones included.

ts
if (!(await device.camera.isAuthorized())) console.log("not allowed yet");

device.camera.requestAuthorization(options?) ​

ts
requestAuthorization(options?: CallOptions): Promise<AuthorizationStatus>;

Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. The namespace's methods ask on their own, so calling this first is optional. Cancel rejects cancelled; a system prompt already up stays, and its answer still counts for later calls.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal.

Returns Promise<AuthorizationStatus>: The status after the prompt, or as it was when the person had already been asked.

ts
const status = await device.camera.requestAuthorization();
if (status === "denied") console.log("allow it in Settings to go on");

device.camera.capture(options?) ​

ts
capture(options?: CallOptions): Promise<Uint8Array | null>;

Shows the system camera and waits for the person to take a photo. Needs the camera permission; the first call asks for it.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal, which closes the camera and rejects cancelled. An interrupt does the same.

Returns Promise<Uint8Array | null>: The photo as JPEG bytes, or null when the person cancels.

Throws DeviceError: denied when the notebook or the system may not use the camera; failed when the photo can't be read or is over 16 MiB.

Availability

In the appPermissionRemote kernelBrowser
iPad, iPhonecameranono

The system camera screen. A Mac has none in the app; a browser would need its own camera view.

ts
const jpeg = await device.camera.capture();
if (jpeg) await device.preview([{ name: "photo.jpg", data: jpeg }]);

device.reminders ​

The person's reminders, over EventKit. Every method needs the reminders permission: the first call asks (the app's prompt for this notebook, then the system's), and a refusal rejects denied. No recurrence, alarms or subtasks.

Over EventKit, on the Mac, iPad and iPhone. Every method needs the reminders permission. Reminders are plain records (Reminder); recurrence, alarms and subtasks aren't wrapped. The permission methods work where the rest of the namespace does.

device.reminders.authorizationStatus(options?) ​

ts
authorizationStatus(options?: CallOptions): Promise<AuthorizationStatus>;

Reads the permission's status, without asking.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<AuthorizationStatus>: "notDetermined" until the person is asked, then a grant ("authorized", or a partial one) or "denied" or "restricted".

ts
console.log(await device.reminders.authorizationStatus());

device.reminders.isAuthorized(options?) ​

ts
isAuthorized(options?: CallOptions): Promise<boolean>;

Whether the permission is granted, without asking.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<boolean>: true when the status is a grant, partial ones included.

ts
if (!(await device.reminders.isAuthorized())) console.log("not allowed yet");

device.reminders.requestAuthorization(options?) ​

ts
requestAuthorization(options?: CallOptions): Promise<AuthorizationStatus>;

Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. The namespace's methods ask on their own, so calling this first is optional. Cancel rejects cancelled; a system prompt already up stays, and its answer still counts for later calls.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal.

Returns Promise<AuthorizationStatus>: The status after the prompt, or as it was when the person had already been asked.

ts
const status = await device.reminders.requestAuthorization();
if (status === "denied") console.log("allow it in Settings to go on");

device.reminders.calendars(options?) ​

ts
calendars(options?: CallOptions): Promise<ReminderCalendar[]>;

The reminder lists.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<ReminderCalendar[]>: The lists, the default one marked isDefault.

Throws DeviceError: denied when the notebook or the system may not read reminders.

Availability

In the appPermissionRemote kernelBrowser
yesremindersnono
ts
for (const list of await device.reminders.calendars()) console.log(list.title, list.isDefault);

device.reminders.list(options?) ​

ts
list(options?: ListRemindersOptions): Promise<Reminder[]>;

Reminders, from one list or all of them.

ParameterTypeRequiredDescription
optionsListRemindersOptionsnocalendar, a list's id; completed, to see only completed or only incomplete ones; and the call's signal.

Returns Promise<Reminder[]>: The reminders, or [].

Throws DeviceError: denied when the notebook or the system may not read reminders; failed when calendar isn't a reminder list.

Availability

In the appPermissionRemote kernelBrowser
yesremindersnono
ts
const open = await device.reminders.list({ completed: false });
for (const r of open) console.log(r.title, r.due ?? "");

device.reminders.add(reminder, options?) ​

ts
add(reminder: NewReminder, options?: CallOptions): Promise<string>;

Adds a reminder.

ParameterTypeRequiredDescription
reminderNewReminderyesA title, and optional notes, due, priority and calendar.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled; a reminder already saved stays.

Returns Promise<string>: The new reminder's id.

Throws DeviceError: denied when the notebook or the system may not add reminders; failed when the title is empty, due isn't a date, priority isn't 0 to 9, or calendar isn't a list that can be added to.

Availability

In the appPermissionRemote kernelBrowser
yesremindersnono

No recurrence or alarms.

ts
const id = await device.reminders.add({ title: "Buy milk", due: "2026-11-10", priority: 1 });

device.reminders.complete(id, options?) ​

ts
complete(id: string, options?: CallOptions): Promise<boolean>;

Marks a reminder done.

ParameterTypeRequiredDescription
idstringyesThe reminder's id.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<boolean>: true, or false when there is no reminder with that id.

Throws DeviceError: denied when the notebook or the system may not read reminders; failed when id is empty or the store refuses.

Availability

In the appPermissionRemote kernelBrowser
yesremindersnono
ts
const [first] = await device.reminders.list({ completed: false });
if (first) await device.reminders.complete(first.id);

device.reminders.uncomplete(id, options?) ​

ts
uncomplete(id: string, options?: CallOptions): Promise<boolean>;

Marks a reminder not done.

ParameterTypeRequiredDescription
idstringyesThe reminder's id.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<boolean>: true, or false when there is no reminder with that id.

Throws DeviceError: denied when the notebook or the system may not read reminders; failed when id is empty or the store refuses.

Availability

In the appPermissionRemote kernelBrowser
yesremindersnono
ts
const [last] = await device.reminders.list({ completed: true });
if (last) await device.reminders.uncomplete(last.id);

device.reminders.delete(id, options?) ​

ts
delete(id: string, options?: CallOptions): Promise<boolean>;

Deletes a reminder.

ParameterTypeRequiredDescription
idstringyesThe reminder's id.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled; a reminder already deleted stays deleted.

Returns Promise<boolean>: true, or false when there is no reminder with that id.

Throws DeviceError: denied when the notebook or the system may not read reminders; failed when id is empty or the store refuses.

Availability

In the appPermissionRemote kernelBrowser
yesremindersnono
ts
const id = await device.reminders.add({ title: "Test" });
console.log(await device.reminders.delete(id)); // true

device.bluetooth ​

A Bluetooth LE central, over Core Bluetooth: scan for peripherals, connect, and read, write and subscribe to their characteristics. On a Mac and an iPad or iPhone, in kernels in the app.

scan(), known() and connect() need the bluetooth permission: the first call asks (the app's prompt for this notebook, then the system's) and a refusal rejects denied. They reject unavailable while Bluetooth is off.

A Bluetooth LE central over Core Bluetooth, on the Mac, iPad and iPhone. scan, known and connect need the bluetooth permission and reject unavailable while Bluetooth is off. A connected peripheral is a Peripheral. The permission methods work where the rest of the namespace does.

device.bluetooth.authorizationStatus(options?) ​

ts
authorizationStatus(options?: CallOptions): Promise<AuthorizationStatus>;

Reads the permission's status, without asking.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<AuthorizationStatus>: "notDetermined" until the person is asked, then a grant ("authorized", or a partial one) or "denied" or "restricted".

ts
console.log(await device.bluetooth.authorizationStatus());

device.bluetooth.isAuthorized(options?) ​

ts
isAuthorized(options?: CallOptions): Promise<boolean>;

Whether the permission is granted, without asking.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<boolean>: true when the status is a grant, partial ones included.

ts
if (!(await device.bluetooth.isAuthorized())) console.log("not allowed yet");

device.bluetooth.requestAuthorization(options?) ​

ts
requestAuthorization(options?: CallOptions): Promise<AuthorizationStatus>;

Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. The namespace's methods ask on their own, so calling this first is optional. Cancel rejects cancelled; a system prompt already up stays, and its answer still counts for later calls.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal.

Returns Promise<AuthorizationStatus>: The status after the prompt, or as it was when the person had already been asked.

ts
const status = await device.bluetooth.requestAuthorization();
if (status === "denied") console.log("allow it in Settings to go on");

device.bluetooth.scan(options?) ​

ts
scan(options?: ScanOptions): DeviceStream<Advertisement>;

Advertisements from nearby peripherals, for for await, until the loop ends or the cell is interrupted. Keeps the newest 128 when the cell reads slower than they come.

ParameterTypeRequiredDescription
optionsScanOptionsnoservices to filter by, allowDuplicates, and the call's signal, which ends the stream.

Returns DeviceStream<Advertisement>: A stream of advertisements; leaving the loop stops scanning.

Throws DeviceError: denied when the notebook or the system may not use Bluetooth; unavailable when Bluetooth is off; failed when a service isn't a UUID.

Availability

In the appPermissionRemote kernelBrowser
yesbluetoothnono

Bluetooth LE only. A browser would need Web Bluetooth, which Safari doesn't have.

ts
for await (const ad of device.bluetooth.scan({ services: ["180D"] })) {
  console.log(ad.name, ad.rssi);
  break;
}

device.bluetooth.known(ids, options?) ​

ts
known(ids: string[], options?: CallOptions): Promise<KnownPeripheral[]>;

Peripherals the system knows by id, such as one a past scan found, to connect again without scanning.

ParameterTypeRequiredDescription
idsstring[]yesPeripheral ids from scan() on this device, at most 64.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<KnownPeripheral[]>: The ones it knows; the others are left out.

Throws DeviceError: denied when the notebook or the system may not use Bluetooth; unavailable when Bluetooth is off; failed when an id isn't a UUID.

Availability

In the appPermissionRemote kernelBrowser
yesbluetoothnono
ts
const [strap] = await device.bluetooth.known(["6E400001-B5A3-F393-E0A9-E50E24DCCA9E"]);
if (strap) {
  await using peripheral = await device.bluetooth.connect(strap.id);
  console.log(await peripheral.rssi());
}

device.bluetooth.connect(id, options?) ​

ts
connect(id: string, options?: ConnectOptions): Promise<Peripheral>;

Connects to a peripheral.

ParameterTypeRequiredDescription
idstringyesIts id, from scan() or known().
optionsConnectOptionsnotimeout in seconds (15 by default), and the call's signal. Cancel stops connecting and rejects cancelled.

Returns Promise<Peripheral>: The connected peripheral, a handle to close when done.

Throws DeviceError: denied when the notebook or the system may not use Bluetooth; unavailable when Bluetooth is off; timeout when it doesn't answer in time; failed when it is unknown, already connected, or the kernel has 8 connected.

Availability

In the appPermissionRemote kernelBrowser
yesbluetoothnono

Services, read, write, notify and RSSI; close it, or hold it with await using. At most 8 per kernel.

ts
for await (const ad of device.bluetooth.scan({ services: ["180D"] })) {
  await using strap = await device.bluetooth.connect(ad.id);
  console.log(await strap.services());
  break;
}

device.folders ​

Folders the person picked, for lasting access from this notebook: the kernel has no file system of its own. The picker is the consent, so there is no permission prompt; Settings lists each notebook's folders and revokes them. Works in kernels in the app only.

Folders the person picked, kept per notebook as bookmarks, on the Mac, iPad and iPhone. No permission: the picker is the consent, and Settings revokes. A picked folder is a Folder.

device.folders.pick(options?) ​

ts
pick(options?: PickFolderOptions): Promise<Folder | null>;

Shows the system folder picker and keeps what the person picks for this notebook. Picking a folder it already has replaces that grant, keeping its id.

ParameterTypeRequiredDescription
optionsPickFolderOptionsnowrite to ask for writing too, and the call's signal, which closes the picker and rejects cancelled.

Returns Promise<Folder | null>: The folder, or null when the person cancelled.

Throws DeviceError: denied when the app doesn't know which notebook this is; failed when access to the folder can't be kept.

Availability

In the appPermissionRemote kernelBrowser
yesnonenono

The system folder picker, which is the consent; the folder stays the notebook's until revoked in Settings. A browser would need the File System Access API, which Safari doesn't have.

ts
const folder = await device.folders.pick();
if (folder) console.log(folder.name, (await folder.entries()).length);

device.folders.list(options?) ​

ts
list(options?: CallOptions): Promise<Folder[]>;

The folders this notebook was given, in the order they were picked.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<Folder[]>: Its folders, or [].

Throws DeviceError: denied when the app doesn't know which notebook this is.

Availability

In the appPermissionRemote kernelBrowser
yesnonenono

A Folder lists, reads, writes and removes inside the folder only; reads up to 16 MiB.

ts
const folders = await device.folders.list();
const data = folders.find((f) => f.name === "Data") ?? (await device.folders.pick());

device.widget ​

Widgets on the home screen, desktop or lock screen. A widget can't run a script itself, so it shows what a run last published: from a notebook or a headless run. No permission.

Widgets show what a run last published, since a widget can't run a script itself. The content is a small layout (WidgetContent): a title, up to six rows, a tint and a sparkline. No permission.

device.widget.update(kind, content, options?) ​

ts
update(kind: string, content: WidgetContent, options?: CallOptions): Promise<void>;

Publishes content for this notebook's widget kind, in place of what it showed, and asks the system to redraw it.

ParameterTypeRequiredDescription
kindstringyesA name for the widget, 1 to 64 letters, digits, ., _ or -; a widget configured for this notebook and kind shows it.
contentWidgetContentyesThe layout: title, rows, tint, sparkline.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled; content already published stays.

Throws DeviceError: failed when kind or content isn't valid; denied when the app hasn't said which notebook runs.

Availability

In the appPermissionRemote kernelBrowser
yesnonenono

Stored in the app's App Group for its widgets; a widget can't run a script itself.

ts
await device.widget.update("weather", {
  title: "Oslo",
  rows: [{ label: "Now", value: 4, unit: "°C" }, { text: "Rain at 15:00" }],
  tint: "blue",
  sparkline: [3, 4, 4, 5, 6, 5],
});

device.shortcuts ​

The person's Shortcuts, run from a cell. The Shortcuts app comes forward while one runs, and Ganymede comes back when it is done. iPad and iPhone stop the app's kernel about 30 seconds after the app leaves the screen, so keep these Shortcuts short. Every call needs the shortcuts permission, which is the app's prompt alone: a Shortcut can do whatever the person set it up to.

The person's Shortcuts, run from a cell through the Shortcuts app, which comes forward while one runs. Every call needs the shortcuts permission, the app's prompt alone. The permission methods work where run does.

device.shortcuts.authorizationStatus(options?) ​

ts
authorizationStatus(options?: CallOptions): Promise<AuthorizationStatus>;

Reads the permission's status, without asking.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<AuthorizationStatus>: "notDetermined" until the person is asked, then a grant ("authorized", or a partial one) or "denied" or "restricted".

ts
console.log(await device.shortcuts.authorizationStatus());

device.shortcuts.isAuthorized(options?) ​

ts
isAuthorized(options?: CallOptions): Promise<boolean>;

Whether the permission is granted, without asking.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<boolean>: true when the status is a grant, partial ones included.

ts
if (!(await device.shortcuts.isAuthorized())) console.log("not allowed yet");

device.shortcuts.requestAuthorization(options?) ​

ts
requestAuthorization(options?: CallOptions): Promise<AuthorizationStatus>;

Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. The namespace's methods ask on their own, so calling this first is optional. Cancel rejects cancelled; a system prompt already up stays, and its answer still counts for later calls.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal.

Returns Promise<AuthorizationStatus>: The status after the prompt, or as it was when the person had already been asked.

ts
const status = await device.shortcuts.requestAuthorization();
if (status === "denied") console.log("allow it in Settings to go on");

device.shortcuts.run(name, options?) ​

ts
run(name: string, options?: RunShortcutOptions): Promise<string | null>;

Runs a Shortcut by name and waits for it to finish.

ParameterTypeRequiredDescription
namestringyesThe Shortcut's name as the Shortcuts app shows it.
optionsRunShortcutOptionsnoinput, text for the Shortcut, and the call's signal. Cancel (an interrupt) stops waiting and rejects cancelled; the Shortcut may still run to its end.

Returns Promise<string | null>: The Shortcut's output as text, or null when it gave none.

Throws DeviceError: cancelled when the Shortcut was cancelled; failed with the Shortcuts app's message when it failed or there is no Shortcut by that name, or when name or input isn't valid; denied when the notebook may not run Shortcuts; unavailable in a headless run.

Availability

In the appPermissionRemote kernelBrowser
yesshortcutsnono

Through the Shortcuts app, which comes forward while it runs. On iPad and iPhone the kernel stops about 30 seconds after the app leaves the screen, so keep the Shortcut short.

ts
const reply = await device.shortcuts.run("Make Uppercase", { input: "hi" });
console.log(reply); // "HI"

device.secrets ​

Strings this notebook saved, such as an API token, kept in the system keychain on this device and never synced. Each notebook sees only its own names; a renamed or moved notebook keeps them. There is no system permission: the first call asks the app's prompt for this notebook, and Settings lists and deletes each notebook's secrets.

Strings a notebook keeps in the system keychain, on this device only and per notebook. Every method needs the secrets permission, which is the app's prompt alone: there is no system one. The permission methods work where the rest of the namespace does.

device.secrets.authorizationStatus(options?) ​

ts
authorizationStatus(options?: CallOptions): Promise<AuthorizationStatus>;

Reads the permission's status, without asking.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<AuthorizationStatus>: "notDetermined" until the person is asked, then a grant ("authorized", or a partial one) or "denied" or "restricted".

ts
console.log(await device.secrets.authorizationStatus());

device.secrets.isAuthorized(options?) ​

ts
isAuthorized(options?: CallOptions): Promise<boolean>;

Whether the permission is granted, without asking.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<boolean>: true when the status is a grant, partial ones included.

ts
if (!(await device.secrets.isAuthorized())) console.log("not allowed yet");

device.secrets.requestAuthorization(options?) ​

ts
requestAuthorization(options?: CallOptions): Promise<AuthorizationStatus>;

Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. The namespace's methods ask on their own, so calling this first is optional. Cancel rejects cancelled; a system prompt already up stays, and its answer still counts for later calls.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal.

Returns Promise<AuthorizationStatus>: The status after the prompt, or as it was when the person had already been asked.

ts
const status = await device.secrets.requestAuthorization();
if (status === "denied") console.log("allow it in Settings to go on");

device.secrets.get(name, options?) ​

ts
get(name: string, options?: CallOptions): Promise<string | null>;

Reads a secret.

ParameterTypeRequiredDescription
namestringyesThe secret's name: 1 to 128 characters, no NUL.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<string | null>: The value, or null when this notebook has no secret by that name.

Throws DeviceError: denied when the notebook may not use secrets; unavailable before the device's first unlock since it started; failed when the name isn't valid.

Availability

In the appPermissionRemote kernelBrowser
yessecretsnono

The keychain, this device only. A notebook sees only its own secrets; the app's prompt is the only consent.

ts
const token = await device.secrets.get("github");
if (!token) console.log("set it first: await device.secrets.set('github', '...')");

device.secrets.set(name, value, options?) ​

ts
set(name: string, value: string, options?: CallOptions): Promise<void>;

Saves a secret, in place of one by the same name.

ParameterTypeRequiredDescription
namestringyesThe secret's name: 1 to 128 characters, no NUL.
valuestringyesThe value: a string up to 64 KiB as UTF-8.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled; a value already saved stays.

Throws DeviceError: denied when the notebook may not use secrets; unavailable before the device's first unlock since it started; failed when the name or value isn't valid.

Availability

In the appPermissionRemote kernelBrowser
yessecretsnono

Values up to 64 KiB; names 1 to 128 characters.

ts
const token = await device.ui.prompt("GitHub token");
if (token) await device.secrets.set("github", token);

device.secrets.delete(name, options?) ​

ts
delete(name: string, options?: CallOptions): Promise<boolean>;

Deletes a secret.

ParameterTypeRequiredDescription
namestringyesThe secret's name.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled; a secret already deleted stays deleted.

Returns Promise<boolean>: true, or false when there was no secret by that name.

Throws DeviceError: denied when the notebook may not use secrets; unavailable before the device's first unlock since it started; failed when the name isn't valid.

Availability

In the appPermissionRemote kernelBrowser
yessecretsnono
ts
console.log(await device.secrets.delete("github"));

device.secrets.list(options?) ​

ts
list(options?: CallOptions): Promise<string[]>;

The names of this notebook's secrets, never their values.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<string[]>: The names, sorted, or [].

Throws DeviceError: denied when the notebook may not use secrets; unavailable before the device's first unlock since it started.

Availability

In the appPermissionRemote kernelBrowser
yessecretsnono
ts
console.log(await device.secrets.list()); // ["github"]

SoundPlayer ​

A loaded sound a cell controls, from device.sound.player: a NativeHandle. Close it when done, or hold it with await using. The app closes a kernel's players when that kernel restarts or shuts down; a kernel has at most 32 open. Each method answers the player's state after the change.

Its methods work wherever device.sound.player does.

player.play(options?) ​

ts
play(options?: CallOptions): Promise<SoundState>;

Plays from the current time.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal.

Returns Promise<SoundState>: The player's state after the change.

Throws DeviceError: failed when the sound can't start, or the player is closed.

ts
const state = await player.play();
console.log(state.playing, state.duration);

player.pause(options?) ​

ts
pause(options?: CallOptions): Promise<SoundState>;

Pauses at the current time.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal.

Returns Promise<SoundState>: The player's state after the change.

ts
const { time } = await player.pause();
console.log(`paused at ${time.toFixed(1)} s`);

player.stop(options?) ​

ts
stop(options?: CallOptions): Promise<SoundState>;

Pauses and goes back to the start.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal.

Returns Promise<SoundState>: The player's state after the change.

ts
await player.stop();

player.seek(seconds, options?) ​

ts
seek(seconds: number, options?: CallOptions): Promise<SoundState>;

Moves to a time, playing or not.

ParameterTypeRequiredDescription
secondsnumberyesThe time to move to, in seconds from the start: 0 or more.
optionsCallOptionsnoThe call's signal.

Returns Promise<SoundState>: The player's state after the change.

Throws DeviceError: failed when seconds is negative or not a number.

ts
await player.seek(player.duration / 2);
await player.play();

player.setVolume(volume, options?) ​

ts
setVolume(volume: number, options?: CallOptions): Promise<SoundState>;

Sets the volume.

ParameterTypeRequiredDescription
volumenumberyesFrom 0 to 1.
optionsCallOptionsnoThe call's signal.

Returns Promise<SoundState>: The player's state after the change.

Throws DeviceError: failed when volume is outside 0 to 1.

ts
await player.setVolume(0.25);

player.state(options?) ​

ts
state(options?: CallOptions): Promise<SoundState>;

Reads the player's state, changing nothing.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal.

Returns Promise<SoundState>: Whether it is playing, its time and its length.

ts
const { playing, time } = await player.state();

player.close() ​

ts
close(): Promise<void>;

Stops the player and frees it; later calls fail failed. Closing twice is fine. Leaving an await using block closes it too.

ts
await player.close();

Properties ​

PropertyTypeDescription
durationnumberThe sound's length in seconds.

Peripheral ​

A connected Bluetooth LE peripheral, from device.bluetooth.connect: a NativeHandle. Close it to disconnect, or hold it with await using. The app closes a kernel's peripherals when that kernel restarts or shuts down; a kernel has at most 8 connected. When the peripheral goes away, waiting calls and the next call reject disconnected, notify streams end with it, and the handle closes (closed reads true once a call has seen it). Services and characteristics are UUIDs: "180D", or the full form.

Its methods work wherever device.bluetooth.connect does.

peripheral.services(options?) ​

ts
services(options?: CallOptions): Promise<BluetoothService[]>;

Its services and their characteristics, discovered on the first call.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<BluetoothService[]>: Each service with its characteristics and what they allow.

Throws DeviceError: disconnected when it went away; failed when it is closed or discovery fails.

ts
for (const service of await peripheral.services()) console.log(service.uuid, service.characteristics.map((c) => c.uuid));

peripheral.read(service, characteristic, options?) ​

ts
read(service: string, characteristic: string, options?: CallOptions): Promise<Uint8Array>;

Reads a characteristic's value.

ParameterTypeRequiredDescription
servicestringyesThe service's UUID.
characteristicstringyesThe characteristic's UUID.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<Uint8Array>: The value's bytes.

Throws DeviceError: disconnected when it went away; failed when it is closed, there is no such characteristic, or it can't be read.

ts
const [level] = await peripheral.read("180F", "2A19");
console.log(`battery ${level}%`);

peripheral.write(service, characteristic, data, options?) ​

ts
write(service: string, characteristic: string, data: Bytes, options?: WriteOptions): Promise<void>;

Writes a characteristic's value.

ParameterTypeRequiredDescription
servicestringyesThe service's UUID.
characteristicstringyesThe characteristic's UUID.
dataBytesyesThe bytes, at most what the link takes at once (512 with a response).
optionsWriteOptionsnowithResponse (true by default), and the call's signal. Cancel rejects cancelled; the write may already be sent.

Returns once the peripheral confirms it, or at once without a response.

Throws DeviceError: disconnected when it went away; failed when it is closed, there is no such characteristic, it can't be written that way, or data is too long.

ts
await peripheral.write("6E400001-B5A3-F393-E0A9-E50E24DCCA9E", "6E400002-B5A3-F393-E0A9-E50E24DCCA9E", new TextEncoder().encode("hi"));

peripheral.notify(service, characteristic, options?) ​

ts
notify(service: string, characteristic: string, options?: CallOptions): DeviceStream<Uint8Array>;

A characteristic's values as it notifies or indicates them, for for await, until the loop ends, the cell is interrupted, or the peripheral closes. Keeps the newest 256 when the cell reads slower than they come.

ParameterTypeRequiredDescription
servicestringyesThe service's UUID.
characteristicstringyesThe characteristic's UUID.
optionsCallOptionsnoThe call's signal, which ends the stream.

Returns DeviceStream<Uint8Array>: A stream of values; leaving the loop unsubscribes.

Throws DeviceError: disconnected when it went away; failed when it is closed, or there is no such characteristic or it doesn't notify.

ts
for await (const value of peripheral.notify("180D", "2A37")) {
  console.log(`heart rate ${value[1]}`);
}

peripheral.rssi(options?) ​

ts
rssi(options?: CallOptions): Promise<number>;

Reads the signal strength now.

ParameterTypeRequiredDescription
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<number>: dBm, such as -60.

Throws DeviceError: disconnected when it went away; failed when it is closed.

ts
console.log(await peripheral.rssi());

Properties ​

PropertyTypeDescription
idstringIts id on this device.
namestring | nullIts name when it connected, or null.

Folder ​

A folder the person picked for this notebook, from device.folders.pick() or list(). It stays this notebook's until the person revokes it in Settings, across kernel and app restarts, so there is nothing to close.

Each path is relative to the folder, with / between parts. An absolute path, a .., or a symlink that leads outside the folder rejects denied.

Its methods work wherever device.folders.pick does. Paths are relative to the folder; one that leads outside it rejects denied.

folder.entries(path?, options?) ​

ts
entries(path?: string, options?: CallOptions): Promise<FolderEntry[]>;

Lists a directory in the folder, sorted by name.

ParameterTypeRequiredDescription
pathstringnoA directory in the folder; the folder itself when left out.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<FolderEntry[]>: Its files and directories, hidden ones included.

Throws DeviceError: denied when the path leaves the folder or the grant was revoked; unavailable when the folder can't be reached now (a disconnected drive, say); failed when there is no directory there.

ts
const [folder] = await device.folders.list();
for (const entry of await folder.entries()) console.log(entry.kind, entry.name, entry.size);

folder.read(path, options?) ​

ts
read(path: string, options?: CallOptions): Promise<Uint8Array>;

Reads a file in the folder.

ParameterTypeRequiredDescription
pathstringyesThe file, relative to the folder.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled.

Returns Promise<Uint8Array>: Its bytes.

Throws DeviceError: denied when the path leaves the folder or the grant was revoked; unavailable when the folder can't be reached now (a disconnected drive, say); failed when there is no file there or it is over 16 MiB.

ts
const [folder] = await device.folders.list();
const csv = new TextDecoder().decode(await folder.read("data/temps.csv"));

folder.write(path, data, options?) ​

ts
write(path: string, data: Bytes | string, options?: CallOptions): Promise<void>;

Writes a file in the folder, replacing what is there and making the directories on the way.

ParameterTypeRequiredDescription
pathstringyesThe file, relative to the folder.
dataBytes | stringyesThe bytes, at most 16 MiB, or a string written as UTF-8.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled; the write may already be done.

Returns once the file is written.

Throws DeviceError: denied when the folder was picked for reading only, the path leaves the folder, or the grant was revoked; unavailable when the folder can't be reached now (a disconnected drive, say); failed when the path is a directory or data is over 16 MiB.

ts
const folder = await device.folders.pick({ write: true });
if (folder) await folder.write("notes/today.txt", "Written from a notebook");

folder.remove(path, options?) ​

ts
remove(path: string, options?: CallOptions): Promise<void>;

Removes a file, a symlink (not what it points to), or an empty directory, in the folder.

ParameterTypeRequiredDescription
pathstringyesWhat to remove, relative to the folder.
optionsCallOptionsnoThe call's signal. Cancel rejects cancelled; it may already be removed.

Returns once it is gone.

Throws DeviceError: denied when the folder was picked for reading only, the path leaves the folder or is the folder itself, or the grant was revoked; unavailable when the folder can't be reached now (a disconnected drive, say); failed when nothing is there or a directory isn't empty.

ts
const folder = await device.folders.pick({ write: true });
if (folder) await folder.remove("notes/old.txt");

Properties ​

PropertyTypeDescription
idstringNames the grant; list() gives the same id after a restart.
namestringThe folder's name, as the picker showed it.
writablebooleanWhether it was picked with write: true.

Errors ​

A call that fails rejects with a DeviceError, an Error whose code says why. A person dismissing a picker or dialog is not an error.

codeMeaning
unavailableNo native host, or this platform doesn't have the feature.
deniedThe person or the system refused permission.
cancelledInterrupted or aborted before the host answered.
timeoutThe call's signal came from AbortSignal.timeout() and timed out.
failedAnything else, such as an argument the method refuses; the message says what.
disconnectedThe peer went away mid-call, such as a Bluetooth device or a remote link.

Types ​

CallOptions ​

Every method takes these. Aborting signal, or interrupting the kernel, cancels the call: it rejects with a DeviceError whose code is cancelled (timeout for AbortSignal.timeout()), and the host drops any dialog or prompt it showed for it.

Where each method works is in SUPPORT (src/support.ts); the docs' table and the reference's Availability lines are generated from it.

PropertyTypeRequiredDescription
signalAbortSignalnoCancels the call when aborted. In a cdp-kernel cell, jupyter.signal aborts when the cell is interrupted.

DeviceInfo ​

What the host answers to hello, once, before the first cell, and the current state when it answers info.

PropertyTypeRequiredDescription
platformstringyes"macos", "ipados", "ios" or "browser".
appVersionstringyesThe app's version, such as "0.5.0".
modelstringnoThe hardware model identifier, such as "Mac14,2" or "iPad13,4".
osVersionstringnoThe operating system's version, such as "26.1.0".
capabilitiesstring[]yesThe methods this host answers, such as "clipboard.readText".
isPadbooleannoTrue on an iPad. This and the properties after it are read when info() is called, on hosts that answer it.
screen{ width: number; height: number; scale: number }noThe main screen in points, and its points-to-pixels scale.
battery{ level: number | null; charging: boolean }noAbsent on a device without a battery. level is 0 to 1, or null when unknown.
darkModebooleannoWhether the system appearance is dark.
localestringnoThe person's locale as a BCP 47 tag, such as "en-US".

OpenURLOptions ​

Options for device.openURL.

PropertyTypeRequiredDescription
inAppbooleannoOn iPhone and iPad, show a web page in the app (Safari view) instead of switching to Safari. Ignored on the Mac.

It also takes CallOptions's properties.

ImpactStyle ​

The strength of a haptics.impact tap.

ts
export type ImpactStyle = "light" | "medium" | "heavy" | "soft" | "rigid";

NotificationFeedback ​

The pattern haptics.notification plays.

ts
export type NotificationFeedback = "success" | "warning" | "error";

AlertOptions ​

Options for device.ui.alert.

PropertyTypeRequiredDescription
titlestringnoA heading above the message.

It also takes CallOptions's properties.

ConfirmOptions ​

Options for device.ui.confirm.

PropertyTypeRequiredDescription
destructivebooleannoShows the confirming button as destructive (red).
okLabelstringnoThe confirming button's label; "OK" by default.

It also takes AlertOptions's properties.

PromptOptions ​

Options for device.ui.prompt.

PropertyTypeRequiredDescription
placeholderstringnoGrey text shown while the field is empty.
defaultValuestringnoText the field starts with.
securebooleannoHides what is typed, as for a password.

It also takes AlertOptions's properties.

ActionSheetOptions ​

Options for device.ui.actionSheet.

PropertyTypeRequiredDescription
messagestringnoA line under the title.
cancelstringnoThe cancel button's label; "Cancel" by default.
destructivenumbernoThe index of an option to show as destructive (red).

It also takes CallOptions's properties.

PickDateOptions ​

Options for device.ui.pickDate.

PropertyTypeRequiredDescription
modeDateModenoWhat to pick; "datetime" by default.
titlestringnoA heading above the picker.
valueDatenoThe date the picker starts at; now by default.
minDatenoThe earliest date that can be picked.
maxDatenoThe latest date that can be picked.

It also takes CallOptions's properties.

DateMode ​

What device.ui.pickDate picks.

ts
export type DateMode = "date" | "time" | "datetime";

ShareOptions ​

What device.share shares: at least one of text, url and files.

PropertyTypeRequiredDescription
textstringnoText to share, such as a message or a caption.
urlstring | URLnoA link to share: http, https or mailto.
filesNamedBytes[]noFiles to share, together at most 16 MiB; their names are what the receiving app sees.

It also takes CallOptions's properties.

NamedBytes ​

A file a cell hands over: a File, or bytes with the name to show and save them under.

ts
export type NamedBytes = File | { name: string; data: Bytes };

Bytes ​

Bytes a device method takes.

ts
export type Bytes = Uint8Array | ArrayBuffer | Blob;

PickFilesOptions ​

Options for device.files.pick.

PropertyTypeRequiredDescription
typesstring[]noWhat may be picked: MIME types ("text/csv"), wildcards ("image/*") or extensions (".parquet"). Anything by default.
multiplebooleannoWhether several files may be picked; one by default.

It also takes CallOptions's properties.

ExportFileOptions ​

Options for device.files.export.

PropertyTypeRequiredDescription
namestringyesThe name the save panel suggests: a plain file name, without slashes.

It also takes CallOptions's properties.

PickPhotosOptions ​

Options for device.photos.pick.

PropertyTypeRequiredDescription
limitnumbernoHow many photos may be picked; 1 by default, 0 for no limit.

It also takes CallOptions's properties.

PlaySoundOptions ​

Options for device.sound.play.

PropertyTypeRequiredDescription
volumenumbernoFrom 0 to 1; 1 by default.

It also takes CallOptions's properties.

SoundState ​

Where a player is: whether it is playing, and its time and length in seconds.

PropertyTypeRequiredDescription
playingbooleanyesWhether it is playing now.
timenumberyesWhere it is, in seconds from the start.
durationnumberyesThe sound's length in seconds.

NativeHandle ​

Something on the device a call opened that outlives the call, such as a sound player. Close it when done, or hold it with await using. The app closes a kernel's handles when that kernel restarts or shuts down. A call on a closed handle rejects failed.

PropertyTypeRequiredDescription
closedbooleanyesWhether it is closed: by close(), or by leaving its await using block.
closePromise<void>yesCloses it and frees what it holds. Closing twice is fine.

HostEvents ​

What each host event's handler gets, by event (device.on).

PropertyTypeRequiredDescription
appearance"light" | "dark"yesThe system appearance changed.
foregroundnullyesThe app came to the front.
backgroundnullyesThe app left the front: another app is in front, or the device locked.
battery{ level: number | null; charging: boolean }yesThe battery's level (0 to 1, or null when unknown) or charging changed. Never on a Mac without a battery.
localestringyesThe system language or region changed: the new locale, a BCP 47 tag such as "fr-FR".

HostEvent ​

A host event's name: a key of HostEvents.

ts
export type HostEvent = keyof HostEvents;

OnOptions ​

Options for device.on.

PropertyTypeRequiredDescription
signalAbortSignalnoRemoves the handler when aborted. An interrupt doesn't: on outlives its cell.

ScheduleNotificationOptions ​

Options for device.notifications.schedule.

PropertyTypeRequiredDescription
delaynumberyesSeconds from now, more than 0.
titlestringnoShown above the body.

It also takes CallOptions's properties.

PendingNotification ​

A notification this notebook scheduled that hasn't been delivered.

PropertyTypeRequiredDescription
idstringyesWhat schedule() returned.
titlestringnoThe title it was scheduled with.
bodystringyesThe text it shows.
dateDatenoWhen it is due, when the host knows.

LocationOptions ​

Options for device.location.current and device.location.watch.

PropertyTypeRequiredDescription
accuracyLocationAccuracynoHow close a fix must be; "best" by default. Coarser fixes come sooner and use less power.

It also takes CallOptions's properties.

LocationAccuracy ​

How close a fix must be: within metres ("best"), a city block, or a city.

ts
export type LocationAccuracy = "best" | "block" | "city";

LocationFix ​

Where the device is, as Core Location reports it.

PropertyTypeRequiredDescription
latitudenumberyesDegrees north of the equator.
longitudenumberyesDegrees east of Greenwich.
accuracynumberyesThe fix is within this many metres of the true position.
altitudenumbernoMetres above sea level, when known.
altitudeAccuracynumbernoThe altitude is within this many metres, when known.
speednumbernoMetres per second, when moving and known.
coursenumbernoDegrees clockwise from true north, when moving and known.
timestampnumberyesWhen the fix was taken, in milliseconds since 1970, as Date.now().

Place ​

A place a geocode found. Every part is there only when known.

PropertyTypeRequiredDescription
namestringnoThe place's name, such as a landmark or the street address.
streetstringnoHouse number and street.
citystringnoCity or town.
regionstringnoState, province or the like.
postalCodestringnoPostal or ZIP code.
countrystringnoCountry name, in the device's language.
countryCodestringnoISO 3166-1 alpha-2, such as "US".
latitudenumbernoDegrees north of the equator.
longitudenumbernoDegrees east of Greenwich.

MotionWatchOptions ​

Options for device.motion.watch.

PropertyTypeRequiredDescription
intervalnumbernoSeconds between samples, 0.01 to 1; 0.1 by default.

It also takes CallOptions's properties.

MotionSample ​

One reading of Core Motion's device motion.

PropertyTypeRequiredDescription
attitudeAttitudeyesWhich way the device is turned.
rotationRateVector3yesRadians per second around each axis.
gravityVector3yesGravity's pull, in g (1 g is 9.81 m/s²).
userAccelerationVector3yesWhat the person adds to gravity, in g.
timestampnumberyesWhen it was read, in milliseconds since 1970, as Date.now().

Attitude ​

Which way the device is turned, in radians.

PropertyTypeRequiredDescription
rollnumberyesAround the long (y) axis.
pitchnumberyesAround the short (x) axis.
yawnumberyesAround the axis out of the screen (z).

Vector3 ​

A direction or rate along the device's three axes.

PropertyTypeRequiredDescription
xnumberyesAlong the short side, to the right with the screen facing you.
ynumberyesAlong the long side, toward the top.
znumberyesOut of the screen, toward you.

Contact ​

A contact as device.contacts reads it: a plain record. Fields the contact doesn't have are left out. Groups, photos and notes aren't included.

PropertyTypeRequiredDescription
idstringyesThe contacts store's identifier, for get and delete.
givenNamestringnoThe first name.
middleNamestringnoThe middle name.
familyNamestringnoThe last name.
nicknamestringnoThe nickname.
organizationstringnoThe company or organization.
jobTitlestringnoThe job title.
emailsLabeledValue[]noEmail addresses.
phonesLabeledValue[]noPhone numbers, as entered.
addressesPostalAddress[]noPostal addresses.
birthdaystringno"YYYY-MM-DD", or "--MM-DD" when the year isn't known.

LabeledValue ​

A label and a value, such as a work email.

PropertyTypeRequiredDescription
labelstringno"home", "work", "mobile", "main", "iPhone", "school", "other", or the person's own label.
valuestringyesThe email address or phone number.

PostalAddress ​

A contact's postal address; any part may be missing.

PropertyTypeRequiredDescription
labelstringno"home", "work", "other", or the person's own label.
streetstringnoThe street, with the house number; may span lines.
citystringnoThe city or town.
statestringnoThe state, province or region.
postalCodestringnoThe postal or ZIP code.
countrystringnoThe country, as written.

ReminderCalendar ​

A reminder list, from device.reminders.calendars().

PropertyTypeRequiredDescription
idstringyesThe list's id, for list({ calendar }) and add({ calendar }).
titlestringyesThe list's name, as the Reminders app shows it.
isDefaultbooleanyesWhether add puts reminders here when it isn't given a list.

Reminder ​

A reminder as device.reminders reads it: a plain record.

PropertyTypeRequiredDescription
idstringyesThe store's identifier, for complete, uncomplete and delete.
titlestringyesWhat to do.
calendarstringyesThe id of the list it is in.
completedbooleanyesWhether it is done.
notesstringnoNotes, when it has any.
duestringno"YYYY-MM-DD" for a due day, or an ISO 8601 date and time when it is due at a time.
prioritynumberno1 (high) to 9 (low); left out for none. The Reminders app shows 1, 5 and 9 as high, medium and low.
completedAtstringnoWhen it was completed, as ISO 8601.

NewReminder ​

A reminder for device.reminders.add: a title, and the rest optional.

PropertyTypeRequiredDescription
titlestringyesWhat to do.
notesstringnoNotes.
duestring | DatenoA due day ("YYYY-MM-DD"), or a day and time: an ISO 8601 string or a Date.
prioritynumberno0 (none, the default) or 1 (high) to 9 (low).
calendarstringnoThe id of the list to add it to; the default list when left out.

ListRemindersOptions ​

Options for device.reminders.list.

PropertyTypeRequiredDescription
calendarstringnoA list's id from calendars(); every list when left out.
completedbooleannotrue for completed reminders only, false for incomplete ones only; both when left out.

It also takes CallOptions's properties.

ScanOptions ​

Options for device.bluetooth.scan.

PropertyTypeRequiredDescription
servicesstring[]noOnly peripherals that advertise one of these service UUIDs ("180D", or a full UUID). All by default.
allowDuplicatesbooleannoEvery advertisement, not only each peripheral's first; for RSSI over time. False by default.

It also takes CallOptions's properties.

One advertisement a scan heard.

PropertyTypeRequiredDescription
idstringyesThe peripheral's id on this device, for connect() and known(). Another device sees a different id.
namestring | nullyesIts advertised or cached name, or null.
rssinumberyesSignal strength in dBm, such as -60; nearer is higher.
servicesstring[]yesThe service UUIDs it advertises.
manufacturerDataUint8Array | nullyesIts manufacturer data, the company id first, or null.
serviceDataRecord<string, Uint8Array>yesService data by service UUID.

KnownPeripheral ​

A peripheral the system knows, from device.bluetooth.known.

PropertyTypeRequiredDescription
idstringyesIts id, for connect().
namestring | nullyesIts cached name, or null.

ConnectOptions ​

Options for device.bluetooth.connect.

PropertyTypeRequiredDescription
timeoutnumbernoSeconds to wait for the peripheral, 1 to 120; 15 by default. Then the call rejects timeout.

It also takes CallOptions's properties.

BluetoothService ​

A GATT service and its characteristics.

PropertyTypeRequiredDescription
uuidstringyesIts UUID, as Core Bluetooth writes it ("180D", or the full form).
characteristicsBluetoothCharacteristic[]yesIts characteristics.

BluetoothCharacteristic ​

A GATT characteristic and what it allows.

PropertyTypeRequiredDescription
uuidstringyesIts UUID, as Core Bluetooth writes it ("2A37", or the full form).
propertiesArray<"read" | "write" | "writeWithoutResponse" | "notify" | "indicate">yesWhat it allows.

WriteOptions ​

Options for Peripheral.write.

PropertyTypeRequiredDescription
withResponsebooleannoWait for the peripheral to confirm; true by default. False sends and resolves at once.

It also takes CallOptions's properties.

PickFolderOptions ​

Options for device.folders.pick.

PropertyTypeRequiredDescription
writebooleannoAsk for writing too, not only reading. false by default.

It also takes CallOptions's properties.

FolderEntry ​

One item in a folder, from Folder.entries().

PropertyTypeRequiredDescription
namestringyesIts name in its directory.
kind"file" | "directory" | "link"yes"link" is a symlink. Reading it follows it while its target stays in the folder; remove takes away the link, not what it points to.
sizenumbernoBytes, for a file.
modifiedDatenoWhen its contents last changed.

AuthorizationStatus ​

A capability's authorization. Partial grants count as granted: limited (some contacts or photos), writeOnly (add reminders or photos, not read them) and reducedAccuracy (approximate location).

ts
export type AuthorizationStatus = "notDetermined" | "denied" | "restricted" | "authorized" | "limited" | "writeOnly" | "reducedAccuracy";

MIT license.