@wix/himalaya-cli 0.1591.0 → 0.1593.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,4 +13,56 @@ export declare function getPermissionStatus(): Promise<PermissionStatus>;
13
13
  export declare function requestPermission(): Promise<PermissionStatus>;
14
14
  /** Search contacts by name (requires read permission). [] when denied/no match. */
15
15
  export declare function search(query: string, limit?: number): Promise<Contact[]>;
16
+ /** One labelled phone number or email address. `label` is free text ("work", "mobile"…);
17
+ * each host maps the common ones to its native label and keeps the rest as a custom label. */
18
+ export interface ContactField {
19
+ label?: string;
20
+ value: string;
21
+ }
22
+ /** A contact to create. At least one of `givenName`, `familyName` or `organization` must be
23
+ * non-blank; every list holds at most {@link MAX_CONTACT_ENTRIES} entries. */
24
+ export interface NewContact {
25
+ givenName?: string;
26
+ familyName?: string;
27
+ organization?: string;
28
+ phones?: ContactField[];
29
+ emails?: ContactField[];
30
+ urls?: string[];
31
+ note?: string;
32
+ }
33
+ /** The most entries `phones`, `emails` or `urls` may carry. */
34
+ export declare const MAX_CONTACT_ENTRIES = 20;
35
+ /** Why a create verb rejected. */
36
+ export type ContactsErrorCode = "invalid_contact" | "permission_denied" | "unavailable";
37
+ /** A create-verb rejection: an `Error` named "ContactsError" carrying a stable `code`. (A plain
38
+ * Error, not a subclass — Tier-5 bundles target an engine without class syntax.) Branch on
39
+ * `isContactsError(e) && e.code === "permission_denied"`. */
40
+ export interface ContactsError extends Error {
41
+ name: "ContactsError";
42
+ code: ContactsErrorCode;
43
+ }
44
+ /** Build a coded contacts rejection. */
45
+ export declare function contactsError(code: ContactsErrorCode, message?: string): ContactsError;
46
+ /** Whether `e` is a coded contacts rejection. */
47
+ export declare function isContactsError(e: unknown): e is ContactsError;
48
+ /** Validate and normalize a {@link NewContact}: strings trimmed, blank entries dropped, blank
49
+ * fields omitted. Throws `invalid_contact` when there is no name or organization, or a list
50
+ * holds more than {@link MAX_CONTACT_ENTRIES} entries. Every host re-checks the same rules. */
51
+ export declare function normalizeNewContact(contact: NewContact): NewContact;
52
+ /** Open the OS new-contact sheet prefilled with `contact` — the recommended way to save a
53
+ * contact: it needs NO contacts permission, because the user reviews and confirms the save.
54
+ * Resolves `{ saved: true }` when the user saved it, `{ saved: false }` on cancel. Web
55
+ * downloads a `.vcf` instead (and resolves `saved: true` once the download is handed off).
56
+ * Rejects `invalid_contact` (see {@link normalizeNewContact}) or `unavailable`. */
57
+ export declare function presentNewContact(contact: NewContact): Promise<{
58
+ saved: boolean;
59
+ }>;
60
+ /** Write `contact` straight into the address book and resolve its new `id`. Needs contacts
61
+ * WRITE permission — ask with {@link requestPermission} first; without it this rejects
62
+ * `permission_denied` (it never prompts). Rejects `invalid_contact`, or `unavailable` where
63
+ * the host has no address book (web). Prefer {@link presentNewContact} unless the app already
64
+ * holds the permission. */
65
+ export declare function add(contact: NewContact): Promise<{
66
+ id: string;
67
+ }>;
16
68
  export declare const version = 1;
@@ -36,5 +36,80 @@ export declare function list(dirPath?: string): Promise<FileStat[]>;
36
36
  export declare function remove(ref: FileHandle | string): Promise<void>;
37
37
  /** Stat a path, or `null` if it doesn't exist (the existence check for handles). */
38
38
  export declare function stat(path: string): Promise<FileStat | null>;
39
- /** Capability version, for bundles that gate on min binary version. */
40
- export declare const version = 2;
39
+ /** Stable failure codes for the v3 verbs. Branch on `FilesError.code` (see `isFilesError`), never on
40
+ * the message text.
41
+ * - `invalid_url` — not an absolute `https:` URL, carries userinfo, or names a
42
+ * loopback / private-network / link-local host (checked on every
43
+ * redirect hop; natively also on the resolved DNS records).
44
+ * - `too_large` — the response is (or announces it will be) over `maxBytes`.
45
+ * - `http_error` — the server answered a non-2xx status (`status` is set).
46
+ * - `network` — the transfer failed (DNS, TLS, reset, CORS on web, …).
47
+ * - `unavailable` — this host cannot do it: no implementation for the verb, no save
48
+ * picker or volume reading to hand, or (`download`) the account signed
49
+ * out mid-transfer, so the file was not kept. */
50
+ export type FilesErrorCode = "invalid_url" | "too_large" | "http_error" | "network" | "unavailable";
51
+ /** A v3 verb failure: an `Error` whose `code` is stable and `message` is for humans.
52
+ * (A plain Error with fields, not a subclass — worker bundles target a syntax level
53
+ * without `class`.) Narrow with {@link isFilesError}. */
54
+ export interface FilesError extends Error {
55
+ name: "FilesError";
56
+ code: FilesErrorCode;
57
+ status?: number;
58
+ }
59
+ /** Build a FilesError. */
60
+ export declare function filesError(code: FilesErrorCode, message: string, status?: number): FilesError;
61
+ /** Whether `e` is a FilesError thrown by a v3 verb. */
62
+ export declare function isFilesError(e: unknown): e is FilesError;
63
+ /** The download cap when `maxBytes` is omitted: 100 MiB. */
64
+ export declare const DEFAULT_DOWNLOAD_MAX_BYTES: number;
65
+ /** The hard ceiling every arm clamps `maxBytes` to: 1 GiB. */
66
+ export declare const MAX_DOWNLOAD_BYTES: number;
67
+ /**
68
+ * Stream an `https:` URL straight into the sandbox and resolve with its FileHandle —
69
+ * the same shape `write` returns. The bytes never cross the worker bridge.
70
+ *
71
+ * - No credentials are attached: a fresh cookie-less session, no auth provider (the
72
+ * `presigned_upload` rule). Use it for public / pre-signed URLs.
73
+ * - `http:`, userinfo, and loopback / private-network / link-local hosts are refused
74
+ * (`invalid_url`); every redirect hop is re-checked before it is followed, and the
75
+ * native arms validate the resolved DNS records too.
76
+ * - `name` is the destination path relative to the files root (like `write`'s
77
+ * `path`); default `downloads/<last URL path segment>`. An existing file is replaced.
78
+ * - `maxBytes` defaults to 100 MiB (clamped to 1 GiB); over it the transfer is aborted (`too_large`) and
79
+ * nothing is left behind.
80
+ */
81
+ export declare function download(url: string, opts?: {
82
+ name?: string;
83
+ maxBytes?: number;
84
+ }): Promise<FileHandle>;
85
+ /**
86
+ * Let the user save a sandbox file OUTSIDE the app through the OS save picker: the
87
+ * Files app on iOS, `NSSavePanel` on macOS, the Storage Access Framework on Android
88
+ * (no storage permission — the user picks the folder), a download on web. Resolves
89
+ * `{ exported: false }` when the user cancels.
90
+ *
91
+ * Exported as `export`, so call it as `files.export(handle)` (namespace import) or
92
+ * `import { export as exportFile } from "@wix/himalaya/files"`.
93
+ */
94
+ declare function exportFile(file: FileHandle | string, opts?: {
95
+ suggestedName?: string;
96
+ mimeType?: string;
97
+ }): Promise<{
98
+ exported: boolean;
99
+ }>;
100
+ export { exportFile as export };
101
+ /** Storage figures, in bytes. `source` says what they measure:
102
+ * - `"device"` — the device volume (iOS `volumeAvailableCapacityForImportantUsage`,
103
+ * Android `StatFs`) and the bytes in this app's files root.
104
+ * - `"estimate"` — web: the origin's `navigator.storage.estimate()` quota/usage. */
105
+ export interface StorageInfo {
106
+ freeBytes: number;
107
+ totalBytes: number;
108
+ appBytes: number;
109
+ source: "device" | "estimate";
110
+ }
111
+ /** How much space is left before a large write, and how much this app's files use. */
112
+ export declare function storageInfo(): Promise<StorageInfo>;
113
+ /** Capability version, for bundles that gate on min binary version.
114
+ * 3 — `download`, `export`, `storageInfo`. */
115
+ export declare const version = 3;
@@ -9,9 +9,9 @@ export declare const CAPABILITY_REGISTRY: {
9
9
  readonly calls: readonly ["answer", "availability", "getPermissionStatus", "getState", "hangup", "listCalls", "offer", "receiveSignal", "reject", "requestPermission", "setMuted", "setRingback", "setSpeaker", "signalNext", "signalStart", "signalStop", "start", "subscribeEvents"];
10
10
  readonly camera: readonly ["capture", "pickDocument", "prepareImage", "scanQR"];
11
11
  readonly clipboard: readonly ["hasImage", "hasText", "readText", "writeImage", "writeText"];
12
- readonly contacts: readonly ["getPermissionStatus", "pick", "requestPermission", "search"];
12
+ readonly contacts: readonly ["add", "getPermissionStatus", "pick", "presentNewContact", "requestPermission", "search"];
13
13
  readonly file_picker: readonly ["pick", "pickDirectoryForExport"];
14
- readonly files: readonly ["list", "readText", "remove", "stat", "write"];
14
+ readonly files: readonly ["download", "export", "list", "readText", "remove", "stat", "storageInfo", "write"];
15
15
  readonly godot: readonly ["loadPack", "release", "send"];
16
16
  readonly haptic: readonly ["play", "sequence", "vibrate"];
17
17
  readonly health: readonly ["availability", "getPermissionStatus", "query", "requestPermission", "saveWorkout"];
@@ -24,9 +24,9 @@ var CAPABILITY_REGISTRY = {
24
24
  "calls": ["answer", "availability", "getPermissionStatus", "getState", "hangup", "listCalls", "offer", "receiveSignal", "reject", "requestPermission", "setMuted", "setRingback", "setSpeaker", "signalNext", "signalStart", "signalStop", "start", "subscribeEvents"],
25
25
  "camera": ["capture", "pickDocument", "prepareImage", "scanQR"],
26
26
  "clipboard": ["hasImage", "hasText", "readText", "writeImage", "writeText"],
27
- "contacts": ["getPermissionStatus", "pick", "requestPermission", "search"],
27
+ "contacts": ["add", "getPermissionStatus", "pick", "presentNewContact", "requestPermission", "search"],
28
28
  "file_picker": ["pick", "pickDirectoryForExport"],
29
- "files": ["list", "readText", "remove", "stat", "write"],
29
+ "files": ["download", "export", "list", "readText", "remove", "stat", "storageInfo", "write"],
30
30
  "godot": ["loadPack", "release", "send"],
31
31
  "haptic": ["play", "sequence", "vibrate"],
32
32
  "health": ["availability", "getPermissionStatus", "query", "requestPermission", "saveWorkout"],
@@ -24,9 +24,9 @@ var CAPABILITY_REGISTRY = {
24
24
  "calls": ["answer", "availability", "getPermissionStatus", "getState", "hangup", "listCalls", "offer", "receiveSignal", "reject", "requestPermission", "setMuted", "setRingback", "setSpeaker", "signalNext", "signalStart", "signalStop", "start", "subscribeEvents"],
25
25
  "camera": ["capture", "pickDocument", "prepareImage", "scanQR"],
26
26
  "clipboard": ["hasImage", "hasText", "readText", "writeImage", "writeText"],
27
- "contacts": ["getPermissionStatus", "pick", "requestPermission", "search"],
27
+ "contacts": ["add", "getPermissionStatus", "pick", "presentNewContact", "requestPermission", "search"],
28
28
  "file_picker": ["pick", "pickDirectoryForExport"],
29
- "files": ["list", "readText", "remove", "stat", "write"],
29
+ "files": ["download", "export", "list", "readText", "remove", "stat", "storageInfo", "write"],
30
30
  "godot": ["loadPack", "release", "send"],
31
31
  "haptic": ["play", "sequence", "vibrate"],
32
32
  "health": ["availability", "getPermissionStatus", "query", "requestPermission", "saveWorkout"],
@@ -24,9 +24,9 @@ var CAPABILITY_REGISTRY = {
24
24
  "calls": ["answer", "availability", "getPermissionStatus", "getState", "hangup", "listCalls", "offer", "receiveSignal", "reject", "requestPermission", "setMuted", "setRingback", "setSpeaker", "signalNext", "signalStart", "signalStop", "start", "subscribeEvents"],
25
25
  "camera": ["capture", "pickDocument", "prepareImage", "scanQR"],
26
26
  "clipboard": ["hasImage", "hasText", "readText", "writeImage", "writeText"],
27
- "contacts": ["getPermissionStatus", "pick", "requestPermission", "search"],
27
+ "contacts": ["add", "getPermissionStatus", "pick", "presentNewContact", "requestPermission", "search"],
28
28
  "file_picker": ["pick", "pickDirectoryForExport"],
29
- "files": ["list", "readText", "remove", "stat", "write"],
29
+ "files": ["download", "export", "list", "readText", "remove", "stat", "storageInfo", "write"],
30
30
  "godot": ["loadPack", "release", "send"],
31
31
  "haptic": ["play", "sequence", "vibrate"],
32
32
  "health": ["availability", "getPermissionStatus", "query", "requestPermission", "saveWorkout"],
@@ -24,9 +24,9 @@ var CAPABILITY_REGISTRY = {
24
24
  "calls": ["answer", "availability", "getPermissionStatus", "getState", "hangup", "listCalls", "offer", "receiveSignal", "reject", "requestPermission", "setMuted", "setRingback", "setSpeaker", "signalNext", "signalStart", "signalStop", "start", "subscribeEvents"],
25
25
  "camera": ["capture", "pickDocument", "prepareImage", "scanQR"],
26
26
  "clipboard": ["hasImage", "hasText", "readText", "writeImage", "writeText"],
27
- "contacts": ["getPermissionStatus", "pick", "requestPermission", "search"],
27
+ "contacts": ["add", "getPermissionStatus", "pick", "presentNewContact", "requestPermission", "search"],
28
28
  "file_picker": ["pick", "pickDirectoryForExport"],
29
- "files": ["list", "readText", "remove", "stat", "write"],
29
+ "files": ["download", "export", "list", "readText", "remove", "stat", "storageInfo", "write"],
30
30
  "godot": ["loadPack", "release", "send"],
31
31
  "haptic": ["play", "sequence", "vibrate"],
32
32
  "health": ["availability", "getPermissionStatus", "query", "requestPermission", "saveWorkout"],
@@ -86,10 +86,99 @@ async function search(query, limit) {
86
86
  const r = await dispatch("capability.contacts.search", params);
87
87
  return Array.isArray(r?.items) ? r.items : [];
88
88
  }
89
+ var MAX_CONTACT_ENTRIES = 20;
90
+ var CODES = ["invalid_contact", "permission_denied", "unavailable"];
91
+ function contactsError(code, message) {
92
+ const text = !message ? code : message.startsWith(code) ? message : `${code}: ${message}`;
93
+ const err = new Error(text);
94
+ err.name = "ContactsError";
95
+ err.code = code;
96
+ return err;
97
+ }
98
+ function isContactsError(e) {
99
+ return e instanceof Error && e.name === "ContactsError" && CODES.includes(e.code);
100
+ }
101
+ function codeOf(err) {
102
+ const msg = err instanceof Error ? err.message : String(err ?? "");
103
+ let best = null;
104
+ for (const c of CODES) {
105
+ const m = new RegExp(`(^|[^a-z_])${c}([^a-z_]|$)`).exec(msg);
106
+ if (!m) continue;
107
+ const at = m.index + m[1].length;
108
+ if (!best || at < best.at) best = { code: c, at };
109
+ }
110
+ return best?.code ?? null;
111
+ }
112
+ var trimmed = (v) => typeof v === "string" ? v.trim() : "";
113
+ function fields(list, name) {
114
+ if (list == null) return [];
115
+ if (!Array.isArray(list)) throw contactsError("invalid_contact", `${name} must be an array`);
116
+ if (list.length > MAX_CONTACT_ENTRIES) throw contactsError("invalid_contact", `${name} holds more than ${MAX_CONTACT_ENTRIES} entries`);
117
+ const out = [];
118
+ for (const entry of list) {
119
+ const value = trimmed(entry?.value);
120
+ if (!value) continue;
121
+ const label = trimmed(entry.label);
122
+ out.push(label ? { label, value } : { value });
123
+ }
124
+ return out;
125
+ }
126
+ function normalizeNewContact(contact) {
127
+ if (contact == null || typeof contact !== "object") throw contactsError("invalid_contact", "contact must be an object");
128
+ const out = {};
129
+ const givenName = trimmed(contact.givenName);
130
+ const familyName = trimmed(contact.familyName);
131
+ const organization = trimmed(contact.organization);
132
+ if (!givenName && !familyName && !organization) {
133
+ throw contactsError("invalid_contact", "a contact needs a givenName, familyName or organization");
134
+ }
135
+ if (givenName) out.givenName = givenName;
136
+ if (familyName) out.familyName = familyName;
137
+ if (organization) out.organization = organization;
138
+ const phones = fields(contact.phones, "phones");
139
+ if (phones.length) out.phones = phones;
140
+ const emails = fields(contact.emails, "emails");
141
+ if (emails.length) out.emails = emails;
142
+ if (contact.urls != null) {
143
+ if (!Array.isArray(contact.urls)) throw contactsError("invalid_contact", "urls must be an array");
144
+ if (contact.urls.length > MAX_CONTACT_ENTRIES) throw contactsError("invalid_contact", `urls holds more than ${MAX_CONTACT_ENTRIES} entries`);
145
+ const urls = contact.urls.map(trimmed).filter((u) => u.length > 0);
146
+ if (urls.length) out.urls = urls;
147
+ }
148
+ const note = trimmed(contact.note);
149
+ if (note) out.note = note;
150
+ return out;
151
+ }
152
+ async function coded(call) {
153
+ try {
154
+ return await call();
155
+ } catch (err) {
156
+ const code = codeOf(err);
157
+ if (code) throw contactsError(code, err instanceof Error ? err.message : String(err));
158
+ throw err;
159
+ }
160
+ }
161
+ async function presentNewContact(contact) {
162
+ const wire = { contact: JSON.stringify(normalizeNewContact(contact)) };
163
+ const r = await coded(() => dispatch("capability.contacts.presentNewContact", wire));
164
+ return { saved: r?.saved === true };
165
+ }
166
+ async function add(contact) {
167
+ const wire = { contact: JSON.stringify(normalizeNewContact(contact)) };
168
+ const r = await coded(() => dispatch("capability.contacts.add", wire));
169
+ if (typeof r?.id !== "string" || r.id.length === 0) throw contactsError("unavailable", "the host returned no contact id");
170
+ return { id: r.id };
171
+ }
89
172
  var version = 1;
90
173
  export {
174
+ MAX_CONTACT_ENTRIES,
175
+ add,
176
+ contactsError,
91
177
  getPermissionStatus,
178
+ isContactsError,
179
+ normalizeNewContact,
92
180
  pick,
181
+ presentNewContact,
93
182
  requestPermission,
94
183
  search,
95
184
  version
@@ -61,13 +61,70 @@ async function stat(path) {
61
61
  const r = await dispatch("capability.files.stat", { path });
62
62
  return r?.stat ?? null;
63
63
  }
64
- var version = 2;
64
+ function filesError(code, message, status) {
65
+ const e = new Error(message);
66
+ e.name = "FilesError";
67
+ e.code = code;
68
+ if (typeof status === "number") e.status = status;
69
+ return e;
70
+ }
71
+ function isFilesError(e) {
72
+ return e instanceof Error && e.name === "FilesError" && typeof e.code === "string";
73
+ }
74
+ var ERROR_CODES = ["invalid_url", "too_large", "http_error", "network", "unavailable"];
75
+ function throwIfError(verb, r) {
76
+ const e = r?.error;
77
+ if (e == null) return;
78
+ const obj = typeof e === "object" ? e : { message: e };
79
+ const code = ERROR_CODES.includes(obj.code) ? obj.code : "network";
80
+ const message = typeof obj.message === "string" && obj.message ? obj.message : code;
81
+ throw filesError(code, `files.${verb}: ${message}`, typeof obj.status === "number" ? obj.status : void 0);
82
+ }
83
+ var DEFAULT_DOWNLOAD_MAX_BYTES = 100 * 1024 * 1024;
84
+ var MAX_DOWNLOAD_BYTES = 1024 * 1024 * 1024;
85
+ async function download(url, opts) {
86
+ const params = { url };
87
+ if (opts?.name) params.name = opts.name;
88
+ if (typeof opts?.maxBytes === "number" && opts.maxBytes > 0) params.maxBytes = String(Math.floor(opts.maxBytes));
89
+ const r = await dispatch("capability.files.download", params);
90
+ throwIfError("download", r);
91
+ return toHandle(r, opts?.name ?? "download");
92
+ }
93
+ async function exportFile(file, opts) {
94
+ const params = { uri: refToPath(file) };
95
+ if (typeof file !== "string" && file.ownerScope) params.ownerScope = file.ownerScope;
96
+ if (opts?.suggestedName) params.suggestedName = opts.suggestedName;
97
+ const mimeType = opts?.mimeType ?? (typeof file !== "string" ? file.mimeType : void 0);
98
+ if (mimeType) params.mimeType = mimeType;
99
+ const r = await dispatch("capability.files.export", params);
100
+ throwIfError("export", r);
101
+ return { exported: r?.exported === true };
102
+ }
103
+ async function storageInfo() {
104
+ const r = await dispatch("capability.files.storageInfo", {});
105
+ throwIfError("storageInfo", r);
106
+ const n = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0;
107
+ return {
108
+ freeBytes: n(r?.freeBytes),
109
+ totalBytes: n(r?.totalBytes),
110
+ appBytes: n(r?.appBytes),
111
+ source: r?.source === "estimate" ? "estimate" : "device"
112
+ };
113
+ }
114
+ var version = 3;
65
115
  export {
116
+ DEFAULT_DOWNLOAD_MAX_BYTES,
117
+ MAX_DOWNLOAD_BYTES,
118
+ download,
119
+ exportFile as export,
120
+ filesError,
121
+ isFilesError,
66
122
  list,
67
123
  readBase64,
68
124
  readText,
69
125
  remove,
70
126
  stat,
127
+ storageInfo,
71
128
  version,
72
129
  write
73
130
  };
@@ -24,9 +24,9 @@ var CAPABILITY_REGISTRY = {
24
24
  "calls": ["answer", "availability", "getPermissionStatus", "getState", "hangup", "listCalls", "offer", "receiveSignal", "reject", "requestPermission", "setMuted", "setRingback", "setSpeaker", "signalNext", "signalStart", "signalStop", "start", "subscribeEvents"],
25
25
  "camera": ["capture", "pickDocument", "prepareImage", "scanQR"],
26
26
  "clipboard": ["hasImage", "hasText", "readText", "writeImage", "writeText"],
27
- "contacts": ["getPermissionStatus", "pick", "requestPermission", "search"],
27
+ "contacts": ["add", "getPermissionStatus", "pick", "presentNewContact", "requestPermission", "search"],
28
28
  "file_picker": ["pick", "pickDirectoryForExport"],
29
- "files": ["list", "readText", "remove", "stat", "write"],
29
+ "files": ["download", "export", "list", "readText", "remove", "stat", "storageInfo", "write"],
30
30
  "godot": ["loadPack", "release", "send"],
31
31
  "haptic": ["play", "sequence", "vibrate"],
32
32
  "health": ["availability", "getPermissionStatus", "query", "requestPermission", "saveWorkout"],
@@ -24,9 +24,9 @@ var CAPABILITY_REGISTRY = {
24
24
  "calls": ["answer", "availability", "getPermissionStatus", "getState", "hangup", "listCalls", "offer", "receiveSignal", "reject", "requestPermission", "setMuted", "setRingback", "setSpeaker", "signalNext", "signalStart", "signalStop", "start", "subscribeEvents"],
25
25
  "camera": ["capture", "pickDocument", "prepareImage", "scanQR"],
26
26
  "clipboard": ["hasImage", "hasText", "readText", "writeImage", "writeText"],
27
- "contacts": ["getPermissionStatus", "pick", "requestPermission", "search"],
27
+ "contacts": ["add", "getPermissionStatus", "pick", "presentNewContact", "requestPermission", "search"],
28
28
  "file_picker": ["pick", "pickDirectoryForExport"],
29
- "files": ["list", "readText", "remove", "stat", "write"],
29
+ "files": ["download", "export", "list", "readText", "remove", "stat", "storageInfo", "write"],
30
30
  "godot": ["loadPack", "release", "send"],
31
31
  "haptic": ["play", "sequence", "vibrate"],
32
32
  "health": ["availability", "getPermissionStatus", "query", "requestPermission", "saveWorkout"],
@@ -24,9 +24,9 @@ var CAPABILITY_REGISTRY = {
24
24
  "calls": ["answer", "availability", "getPermissionStatus", "getState", "hangup", "listCalls", "offer", "receiveSignal", "reject", "requestPermission", "setMuted", "setRingback", "setSpeaker", "signalNext", "signalStart", "signalStop", "start", "subscribeEvents"],
25
25
  "camera": ["capture", "pickDocument", "prepareImage", "scanQR"],
26
26
  "clipboard": ["hasImage", "hasText", "readText", "writeImage", "writeText"],
27
- "contacts": ["getPermissionStatus", "pick", "requestPermission", "search"],
27
+ "contacts": ["add", "getPermissionStatus", "pick", "presentNewContact", "requestPermission", "search"],
28
28
  "file_picker": ["pick", "pickDirectoryForExport"],
29
- "files": ["list", "readText", "remove", "stat", "write"],
29
+ "files": ["download", "export", "list", "readText", "remove", "stat", "storageInfo", "write"],
30
30
  "godot": ["loadPack", "release", "send"],
31
31
  "haptic": ["play", "sequence", "vibrate"],
32
32
  "health": ["availability", "getPermissionStatus", "query", "requestPermission", "saveWorkout"],
@@ -9,9 +9,9 @@ var CAPABILITY_REGISTRY = {
9
9
  "calls": ["answer", "availability", "getPermissionStatus", "getState", "hangup", "listCalls", "offer", "receiveSignal", "reject", "requestPermission", "setMuted", "setRingback", "setSpeaker", "signalNext", "signalStart", "signalStop", "start", "subscribeEvents"],
10
10
  "camera": ["capture", "pickDocument", "prepareImage", "scanQR"],
11
11
  "clipboard": ["hasImage", "hasText", "readText", "writeImage", "writeText"],
12
- "contacts": ["getPermissionStatus", "pick", "requestPermission", "search"],
12
+ "contacts": ["add", "getPermissionStatus", "pick", "presentNewContact", "requestPermission", "search"],
13
13
  "file_picker": ["pick", "pickDirectoryForExport"],
14
- "files": ["list", "readText", "remove", "stat", "write"],
14
+ "files": ["download", "export", "list", "readText", "remove", "stat", "storageInfo", "write"],
15
15
  "godot": ["loadPack", "release", "send"],
16
16
  "haptic": ["play", "sequence", "vibrate"],
17
17
  "health": ["availability", "getPermissionStatus", "query", "requestPermission", "saveWorkout"],
@@ -24,9 +24,9 @@ var CAPABILITY_REGISTRY = {
24
24
  "calls": ["answer", "availability", "getPermissionStatus", "getState", "hangup", "listCalls", "offer", "receiveSignal", "reject", "requestPermission", "setMuted", "setRingback", "setSpeaker", "signalNext", "signalStart", "signalStop", "start", "subscribeEvents"],
25
25
  "camera": ["capture", "pickDocument", "prepareImage", "scanQR"],
26
26
  "clipboard": ["hasImage", "hasText", "readText", "writeImage", "writeText"],
27
- "contacts": ["getPermissionStatus", "pick", "requestPermission", "search"],
27
+ "contacts": ["add", "getPermissionStatus", "pick", "presentNewContact", "requestPermission", "search"],
28
28
  "file_picker": ["pick", "pickDirectoryForExport"],
29
- "files": ["list", "readText", "remove", "stat", "write"],
29
+ "files": ["download", "export", "list", "readText", "remove", "stat", "storageInfo", "write"],
30
30
  "godot": ["loadPack", "release", "send"],
31
31
  "haptic": ["play", "sequence", "vibrate"],
32
32
  "health": ["availability", "getPermissionStatus", "query", "requestPermission", "saveWorkout"],