@monimesl/dot-dash 0.1.0-alpha.8 → 1.0.0-beta.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.
package/dist/index.js CHANGED
@@ -1,113 +1,295 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { HostError, HostError as HostError$1, callHost, createHostRequest } from "@monimesl/dot-core";
2
+ import { DEFAULT_DASH_API_VERSION, HostError, HostError as DashError, callHost, createHostRequest, offersDashApiVersion } from "@monimesl/dot-core";
3
+ //#region src/api-versions.ts
4
+ const DASH_API_VERSION = "1.0";
5
+ const DASH_API_SINCE = {
6
+ "account.signIn": "1.0",
7
+ "contacts.pickPhone": "1.0",
8
+ "crypto.hmac": "1.0",
9
+ "crypto.randomBytes": "1.0",
10
+ "crypto.randomUUID": "1.0",
11
+ "crypto.sha256": "1.0",
12
+ "crypto.sha512": "1.0",
13
+ "dot.close": "1.0",
14
+ "media.pickFile": "1.0",
15
+ "media.pickPhoto": "1.0",
16
+ "media.scanCode": "1.0",
17
+ "network.http.download": "1.0",
18
+ "network.http.request": "1.0",
19
+ "network.http.upload": "1.0",
20
+ "network.status": "1.0",
21
+ "payments.get": "1.0",
22
+ "payments.request": "1.0",
23
+ "storage.cache.clear": "1.0",
24
+ "storage.cache.exists": "1.0",
25
+ "storage.cache.get": "1.0",
26
+ "storage.cache.keys": "1.0",
27
+ "storage.cache.remove": "1.0",
28
+ "storage.cache.set": "1.0",
29
+ "storage.file.copy": "1.0",
30
+ "storage.file.createFolder": "1.0",
31
+ "storage.file.list": "1.0",
32
+ "storage.file.move": "1.0",
33
+ "storage.file.open": "1.0",
34
+ "storage.file.read": "1.0",
35
+ "storage.file.remove": "1.0",
36
+ "storage.file.save": "1.0",
37
+ "storage.file.stat": "1.0",
38
+ "storage.file.usage": "1.0",
39
+ "storage.file.write": "1.0",
40
+ "storage.secret.clear": "1.0",
41
+ "storage.secret.get": "1.0",
42
+ "storage.secret.remove": "1.0",
43
+ "storage.secret.set": "1.0",
44
+ "system.brightness": "1.0",
45
+ "system.copyText": "1.0",
46
+ "system.delay": "1.0",
47
+ "system.dial": "1.0",
48
+ "system.info": "1.0",
49
+ "system.keepAwake": "1.0",
50
+ "system.openUrl": "1.0",
51
+ "system.share": "1.0",
52
+ "system.torch": "1.0",
53
+ "system.vibrate": "1.0",
54
+ "ui.alert": "1.0",
55
+ "ui.pickDate": "1.0",
56
+ "ui.pickTime": "1.0"
57
+ };
58
+ let hostApiVersions = [DEFAULT_DASH_API_VERSION];
59
+ let dotMinApiVersion;
60
+ const checkedFeatures = /* @__PURE__ */ new Set();
61
+ const warnedFeatures = /* @__PURE__ */ new Set();
62
+ function useHostDashApiVersions(versions) {
63
+ hostApiVersions = versions;
64
+ }
65
+ function warnAboveDashApiVersion(minApiVersion) {
66
+ dotMinApiVersion = minApiVersion;
67
+ }
68
+ /**
69
+ * Whether the Dash the dot runs in has `feature`: a call, such as "contacts.pickPhone", or a field
70
+ * added to a call later. Check it before using one that's newer than the dot's minApiVersion.
71
+ * @since 1.0
72
+ */
73
+ function supports(feature) {
74
+ checkedFeatures.add(feature);
75
+ const since = sinceOf(feature);
76
+ return since !== void 0 && offersDashApiVersion(hostApiVersions, since);
77
+ }
78
+ function warnIfNewerThanDot(method, params) {
79
+ if (dotMinApiVersion === void 0) return;
80
+ const callName = method.replace(/^dash\./, "");
81
+ for (const feature of [callName, ...fieldsSent(callName, params)]) {
82
+ const since = sinceOf(feature);
83
+ if (since === void 0) continue;
84
+ if (checkedFeatures.has(feature) || warnedFeatures.has(feature)) continue;
85
+ if (offersDashApiVersion([dotMinApiVersion], since)) continue;
86
+ warnedFeatures.add(feature);
87
+ console.warn(`dash.${feature} came in Dash API ${since}, but this dot's minApiVersion is ${dotMinApiVersion}, so older Dash doesn't have it. Raise "minApiVersion" in dot.json, or check dash.supports('${feature}') first.`);
88
+ }
89
+ }
90
+ function sinceOf(feature) {
91
+ const table = DASH_API_SINCE;
92
+ return Object.hasOwn(table, feature) ? table[feature] : void 0;
93
+ }
94
+ function fieldsSent(callName, params) {
95
+ if (typeof params !== "object" || params === null) return [];
96
+ return Object.keys(params).map((field) => `${callName}.${field}`).filter((feature) => sinceOf(feature) !== void 0);
97
+ }
98
+ //#endregion
3
99
  //#region src/call.ts
4
100
  function call(method, params, options = {}) {
101
+ warnIfNewerThanDot(method, params);
5
102
  return callHost(method, params, options);
6
103
  }
7
104
  //#endregion
8
- //#region src/file/index.ts
9
- var file_exports = /* @__PURE__ */ __exportAll({
10
- copy: () => copy,
11
- createFolder: () => createFolder,
12
- list: () => list,
13
- move: () => move,
14
- open: () => open,
15
- read: () => read,
16
- remove: () => remove,
17
- save: () => save,
18
- stat: () => stat,
19
- usage: () => usage,
20
- write: () => write
105
+ //#region src/account/index.ts
106
+ var account_exports = /* @__PURE__ */ __exportAll({ signIn: () => signIn });
107
+ function signIn(options, callOptions) {
108
+ const params = {
109
+ mode: options.mode,
110
+ nonce: options.nonce ?? ""
111
+ };
112
+ if (options.mode === "oidc" && options.scopes) params.scopes = options.scopes;
113
+ return call("dash.account.signIn", params, callOptions);
114
+ }
115
+ //#endregion
116
+ //#region src/contacts/pick-phone.ts
117
+ /**
118
+ * Shows the phone's contacts so the person chooses one phone number, such as who to send money to.
119
+ * Needs the `contacts` permission. The dot sees only the chosen contact. Rejects with `cancelled` if
120
+ * the person closes it.
121
+ * @since 1.0
122
+ */
123
+ function pickPhone(callOptions) {
124
+ return call("dash.contacts.pickPhone", {}, callOptions);
125
+ }
126
+ //#endregion
127
+ //#region src/contacts/index.ts
128
+ var contacts_exports = /* @__PURE__ */ __exportAll({ pickPhone: () => pickPhone });
129
+ //#endregion
130
+ //#region src/crypto/index.ts
131
+ var crypto_exports = /* @__PURE__ */ __exportAll({
132
+ hmac: () => hmac,
133
+ randomBytes: () => randomBytes,
134
+ randomUUID: () => randomUUID,
135
+ sha256: () => sha256,
136
+ sha512: () => sha512
21
137
  });
22
- function read(path, options = {}, callOptions) {
23
- return call("dash.file.read", {
24
- path,
25
- ...options
26
- }, callOptions);
138
+ /** @since 1.0 */
139
+ function sha256(data, options = {}, callOptions) {
140
+ return call("dash.crypto.sha256", wireInput(data, options), callOptions);
27
141
  }
28
- function write(path, data, options = {}, callOptions) {
29
- return call("dash.file.write", {
30
- path,
31
- data,
32
- ...options
33
- }, callOptions);
142
+ /** @since 1.0 */
143
+ function sha512(data, options = {}, callOptions) {
144
+ return call("dash.crypto.sha512", wireInput(data, options), callOptions);
34
145
  }
35
- function stat(path, callOptions) {
36
- return call("dash.file.stat", { path }, callOptions);
146
+ /**
147
+ * Signs `data` with `key`, as HMAC-SHA256 or HMAC-SHA512, such as
148
+ * `hmac({ algorithm: 'sha256', key, data })`.
149
+ * @since 1.0
150
+ */
151
+ function hmac(options, callOptions) {
152
+ const wireKey = valueAndEncoding(options.key);
153
+ return call("dash.crypto.hmac", {
154
+ ...wireInput(options.data, options),
155
+ algorithm: options.algorithm,
156
+ key: wireKey.value,
157
+ keyEncoding: wireKey.encoding
158
+ }, callOptions);
37
159
  }
38
- function list(path = "", options = {}, callOptions) {
39
- return call("dash.file.list", {
40
- path,
41
- ...options
160
+ /**
161
+ * `count` secure random bytes, from 1 to 1024, such as for a nonce.
162
+ * @since 1.0
163
+ */
164
+ function randomBytes(count, options = {}, callOptions) {
165
+ return call("dash.crypto.randomBytes", {
166
+ count,
167
+ as: outputEncoding(options)
42
168
  }, callOptions);
43
169
  }
44
- function createFolder(path, callOptions) {
45
- return call("dash.file.createFolder", { path }, callOptions);
170
+ /**
171
+ * A random version 4 UUID, such as "3f0e5c3a-6d1b-4c8e-9a2f-1b7d4e6a9c05".
172
+ * @since 1.0
173
+ */
174
+ function randomUUID(callOptions) {
175
+ return call("dash.crypto.randomUUID", {}, callOptions);
46
176
  }
47
- function move(from, to, options = {}, callOptions) {
48
- return call("dash.file.move", {
49
- from,
50
- to,
51
- ...options
52
- }, callOptions);
177
+ function wireInput(data, options) {
178
+ const { value, encoding } = valueAndEncoding(data);
179
+ return {
180
+ data: value,
181
+ dataEncoding: encoding,
182
+ as: outputEncoding(options)
183
+ };
53
184
  }
54
- function copy(from, to, options = {}, callOptions) {
55
- return call("dash.file.copy", {
56
- from,
57
- to,
58
- ...options
59
- }, callOptions);
185
+ function outputEncoding(options) {
186
+ return options.as ?? "hex";
60
187
  }
61
- function remove(path, options = {}, callOptions) {
62
- return call("dash.file.remove", {
63
- path,
64
- ...options
65
- }, callOptions);
188
+ function valueAndEncoding(input) {
189
+ if (typeof input === "string") return {
190
+ value: input,
191
+ encoding: "text"
192
+ };
193
+ return {
194
+ value: input.base64,
195
+ encoding: "base64"
196
+ };
66
197
  }
67
- function save(file, path, options = {}, callOptions) {
68
- return call("dash.file.save", {
69
- file: typeof file === "string" ? file : file.id,
70
- path,
71
- ...options
72
- }, callOptions);
198
+ //#endregion
199
+ //#region src/dot/index.ts
200
+ var dot_exports = /* @__PURE__ */ __exportAll({ close: () => close });
201
+ /**
202
+ * Closes the dot, as going back from its first page does. Calls still running are cancelled.
203
+ * @since 1.0
204
+ */
205
+ function close(callOptions) {
206
+ return call("dash.dot.close", {}, callOptions);
73
207
  }
74
- function open(path, callOptions) {
75
- return call("dash.file.open", { path }, callOptions);
208
+ //#endregion
209
+ //#region src/media/pick-file.ts
210
+ /**
211
+ * Lets the person choose a file from their phone, of at most 25 MB. Needs the `files` permission.
212
+ * Rejects with `cancelled` if they close the picker, and with `invalid` for a larger file. Like a
213
+ * download, it lasts until the dot closes, so `dash.storage.file.save` it to keep it.
214
+ * @since 1.0
215
+ */
216
+ function pickFile(options = {}, callOptions) {
217
+ const params = {};
218
+ if (options.types) params.types = options.types;
219
+ return call("dash.media.pickFile", params, callOptions);
76
220
  }
77
- function usage(callOptions) {
78
- return call("dash.file.usage", {}, callOptions);
221
+ //#endregion
222
+ //#region src/media/pick-photo.ts
223
+ /**
224
+ * Lets the person choose a photo or take one. Resolves with it as a JPEG of at most 2048 pixels on its
225
+ * longest side, without its location or other details. Rejects with `cancelled` if they close the
226
+ * picker, and with `unsupported` on a device without a camera. Save it with `dash.storage.file.save`
227
+ * to keep it after the dot closes.
228
+ * @since 1.0
229
+ */
230
+ function pickPhoto(options, callOptions) {
231
+ return call("dash.media.pickPhoto", { source: options.source }, callOptions);
232
+ }
233
+ //#endregion
234
+ //#region src/media/scan-code.ts
235
+ /**
236
+ * Opens Dash's scanner until it reads a QR code. Needs the `camera` permission. Rejects with
237
+ * `cancelled` if the person closes it, and with `unsupported` where Dash has no scanner.
238
+ * @since 1.0
239
+ */
240
+ function scanCode(callOptions) {
241
+ return call("dash.media.scanCode", {}, callOptions);
79
242
  }
80
243
  //#endregion
244
+ //#region src/media/index.ts
245
+ var media_exports = /* @__PURE__ */ __exportAll({
246
+ pickFile: () => pickFile,
247
+ pickPhoto: () => pickPhoto,
248
+ scanCode: () => scanCode
249
+ });
250
+ //#endregion
81
251
  //#region src/network/http.ts
82
252
  var http_exports = /* @__PURE__ */ __exportAll({
83
253
  download: () => download,
84
- request: () => request,
254
+ request: () => request$1,
85
255
  upload: () => upload
86
256
  });
87
- function request(options, callOptions) {
88
- const { as, ...request } = options;
89
- return readBody(call("dash.network.http.request", wireRequest(request, as), callOptions), as);
257
+ /**
258
+ * Like fetch, resolves with any response, even a 404. An object body is sent as JSON.
259
+ * @since 1.0
260
+ */
261
+ function request$1(options, callOptions) {
262
+ const { as: readAs, ...request } = options;
263
+ return readBody(call("dash.network.http.request", wireRequest(request, readAs), callOptions), readAs);
90
264
  }
265
+ /**
266
+ * Saves a 2xx response's body, up to 256 MB, to a temporary file.
267
+ * @since 1.0
268
+ */
91
269
  function download(options, callOptions) {
92
270
  return call("dash.network.http.download", wireRequest(options), callOptions);
93
271
  }
272
+ /**
273
+ * Sends the dot's files as a multipart form.
274
+ * @since 1.0
275
+ */
94
276
  function upload(options, callOptions) {
95
- const { as, ...request } = options;
96
- const files = request.files.map(({ file, ...rest }) => ({
97
- ...rest,
98
- file: typeof file === "string" ? file : file.id
277
+ const { as: readAs, ...request } = options;
278
+ const files = request.files.map((upload) => ({
279
+ ...upload,
280
+ file: upload.file.id
99
281
  }));
100
282
  const params = {
101
283
  ...request,
102
284
  files
103
285
  };
104
- if (as === "json") params.headers = withDefaultHeader(request.headers, "Accept", "application/json");
105
- return readBody(call("dash.network.http.upload", params, callOptions), as);
286
+ if (readAs === "json") params.headers = withDefaultHeader(request.headers, "Accept", "application/json");
287
+ return readBody(call("dash.network.http.upload", params, callOptions), readAs);
106
288
  }
107
- function wireRequest(request, as) {
289
+ function wireRequest(request, readAs) {
108
290
  let headers = request.headers;
109
291
  let body;
110
- if (as === "json") headers = withDefaultHeader(headers, "Accept", "application/json");
292
+ if (readAs === "json") headers = withDefaultHeader(headers, "Accept", "application/json");
111
293
  if (typeof request.body === "string") body = request.body;
112
294
  else if (request.body !== void 0) {
113
295
  headers = withDefaultHeader(headers, "Content-Type", "application/json");
@@ -120,15 +302,16 @@ function wireRequest(request, as) {
120
302
  };
121
303
  }
122
304
  function withDefaultHeader(headers, name, value) {
305
+ if (headers === void 0) return { [name]: value };
123
306
  const lowerName = name.toLowerCase();
124
- if (headers && Object.keys(headers).some((existing) => existing.toLowerCase() === lowerName)) return headers;
307
+ if (Object.keys(headers).some((existing) => existing.toLowerCase() === lowerName)) return headers;
125
308
  return {
126
309
  ...headers,
127
310
  [name]: value
128
311
  };
129
312
  }
130
- function readBody(response, as) {
131
- if (as !== "json") return response;
313
+ function readBody(response, readAs) {
314
+ if (readAs !== "json") return response;
132
315
  const parsed = response.then((result) => ({
133
316
  ...result,
134
317
  body: parseJson(result)
@@ -140,54 +323,356 @@ function parseJson({ status, statusText, body }) {
140
323
  return JSON.parse(body);
141
324
  } catch (error) {
142
325
  const message = error instanceof Error ? error.message : String(error);
143
- throw new HostError$1("failed", `The response (${status} ${statusText}) isn't JSON: ${message}`);
326
+ throw new HostError("failed", `The response (${status} ${statusText}) isn't JSON: ${message}`);
144
327
  }
145
328
  }
146
329
  //#endregion
330
+ //#region src/network/status.ts
331
+ /**
332
+ * Whether the phone is online now, and how. It can still be online without reaching a server.
333
+ * @since 1.0
334
+ */
335
+ function status(callOptions) {
336
+ return call("dash.network.status", {}, callOptions);
337
+ }
338
+ //#endregion
147
339
  //#region src/network/index.ts
148
- var network_exports = /* @__PURE__ */ __exportAll({ http: () => http_exports });
340
+ var network_exports = /* @__PURE__ */ __exportAll({
341
+ http: () => http_exports,
342
+ status: () => status
343
+ });
344
+ //#endregion
345
+ //#region src/payments/payments.ts
346
+ /**
347
+ * Opens Dash's payment sheet for `order`, where the person picks one of `methods` and pays the
348
+ * Space that owns the dot. Needs `payments` in dot.json's permissions. Monime checks the request
349
+ * before the sheet opens: an order it won't take, such as one below its smallest amount, rejects
350
+ * with `invalid`, a dot or Space it won't take payments for rejects with `denied`, and `failed`
351
+ * means Dash couldn't reach Monime. If a method fails, the sheet stays open for the person to try
352
+ * again or pick another. Once they pick a method, it resolves with how the payment ended:
353
+ * `completed`, `pending` while it's still on its way, `expired`, or `cancelled` if the person
354
+ * closed the sheet. Before that, it rejects with `cancelled` if the person closes the sheet, or
355
+ * `timeout` if Dash's time runs out. A payment already under way carries on after `cancel()`: read
356
+ * it later with `get`.
357
+ * @since 1.0
358
+ */
359
+ function request(options, callOptions) {
360
+ const withoutTimeout = { ...callOptions };
361
+ delete withoutTimeout.timeout;
362
+ return call("dash.payments.request", options, withoutTimeout);
363
+ }
364
+ /**
365
+ * The payment for the order with this reference, such as to check a pending one later, or after
366
+ * the dot opens again. Undefined if this dot never asked for it, or the person never picked a
367
+ * method. Needs `payments` in dot.json's permissions.
368
+ * @since 1.0
369
+ */
370
+ function get$2(reference, callOptions) {
371
+ const response = call("dash.payments.get", { reference }, callOptions);
372
+ const found = response.then((result) => result ?? void 0);
373
+ return createHostRequest(found, () => response.cancel());
374
+ }
375
+ //#endregion
376
+ //#region src/payments/index.ts
377
+ var payments_exports = /* @__PURE__ */ __exportAll({
378
+ get: () => get$2,
379
+ request: () => request
380
+ });
381
+ //#endregion
382
+ //#region src/storage/cache.ts
383
+ var cache_exports = /* @__PURE__ */ __exportAll({
384
+ clear: () => clear$1,
385
+ exists: () => exists,
386
+ get: () => get$1,
387
+ keys: () => keys,
388
+ remove: () => remove$2,
389
+ set: () => set$1
390
+ });
391
+ /**
392
+ * Resolves with null for a missing or expired key.
393
+ * @since 1.0
394
+ */
395
+ function get$1(key, callOptions) {
396
+ return call("dash.storage.cache.get", { key }, callOptions);
397
+ }
398
+ /** @since 1.0 */
399
+ function set$1(key, value, options = {}, callOptions) {
400
+ return call("dash.storage.cache.set", {
401
+ key,
402
+ value,
403
+ ...options
404
+ }, callOptions);
405
+ }
406
+ /** @since 1.0 */
407
+ function remove$2(key, callOptions) {
408
+ return call("dash.storage.cache.remove", { key }, callOptions);
409
+ }
410
+ /** @since 1.0 */
411
+ function exists(key, callOptions) {
412
+ return call("dash.storage.cache.exists", { key }, callOptions);
413
+ }
414
+ /**
415
+ * The keys, sorted.
416
+ * @since 1.0
417
+ */
418
+ function keys(callOptions) {
419
+ return call("dash.storage.cache.keys", {}, callOptions);
420
+ }
421
+ /** @since 1.0 */
422
+ function clear$1(callOptions) {
423
+ return call("dash.storage.cache.clear", {}, callOptions);
424
+ }
425
+ //#endregion
426
+ //#region src/storage/file.ts
427
+ var file_exports = /* @__PURE__ */ __exportAll({
428
+ copy: () => copy,
429
+ createFolder: () => createFolder,
430
+ list: () => list,
431
+ move: () => move,
432
+ open: () => open,
433
+ read: () => read,
434
+ remove: () => remove$1,
435
+ save: () => save,
436
+ stat: () => stat,
437
+ usage: () => usage,
438
+ write: () => write
439
+ });
440
+ /**
441
+ * Reads a file of up to 5 MB.
442
+ * @since 1.0
443
+ */
444
+ function read(path, options = {}, callOptions) {
445
+ return call("dash.storage.file.read", {
446
+ path,
447
+ ...options
448
+ }, callOptions);
449
+ }
450
+ /**
451
+ * Creates any missing folders above the file.
452
+ * @since 1.0
453
+ */
454
+ function write(path, data, options = {}, callOptions) {
455
+ return call("dash.storage.file.write", {
456
+ path,
457
+ data,
458
+ ...options
459
+ }, callOptions);
460
+ }
461
+ /** @since 1.0 */
462
+ function stat(path, callOptions) {
463
+ return call("dash.storage.file.stat", { path }, callOptions);
464
+ }
465
+ /**
466
+ * Sorted by path.
467
+ * @since 1.0
468
+ */
469
+ function list(path = "", options = {}, callOptions) {
470
+ return call("dash.storage.file.list", {
471
+ path,
472
+ ...options
473
+ }, callOptions);
474
+ }
475
+ /** @since 1.0 */
476
+ function createFolder(path, callOptions) {
477
+ return call("dash.storage.file.createFolder", { path }, callOptions);
478
+ }
479
+ /** @since 1.0 */
480
+ function move(from, to, options = {}, callOptions) {
481
+ return call("dash.storage.file.move", {
482
+ from,
483
+ to,
484
+ ...options
485
+ }, callOptions);
486
+ }
487
+ /** @since 1.0 */
488
+ function copy(from, to, options = {}, callOptions) {
489
+ return call("dash.storage.file.copy", {
490
+ from,
491
+ to,
492
+ ...options
493
+ }, callOptions);
494
+ }
495
+ /**
496
+ * Removing a folder that isn't empty needs `recursive`.
497
+ * @since 1.0
498
+ */
499
+ function remove$1(path, options = {}, callOptions) {
500
+ return call("dash.storage.file.remove", {
501
+ path,
502
+ ...options
503
+ }, callOptions);
504
+ }
505
+ /**
506
+ * Keeps a temporary file, such as a download, at `path`.
507
+ * @since 1.0
508
+ */
509
+ function save(file, path, options = {}, callOptions) {
510
+ return call("dash.storage.file.save", {
511
+ file: file.id,
512
+ path,
513
+ ...options
514
+ }, callOptions);
515
+ }
516
+ /**
517
+ * A stored file as a DotFile, to show with its `url` or send with `http.upload`.
518
+ * @since 1.0
519
+ */
520
+ function open(path, callOptions) {
521
+ return call("dash.storage.file.open", { path }, callOptions);
522
+ }
523
+ /** @since 1.0 */
524
+ function usage(callOptions) {
525
+ return call("dash.storage.file.usage", {}, callOptions);
526
+ }
527
+ //#endregion
528
+ //#region src/storage/secret.ts
529
+ var secret_exports = /* @__PURE__ */ __exportAll({
530
+ clear: () => clear,
531
+ get: () => get,
532
+ remove: () => remove,
533
+ set: () => set
534
+ });
535
+ /** @since 1.0 */
536
+ function get(key, callOptions) {
537
+ return call("dash.storage.secret.get", { key }, callOptions);
538
+ }
539
+ /** @since 1.0 */
540
+ function set(key, value, callOptions) {
541
+ return call("dash.storage.secret.set", {
542
+ key,
543
+ value
544
+ }, callOptions);
545
+ }
546
+ /** @since 1.0 */
547
+ function remove(key, callOptions) {
548
+ return call("dash.storage.secret.remove", { key }, callOptions);
549
+ }
550
+ /** @since 1.0 */
551
+ function clear(callOptions) {
552
+ return call("dash.storage.secret.clear", {}, callOptions);
553
+ }
554
+ //#endregion
555
+ //#region src/storage/index.ts
556
+ var storage_exports = /* @__PURE__ */ __exportAll({
557
+ cache: () => cache_exports,
558
+ file: () => file_exports,
559
+ secret: () => secret_exports
560
+ });
149
561
  //#endregion
150
562
  //#region src/system/clipboard.ts
563
+ /** @since 1.0 */
151
564
  function copyText(text, callOptions) {
152
565
  return call("dash.system.copyText", { text }, callOptions);
153
566
  }
154
567
  //#endregion
155
568
  //#region src/system/delay.ts
569
+ /**
570
+ * Resolves after `milliseconds`. The host keeps the time, since a dot's logic has no timers.
571
+ * @since 1.0
572
+ */
156
573
  function delay(milliseconds, callOptions) {
157
574
  return call("dash.system.delay", { milliseconds }, callOptions);
158
575
  }
159
576
  //#endregion
577
+ //#region src/system/dial.ts
578
+ /**
579
+ * Opens the phone's dialer with `number` ready, such as "+232 76 123456" or a USSD code such as
580
+ * "*715#". The person taps Call. Spaces and dashes are fine; anything else but digits, +, * and #
581
+ * rejects with `invalid`. For a code, Dash asks the person first, and rejects with `cancelled` if
582
+ * they say no. Codes that set up call forwarding, such as "**21*…#", reject with `denied`.
583
+ * @since 1.0
584
+ */
585
+ function dial(number, callOptions) {
586
+ return call("dash.system.dial", { number }, callOptions);
587
+ }
588
+ //#endregion
160
589
  //#region src/system/haptics.ts
590
+ /**
591
+ * Plays a short vibration. Resolves once it's done.
592
+ * @since 1.0
593
+ */
161
594
  function vibrate(options = {}, callOptions) {
162
595
  return call("dash.system.vibrate", { style: options.style ?? "medium" }, callOptions);
163
596
  }
164
597
  //#endregion
598
+ //#region src/system/info.ts
599
+ /**
600
+ * The phone and the Dash the dot runs in. It has nothing that identifies the person or the phone.
601
+ * @since 1.0
602
+ */
603
+ function info(callOptions) {
604
+ return call("dash.system.info", {}, callOptions);
605
+ }
606
+ //#endregion
607
+ //#region src/system/open-url.ts
608
+ /**
609
+ * Opens an https address in the device's browser. Any other address rejects with `invalid`.
610
+ * @since 1.0
611
+ */
612
+ function openUrl(url, callOptions) {
613
+ return call("dash.system.openUrl", { url }, callOptions);
614
+ }
615
+ //#endregion
616
+ //#region src/system/screen.ts
617
+ /**
618
+ * Keeps the screen from dimming and locking while the dot shows, such as while a ticket's QR code is
619
+ * up. Dash undoes it when the dot closes.
620
+ * @since 1.0
621
+ */
622
+ function keepAwake(on, callOptions) {
623
+ return call("dash.system.keepAwake", { on }, callOptions);
624
+ }
625
+ /**
626
+ * `max` makes the screen as bright as it goes, so a scanner can read a QR code on it, and `normal`
627
+ * puts it back. Dash puts it back too when the dot closes or the person leaves the app.
628
+ * @since 1.0
629
+ */
630
+ function brightness(level, callOptions) {
631
+ return call("dash.system.brightness", { level }, callOptions);
632
+ }
633
+ //#endregion
165
634
  //#region src/system/share.ts
635
+ /**
636
+ * Opens the device's share sheet. Resolves once it shows.
637
+ * @since 1.0
638
+ */
166
639
  function share(options, callOptions) {
167
- const { file, ...rest } = options;
168
- if (file === void 0) return call("dash.system.share", rest, callOptions);
169
- const id = typeof file === "string" ? file : file.id;
640
+ const { file, ...content } = options;
641
+ if (file === void 0) return call("dash.system.share", content, callOptions);
170
642
  return call("dash.system.share", {
171
- ...rest,
172
- file: id
643
+ ...content,
644
+ file: file.id
173
645
  }, callOptions);
174
646
  }
175
647
  //#endregion
176
648
  //#region src/system/torch.ts
177
- function torch(state, callOptions) {
178
- return call("dash.system.torch", { state }, callOptions);
649
+ /**
650
+ * Turns the torch on or off. Rejects with `unsupported` on a device without one.
651
+ * @since 1.0
652
+ */
653
+ function torch(on, callOptions) {
654
+ return call("dash.system.torch", { on }, callOptions);
179
655
  }
180
656
  //#endregion
181
657
  //#region src/system/index.ts
182
658
  var system_exports = /* @__PURE__ */ __exportAll({
659
+ brightness: () => brightness,
183
660
  copyText: () => copyText,
184
661
  delay: () => delay,
662
+ dial: () => dial,
663
+ info: () => info,
664
+ keepAwake: () => keepAwake,
665
+ openUrl: () => openUrl,
185
666
  share: () => share,
186
667
  torch: () => torch,
187
668
  vibrate: () => vibrate
188
669
  });
189
670
  //#endregion
190
671
  //#region src/ui/dialogs.ts
672
+ /**
673
+ * Shows a native alert. Resolves with the action the person chose.
674
+ * @since 1.0
675
+ */
191
676
  function alert(options, callOptions) {
192
677
  const actions = options.actions ?? [{
193
678
  id: "ok",
@@ -199,31 +684,234 @@ function alert(options, callOptions) {
199
684
  }, callOptions);
200
685
  }
201
686
  //#endregion
687
+ //#region src/ui/pickers.ts
688
+ /**
689
+ * Opens the phone's own calendar. Resolves with the chosen date, such as "2026-10-24", or rejects
690
+ * with `cancelled` if the person closes it.
691
+ * @since 1.0
692
+ */
693
+ function pickDate(options = {}, callOptions) {
694
+ return call("dash.ui.pickDate", options, callOptions);
695
+ }
696
+ /**
697
+ * Opens the phone's own clock. Resolves with the chosen time in 24 hours, such as "19:30", or
698
+ * rejects with `cancelled` if the person closes it.
699
+ * @since 1.0
700
+ */
701
+ function pickTime(options = {}, callOptions) {
702
+ return call("dash.ui.pickTime", options, callOptions);
703
+ }
704
+ //#endregion
202
705
  //#region src/ui/index.ts
203
- var ui_exports = /* @__PURE__ */ __exportAll({ alert: () => alert });
706
+ var ui_exports = /* @__PURE__ */ __exportAll({
707
+ alert: () => alert,
708
+ pickDate: () => pickDate,
709
+ pickTime: () => pickTime
710
+ });
711
+ //#endregion
712
+ //#region src/utils/encoding.ts
713
+ const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
714
+ const HEX_PATTERN = /^(?:[0-9a-fA-F]{2})*$/;
715
+ const base64 = {
716
+ encode(text) {
717
+ return bytesToBase64(utf8Bytes(text));
718
+ },
719
+ decode(encoded) {
720
+ return utf8Text(base64ToBytes(encoded));
721
+ }
722
+ };
723
+ const hex = {
724
+ encode(text) {
725
+ return utf8Bytes(text).map((byte) => byte.toString(16).padStart(2, "0")).join("");
726
+ },
727
+ decode(encoded) {
728
+ if (!HEX_PATTERN.test(encoded)) throw new Error(`"${encoded.slice(0, 20)}" isn't hex. Use pairs of 0-9 and a-f.`);
729
+ const bytes = [];
730
+ for (let index = 0; index < encoded.length; index += 2) bytes.push(parseInt(encoded.slice(index, index + 2), 16));
731
+ return utf8Text(bytes);
732
+ }
733
+ };
734
+ function bytesToBase64(bytes) {
735
+ let encoded = "";
736
+ for (let index = 0; index < bytes.length; index += 3) {
737
+ const group = bytes.slice(index, index + 3);
738
+ const bits = (group[0] ?? 0) << 16 | (group[1] ?? 0) << 8 | (group[2] ?? 0);
739
+ const sextets = [
740
+ bits >> 18 & 63,
741
+ bits >> 12 & 63,
742
+ bits >> 6 & 63,
743
+ bits & 63
744
+ ];
745
+ const characterCount = group.length + 1;
746
+ for (const sextet of sextets.slice(0, characterCount)) encoded += BASE64_ALPHABET[sextet];
747
+ encoded += "=".repeat(4 - characterCount);
748
+ }
749
+ return encoded;
750
+ }
751
+ function base64ToBytes(encoded) {
752
+ if (!isBase64(encoded)) throw new Error(`"${encoded.slice(0, 20)}" isn't base64.`);
753
+ const bytes = [];
754
+ for (let index = 0; index < encoded.length; index += 4) {
755
+ const characters = encoded.slice(index, index + 4);
756
+ let bits = 0;
757
+ let padding = 0;
758
+ for (const character of characters) {
759
+ bits <<= 6;
760
+ if (character === "=") padding += 1;
761
+ else bits |= BASE64_ALPHABET.indexOf(character);
762
+ }
763
+ bytes.push(bits >> 16 & 255);
764
+ if (padding < 2) bytes.push(bits >> 8 & 255);
765
+ if (padding < 1) bytes.push(bits & 255);
766
+ }
767
+ return bytes;
768
+ }
769
+ function isBase64(encoded) {
770
+ if (encoded.length % 4 !== 0) return false;
771
+ let padding = 0;
772
+ if (encoded.endsWith("==")) padding = 2;
773
+ else if (encoded.endsWith("=")) padding = 1;
774
+ for (let index = 0; index < encoded.length - padding; index += 1) if (BASE64_ALPHABET.indexOf(encoded[index]) === -1) return false;
775
+ return true;
776
+ }
777
+ function utf8Bytes(text) {
778
+ const bytes = [];
779
+ for (const character of text) {
780
+ const codePoint = character.codePointAt(0);
781
+ if (codePoint < 128) bytes.push(codePoint);
782
+ else if (codePoint < 2048) bytes.push(192 | codePoint >> 6, 128 | codePoint & 63);
783
+ else if (codePoint < 65536) bytes.push(224 | codePoint >> 12, 128 | codePoint >> 6 & 63, 128 | codePoint & 63);
784
+ else bytes.push(240 | codePoint >> 18, 128 | codePoint >> 12 & 63, 128 | codePoint >> 6 & 63, 128 | codePoint & 63);
785
+ }
786
+ return bytes;
787
+ }
788
+ const MAX_CODE_POINT = 1114111;
789
+ const MIN_CODE_POINT_FOR_LENGTH = [
790
+ 0,
791
+ 0,
792
+ 128,
793
+ 2048,
794
+ 65536
795
+ ];
796
+ function utf8Text(bytes) {
797
+ let text = "";
798
+ let index = 0;
799
+ while (index < bytes.length) {
800
+ const first = bytes[index];
801
+ const length = sequenceLength(first);
802
+ const continuation = bytes.slice(index + 1, index + length);
803
+ if (length === 0 || !isCompleteContinuation(continuation, length)) {
804
+ text += "�";
805
+ index += 1;
806
+ continue;
807
+ }
808
+ let codePoint = firstByteBits(first, length);
809
+ for (const byte of continuation) codePoint = codePoint << 6 | byte & 63;
810
+ const isPastLastCharacter = codePoint > MAX_CODE_POINT;
811
+ const isSpelledTooLong = codePoint < MIN_CODE_POINT_FOR_LENGTH[length];
812
+ if (isPastLastCharacter || isSpelledTooLong || codePoint >= 55296 && codePoint <= 57343) {
813
+ text += "�";
814
+ index += 1;
815
+ continue;
816
+ }
817
+ text += String.fromCodePoint(codePoint);
818
+ index += length;
819
+ }
820
+ return text;
821
+ }
822
+ function sequenceLength(first) {
823
+ if (first < 128) return 1;
824
+ if (first >= 240) return 4;
825
+ if (first >= 224) return 3;
826
+ if (first >= 192) return 2;
827
+ return 0;
828
+ }
829
+ function isCompleteContinuation(continuation, length) {
830
+ if (continuation.length !== length - 1) return false;
831
+ return continuation.every((byte) => (byte & 192) === 128);
832
+ }
833
+ function firstByteBits(first, length) {
834
+ if (length === 1) return first;
835
+ return first & 255 >> length + 1;
836
+ }
837
+ //#endregion
838
+ //#region src/utils/index.ts
839
+ var utils_exports = /* @__PURE__ */ __exportAll({
840
+ base64: () => base64,
841
+ hex: () => hex
842
+ });
204
843
  //#endregion
205
844
  //#region src/tracked.ts
206
845
  function tracked(group, track) {
207
846
  const copy = {};
208
847
  for (const [name, member] of Object.entries(group)) if (typeof member === "function") {
209
- const start = member;
848
+ const startCall = member;
210
849
  copy[name] = (...args) => {
211
- const request = start(...args);
212
- track(request);
213
- return request;
850
+ const result = startCall(...args);
851
+ if (result instanceof Promise) track(result);
852
+ return result;
214
853
  };
215
854
  } else copy[name] = tracked(member, track);
216
855
  return copy;
217
856
  }
218
857
  //#endregion
858
+ //#region src/errors.ts
859
+ function isDashError(error, ...codes) {
860
+ if (error instanceof HostError) return codes.length === 0 || codes.includes(error.code);
861
+ return false;
862
+ }
863
+ function errorMessage(error) {
864
+ if (error instanceof Error) return error.message;
865
+ if (typeof error === "string" && error) return error;
866
+ return "Something went wrong. Try again.";
867
+ }
868
+ async function unlessCancelled(call) {
869
+ try {
870
+ return await call;
871
+ } catch (error) {
872
+ if (isDashError(error, "cancelled")) return;
873
+ throw error;
874
+ }
875
+ }
876
+ //#endregion
877
+ //#region src/latest.ts
878
+ function latest() {
879
+ let running;
880
+ return (call) => {
881
+ running?.cancel();
882
+ running = call;
883
+ return call;
884
+ };
885
+ }
886
+ function debounce(milliseconds) {
887
+ const wait = latest();
888
+ return async (dash) => {
889
+ try {
890
+ await wait(dash.system.delay(milliseconds));
891
+ return true;
892
+ } catch (error) {
893
+ if (isDashError(error, "cancelled")) return false;
894
+ throw error;
895
+ }
896
+ };
897
+ }
898
+ //#endregion
219
899
  //#region src/index.ts
220
900
  const dash = {
221
901
  ui: ui_exports,
222
902
  system: system_exports,
223
903
  network: network_exports,
224
- file: file_exports
904
+ storage: storage_exports,
905
+ media: media_exports,
906
+ contacts: contacts_exports,
907
+ dot: dot_exports,
908
+ account: account_exports,
909
+ payments: payments_exports,
910
+ crypto: crypto_exports,
911
+ utils: utils_exports,
912
+ supports
225
913
  };
226
914
  //#endregion
227
- export { HostError, dash, tracked };
915
+ export { DASH_API_SINCE, DASH_API_VERSION, DashError, dash, debounce, errorMessage, isDashError, latest, tracked, unlessCancelled, useHostDashApiVersions, warnAboveDashApiVersion };
228
916
 
229
917
  //# sourceMappingURL=index.js.map