@domino-sdk/relay 1.24.0 → 1.29.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.
Files changed (57) hide show
  1. package/dist/{accounts-B31dWEYD.d.ts → accounts-DRPENuZe.d.ts} +1 -1
  2. package/dist/{attempt-captures-JDIgz2lY.d.ts → attempt-captures-BPMbTDYG.d.ts} +2 -2
  3. package/dist/authoring.d.ts +56 -56
  4. package/dist/authoring.js +7 -7
  5. package/dist/{automatic-outcomes-T9lEIvTj.d.ts → automatic-outcomes-CGAxTwEj.d.ts} +2 -2
  6. package/dist/avatar.d.ts +2 -2
  7. package/dist/browser.d.ts +10 -10
  8. package/dist/browser.js +10 -10
  9. package/dist/{capability-error-oTd3eYFH.d.ts → capability-error-CvSZaif2.d.ts} +1 -1
  10. package/dist/{chunk-B4EZ76WX.js → chunk-22QERRAG.js} +3 -3
  11. package/dist/{chunk-MC6V6VMG.js → chunk-4OC7REYX.js} +1 -1
  12. package/dist/{chunk-MO2FASX4.js → chunk-5FQ2BNG2.js} +1 -1
  13. package/dist/{chunk-UCZGXYCN.js → chunk-6YLULKQM.js} +10 -10
  14. package/dist/{chunk-6UFXPKLA.js → chunk-BVEWY2ZL.js} +99 -39
  15. package/dist/{chunk-MRU7GX2V.js → chunk-C3KL5SGM.js} +21 -8
  16. package/dist/{chunk-WOGCRDFT.js → chunk-CCTZIHWN.js} +1 -1
  17. package/dist/{chunk-G75JVB4D.js → chunk-DDA7G23P.js} +3 -3
  18. package/dist/{chunk-USVER53M.js → chunk-DP4YWFMY.js} +7 -0
  19. package/dist/{chunk-W33PYWYQ.js → chunk-JNBNL7FE.js} +2 -2
  20. package/dist/{chunk-AXQYGVOP.js → chunk-LPURBE7D.js} +1 -1
  21. package/dist/{chunk-GB26FIZT.js → chunk-MNL5NSC4.js} +8 -8
  22. package/dist/{chunk-JUQ2GNMD.js → chunk-PNCLJOUD.js} +2 -2
  23. package/dist/{chunk-NXWPYO6U.js → chunk-V4FGPIR3.js} +47 -16
  24. package/dist/{chunk-VT7355QN.js → chunk-VP63H7IE.js} +2 -2
  25. package/dist/{chunk-ENBQ6UEE.js → chunk-W2HGXOZ5.js} +3 -3
  26. package/dist/{chunk-E7MVYASS.js → chunk-W7PDXGYB.js} +1 -1
  27. package/dist/{chunk-PLELWE5L.js → chunk-ZLYVOLU2.js} +595 -147
  28. package/dist/{client-HG0uMG0Q.d.ts → client-D4WMWeWC.d.ts} +123 -58
  29. package/dist/{deployments-BFl_dOgc.d.ts → deployments-r5RxhZ9x.d.ts} +110 -110
  30. package/dist/{groups-CfyRewkJ.d.ts → groups-BNUhmxYY.d.ts} +26 -3
  31. package/dist/{images-DwNmzqWv.d.ts → images-PPPTL2Ct.d.ts} +2 -2
  32. package/dist/index.d.ts +13 -13
  33. package/dist/index.js +12 -10
  34. package/dist/{integrations-BMNtARU_.d.ts → integrations-CllF-kWg.d.ts} +1 -1
  35. package/dist/internal.d.ts +10 -10
  36. package/dist/internal.js +9 -9
  37. package/dist/{offers-CEwkAGsB.d.ts → offers-CdB5jnRl.d.ts} +3 -3
  38. package/dist/{pages-D1xrbGJl.d.ts → pages-BUmWkUu2.d.ts} +17 -3
  39. package/dist/{participant-activity-DYdUUhw2.d.ts → participant-activity-BPZiW7m7.d.ts} +1 -1
  40. package/dist/{participant-rewards-3duLb8YJ.d.ts → participant-rewards-BcWkBRn_.d.ts} +1 -1
  41. package/dist/portal-proxy.js +4 -4
  42. package/dist/{profiles-D29SS12z.d.ts → profiles-BHuQdzsW.d.ts} +1 -1
  43. package/dist/react/index.d.ts +13 -13
  44. package/dist/react/index.js +16 -15
  45. package/dist/{schema-D57mhL2q.d.ts → schema-lmOnv5C5.d.ts} +164 -2
  46. package/dist/schema.d.ts +380 -381
  47. package/dist/schema.js +22 -8
  48. package/dist/server/index.d.ts +10 -10
  49. package/dist/server/index.js +978 -723
  50. package/dist/{streaks-CWyq8Px5.d.ts → streaks-0YuipsUQ.d.ts} +1 -1
  51. package/dist/{submission-input-DjXg0o-r.d.ts → submission-input-DdvTz7ac.d.ts} +3 -3
  52. package/dist/{telegram-browser-DaP0Rawg.d.ts → telegram-browser-SK3KxXBL.d.ts} +13 -5
  53. package/dist/testing.d.ts +8 -8
  54. package/dist/testing.js +15 -15
  55. package/dist/{transport-M8ouWh7j.d.ts → transport-BNdKQ_m4.d.ts} +6150 -3299
  56. package/dist/{wallet-CDyo-U4n.d.ts → wallet-t7uNDg9p.d.ts} +1 -1
  57. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createRelayAction,
3
3
  withActionId
4
- } from "./chunk-E7MVYASS.js";
4
+ } from "./chunk-W7PDXGYB.js";
5
5
  import {
6
6
  collectionPassSchema,
7
7
  participantActivityQuerySchema,
@@ -16,7 +16,7 @@ import {
16
16
  questOutcomeQuerySchema,
17
17
  questOutcomeSchema,
18
18
  rewardDeliveriesSchema
19
- } from "./chunk-GB26FIZT.js";
19
+ } from "./chunk-MNL5NSC4.js";
20
20
  import {
21
21
  AVATAR_UPLOAD_LIMIT,
22
22
  avatarUploadTypes,
@@ -45,29 +45,29 @@ import {
45
45
  referralDefinitionSchema,
46
46
  referralHistorySchema,
47
47
  referralSummarySchema
48
- } from "./chunk-MRU7GX2V.js";
48
+ } from "./chunk-C3KL5SGM.js";
49
49
  import {
50
50
  telegramAccountConnectionSchema,
51
51
  telegramMiniAppSignInSchema,
52
52
  telegramSignInMethodsSchema
53
- } from "./chunk-W33PYWYQ.js";
53
+ } from "./chunk-JNBNL7FE.js";
54
54
  import {
55
55
  PARTICIPANT_TIMEOUT,
56
56
  lifetime,
57
57
  retryAfter,
58
58
  retrying,
59
59
  validTimeout
60
- } from "./chunk-WOGCRDFT.js";
60
+ } from "./chunk-CCTZIHWN.js";
61
61
  import {
62
62
  clientIdentity
63
- } from "./chunk-MO2FASX4.js";
63
+ } from "./chunk-5FQ2BNG2.js";
64
64
  import {
65
65
  exchangeResultSchema,
66
66
  exchangeSchema,
67
67
  memberSummarySchema,
68
68
  participantSessionSchema,
69
69
  sessionMember
70
- } from "./chunk-JUQ2GNMD.js";
70
+ } from "./chunk-PNCLJOUD.js";
71
71
  import {
72
72
  RelayError,
73
73
  RelayErrorCode,
@@ -92,6 +92,8 @@ import {
92
92
  missingPrerequisiteSchema,
93
93
  pageQuerySchema,
94
94
  parseInput,
95
+ questSubmissionResultSchema,
96
+ questSubmissionSchema,
95
97
  quizEvidenceSchema,
96
98
  submissionEvidenceSchema,
97
99
  submitSchema,
@@ -104,10 +106,10 @@ import {
104
106
  walletSignInNetworksSchema,
105
107
  walletSignatureSchema,
106
108
  walletUnlinkInputSchema
107
- } from "./chunk-6UFXPKLA.js";
109
+ } from "./chunk-BVEWY2ZL.js";
108
110
 
109
- // src/submissions.ts
110
- import { z } from "zod";
111
+ // src/legacy-submissions.ts
112
+ import * as z from "zod";
111
113
  var common = {
112
114
  id: identifier,
113
115
  quest: identifier,
@@ -156,7 +158,7 @@ var recordSchema = z.union([
156
158
  previous: activeRecordSchema
157
159
  })
158
160
  ]);
159
- function submissions(transport, configuredStore, namespace) {
161
+ function legacySubmissions(transport, configuredStore, namespace) {
160
162
  async function open(quest, options = {
161
163
  input: "photo"
162
164
  }) {
@@ -168,6 +170,7 @@ function submissions(transport, configuredStore, namespace) {
168
170
  }
169
171
  const store = configuredStore;
170
172
  parseInput(identifier, quest);
173
+ parseInput(identifier.optional(), options.release);
171
174
  const session = await transport.session();
172
175
  const member = sessionMember(session);
173
176
  if (!session || !member) {
@@ -186,8 +189,8 @@ function submissions(transport, configuredStore, namespace) {
186
189
  member,
187
190
  quest
188
191
  ]);
189
- async function currentSession() {
190
- const current = await transport.session();
192
+ async function currentSession(step) {
193
+ const current = await transport.session(step());
191
194
  if (current?.id !== sessionId) {
192
195
  throw clientError(
193
196
  "Your session changed. Reopen the submission before continuing.",
@@ -217,7 +220,7 @@ function submissions(transport, configuredStore, namespace) {
217
220
  }
218
221
  return record;
219
222
  }
220
- async function claim(record) {
223
+ async function claim(record, step) {
221
224
  try {
222
225
  return await transport.claim(
223
226
  {
@@ -226,10 +229,11 @@ function submissions(transport, configuredStore, namespace) {
226
229
  ...record.release ? { release: record.release } : {},
227
230
  ..."evidence" in record ? { evidence: record.evidence } : record.input.kind !== "none" ? { input: record.input } : {}
228
231
  },
229
- sessionId
232
+ sessionId,
233
+ step()
230
234
  );
231
235
  } catch (error) {
232
- if (isRelayError(error) && error.kind === "http" && !error.uncertain && error.status === 409 && error.code === "operation-retired") {
236
+ if (isRelayError(error) && error.kind === "http" && !error.uncertain && error.status === 409 && error.code === RelayErrorCode.OperationRetired) {
233
237
  await store.write(key, {
234
238
  id: record.id,
235
239
  quest,
@@ -244,10 +248,10 @@ function submissions(transport, configuredStore, namespace) {
244
248
  function retiredError() {
245
249
  return clientError(
246
250
  "This saved submission was retired. Review the current quest, then discard it before starting a new submission.",
247
- "operation-retired"
251
+ RelayErrorCode.OperationRetired
248
252
  );
249
253
  }
250
- async function resumeRecord(saved) {
254
+ async function resumeRecord(saved, step) {
251
255
  if (saved.stage === "retired") {
252
256
  throw retiredError();
253
257
  }
@@ -260,11 +264,9 @@ function submissions(transport, configuredStore, namespace) {
260
264
  }
261
265
  let record = saved;
262
266
  if (record.stage === "upload") {
263
- const evidence = await transport.upload(
264
- record.file,
265
- record.id,
266
- sessionId
267
- );
267
+ const file = record.file;
268
+ const action = record.id;
269
+ const evidence = await transport.upload(file, action, sessionId, step());
268
270
  record = {
269
271
  id: record.id,
270
272
  quest,
@@ -277,7 +279,7 @@ function submissions(transport, configuredStore, namespace) {
277
279
  }
278
280
  let attempt;
279
281
  try {
280
- attempt = await claim(record);
282
+ attempt = await claim(record, step);
281
283
  } catch (error) {
282
284
  if (error instanceof RelayError && error.status === 422 && error.code === "invalid-submission" && record.stage === "claim") {
283
285
  await store.write(key, null);
@@ -306,13 +308,14 @@ function submissions(transport, configuredStore, namespace) {
306
308
  attempt: attempt.id
307
309
  });
308
310
  if (attempt.status === "failed") {
309
- attempt = await transport.retry(attempt.id, sessionId);
311
+ const failed = attempt.id;
312
+ attempt = await transport.retry(failed, sessionId, step());
310
313
  }
311
314
  return attempt;
312
315
  }
313
- async function submit(record) {
316
+ async function submit(record, step) {
314
317
  return store.exclusive(key, async () => {
315
- await currentSession();
318
+ await currentSession(step);
316
319
  const existing = await read();
317
320
  if (existing?.stage === "retired") {
318
321
  throw retiredError();
@@ -331,27 +334,29 @@ function submissions(transport, configuredStore, namespace) {
331
334
  );
332
335
  }
333
336
  if (existing?.stage === "submitted") {
334
- const previous = await claim(existing);
337
+ const previous = await claim(existing, step);
335
338
  if (previous.status === "failed") {
336
- return transport.retry(previous.id, sessionId);
339
+ return transport.retry(previous.id, sessionId, step());
337
340
  }
338
341
  if (previous.status !== "rejected" && !(options.repeatable && previous.status === "accepted")) {
339
342
  return previous;
340
343
  }
341
344
  }
342
345
  await store.write(key, record);
343
- return resumeRecord(record);
346
+ return resumeRecord(record, step);
344
347
  });
345
348
  }
346
349
  const handle = {
347
- async discard() {
350
+ async discard(call) {
351
+ const step = steps(call);
348
352
  return store.exclusive(key, async () => {
349
- await currentSession();
353
+ await currentSession(step);
350
354
  const record = await read();
351
355
  if (!record) {
352
356
  return;
353
357
  }
354
358
  if (record.stage === "retired") {
359
+ step();
355
360
  await store.write(key, null);
356
361
  return;
357
362
  }
@@ -361,18 +366,20 @@ function submissions(transport, configuredStore, namespace) {
361
366
  RelayErrorCode.SubmissionConflict
362
367
  );
363
368
  }
364
- const attempt = await claim(record);
369
+ const attempt = await claim(record, step);
365
370
  if (attempt.status !== "failed" && attempt.status !== "rejected" && !(options.repeatable && attempt.status === "accepted")) {
366
371
  throw clientError(
367
372
  "Only a failed or rejected submission can be discarded.",
368
373
  RelayErrorCode.SubmissionConflict
369
374
  );
370
375
  }
376
+ step();
371
377
  await store.write(key, null);
372
378
  });
373
379
  },
374
- async status() {
375
- await currentSession();
380
+ async status(call) {
381
+ const step = steps(call);
382
+ await currentSession(step);
376
383
  const record = await read();
377
384
  if (!record) {
378
385
  return { kind: "empty" };
@@ -385,9 +392,10 @@ function submissions(transport, configuredStore, namespace) {
385
392
  }
386
393
  return record.stage === "submitted" ? { kind: "submitted", attempt: record.attempt } : { kind: "pending", stage: record.stage };
387
394
  },
388
- async resume() {
395
+ async resume(call) {
396
+ const step = steps(call);
389
397
  return store.exclusive(key, async () => {
390
- await currentSession();
398
+ await currentSession(step);
391
399
  const record = await read();
392
400
  if (!record) {
393
401
  throw clientError(
@@ -395,7 +403,7 @@ function submissions(transport, configuredStore, namespace) {
395
403
  RelayErrorCode.SubmissionConflict
396
404
  );
397
405
  }
398
- return resumeRecord(record);
406
+ return resumeRecord(record, step);
399
407
  });
400
408
  }
401
409
  };
@@ -403,54 +411,347 @@ function submissions(transport, configuredStore, namespace) {
403
411
  const kind = options.input;
404
412
  return {
405
413
  ...handle,
406
- submit(value) {
414
+ async submit(value, call) {
407
415
  const input = parseInput(valueEvidenceSchema, { kind, value });
408
- return submit({
409
- id: crypto.randomUUID(),
410
- quest,
411
- release: options.release,
412
- stage: "claim",
413
- input
414
- });
416
+ const step = steps(call);
417
+ return submit(
418
+ {
419
+ id: crypto.randomUUID(),
420
+ quest,
421
+ release: options.release,
422
+ stage: "claim",
423
+ input
424
+ },
425
+ step
426
+ );
415
427
  }
416
428
  };
417
429
  }
418
430
  if (options.input === "quiz") {
419
431
  return {
420
432
  ...handle,
421
- submit(answers, version) {
433
+ async submit(answers, call = {}) {
434
+ const { release, ...callOptions } = call;
435
+ const step = steps(callOptions);
422
436
  const input = parseInput(quizEvidenceSchema, {
423
437
  kind: "quiz",
424
438
  answers,
425
- ...version || options.release ? { release: version?.release ?? options.release } : {}
426
- });
427
- return submit({
428
- id: crypto.randomUUID(),
429
- ...options.release ? { release: options.release } : {},
430
- quest,
431
- stage: "claim",
432
- input
439
+ ...release || options.release ? { release: release ?? options.release } : {}
433
440
  });
441
+ return submit(
442
+ {
443
+ id: crypto.randomUUID(),
444
+ ...options.release ? { release: options.release } : {},
445
+ quest,
446
+ stage: "claim",
447
+ input
448
+ },
449
+ step
450
+ );
434
451
  }
435
452
  };
436
453
  }
437
454
  if (options.input === "none") {
438
455
  return {
439
456
  ...handle,
440
- submit() {
441
- return submit({
457
+ async submit(call) {
458
+ const step = steps(call);
459
+ return submit(
460
+ {
461
+ id: crypto.randomUUID(),
462
+ ...options.release ? { release: options.release } : {},
463
+ quest,
464
+ stage: "claim",
465
+ input: { kind: "none" }
466
+ },
467
+ step
468
+ );
469
+ }
470
+ };
471
+ }
472
+ return {
473
+ ...handle,
474
+ async submit(file, call) {
475
+ const step = steps(call);
476
+ if (!["image/jpeg", "image/png"].includes(file.type) || file.size === 0 || file.size > 5 * 1024 * 1024) {
477
+ throw clientError(
478
+ "Choose a JPG or PNG photo under 5 MB",
479
+ RelayErrorCode.InvalidInput
480
+ );
481
+ }
482
+ const bytes = new Uint8Array(await file.slice(0, 4).arrayBuffer());
483
+ const signature = file.type === "image/png" ? [137, 80, 78, 71] : [255, 216, 255];
484
+ if (!signature.every((byte, index) => bytes[index] === byte)) {
485
+ throw clientError(
486
+ "Image content does not match its file type",
487
+ RelayErrorCode.InvalidInput
488
+ );
489
+ }
490
+ return submit(
491
+ {
442
492
  id: crypto.randomUUID(),
443
493
  ...options.release ? { release: options.release } : {},
444
494
  quest,
445
- stage: "claim",
446
- input: { kind: "none" }
495
+ stage: "upload",
496
+ file
497
+ },
498
+ step
499
+ );
500
+ }
501
+ };
502
+ }
503
+ return open;
504
+ }
505
+ function legacySubmissionKey(namespace, session, member, quest) {
506
+ return JSON.stringify([
507
+ "relay-submission-v1",
508
+ namespace,
509
+ session.organization,
510
+ session.environment,
511
+ session.project,
512
+ member,
513
+ quest
514
+ ]);
515
+ }
516
+ async function legacySubmissionState(store, key) {
517
+ try {
518
+ const saved = await store.read(key);
519
+ if (saved === void 0 || saved === null) {
520
+ return "none";
521
+ }
522
+ const parsed = recordSchema.safeParse(saved);
523
+ return parsed.success && parsed.data.stage === "submitted" ? "submitted" : "unfinished";
524
+ } catch {
525
+ return "none";
526
+ }
527
+ }
528
+
529
+ // src/submissions.ts
530
+ function steps(options = {}) {
531
+ const { signal, timeout } = options;
532
+ if (timeout !== void 0 && !validTimeout(timeout)) {
533
+ throw clientError(
534
+ "timeout must be a positive, finite number of milliseconds",
535
+ RelayErrorCode.InvalidInput
536
+ );
537
+ }
538
+ const ends = timeout === void 0 ? void 0 : Date.now() + timeout;
539
+ return () => {
540
+ if (signal?.aborted) {
541
+ throw new RelayError("The request was cancelled", {
542
+ kind: "aborted",
543
+ code: RelayErrorCode.Aborted,
544
+ cause: signal.reason
545
+ });
546
+ }
547
+ if (ends === void 0) {
548
+ return { signal };
549
+ }
550
+ const remaining = ends - Date.now();
551
+ if (remaining <= 0) {
552
+ throw new RelayError(`Relay did not respond within ${timeout} ms`, {
553
+ kind: "timeout",
554
+ code: RelayErrorCode.Timeout
555
+ });
556
+ }
557
+ return { signal, timeout: remaining };
558
+ };
559
+ }
560
+ var questChanged = (error) => isRelayError(error) && error.kind === "http" && error.status === 409 && (error.code === RelayErrorCode.QuestUpdated || error.code === RelayErrorCode.StaleQuiz);
561
+ function submissions(transport, configuredStore, namespace) {
562
+ const legacy = configuredStore ? legacySubmissions(transport.legacy, configuredStore, namespace) : null;
563
+ async function open(quest, options = { input: "photo" }) {
564
+ parseInput(identifier, quest);
565
+ parseInput(identifier.optional(), options.release);
566
+ const session = await transport.session();
567
+ const member = sessionMember(session);
568
+ if (!session || !member) {
569
+ throw clientError(
570
+ "Sign in to submit an activity",
571
+ RelayErrorCode.NotSignedIn
572
+ );
573
+ }
574
+ const sessionId = session.id;
575
+ const legacyKey = legacySubmissionKey(namespace, session, member, quest);
576
+ const legacyState = async () => configuredStore && legacy ? legacySubmissionState(configuredStore, legacyKey) : "none";
577
+ const pendingLegacy = async () => await legacyState() === "unfinished";
578
+ const legacyHandle = await legacyState() !== "none" ? await legacy(
579
+ quest,
580
+ options
581
+ ) : null;
582
+ const draftOf = (submission, file) => file ? { kind: "photo", file } : submission.input ? submission.input : options.input === "none" ? { kind: "none" } : null;
583
+ const outdated = (submission) => submission.reviewRequired || options.release !== void 0 && submission.release !== null && submission.release !== options.release;
584
+ const drafted = (submission) => options.input === "none" ? true : options.input === "photo" ? submission.evidence !== null : submission.release !== null || submission.input !== null || submission.evidence !== null;
585
+ async function finish(submission, step, file) {
586
+ let attempt;
587
+ try {
588
+ attempt = (await transport.submit(submission.id, sessionId, step())).attempt;
589
+ } catch (error) {
590
+ if (questChanged(error)) {
591
+ throw new QuestUpdatedError(
592
+ error.message,
593
+ error.code,
594
+ draftOf(submission, file),
595
+ error
596
+ );
597
+ }
598
+ throw error;
599
+ }
600
+ return attempt.status === "failed" ? transport.retry(attempt.id, sessionId, step()) : attempt;
601
+ }
602
+ async function submit(draft, step) {
603
+ const current = await transport.current(quest, sessionId, step());
604
+ if (current?.status === "submitted" && current.attempt) {
605
+ const previous = await finish(current, step);
606
+ if (previous.status !== "rejected" && !(options.repeatable && previous.status === "accepted")) {
607
+ return previous;
608
+ }
609
+ }
610
+ const opened = await transport.open(quest, sessionId, step());
611
+ let saved;
612
+ try {
613
+ saved = await transport.save(
614
+ opened.id,
615
+ {
616
+ release: options.release ?? null,
617
+ input: draft.input ?? null,
618
+ evidence: null
619
+ },
620
+ sessionId,
621
+ step()
622
+ );
623
+ if (draft.file) {
624
+ saved = await transport.upload(
625
+ opened.id,
626
+ draft.file,
627
+ sessionId,
628
+ step()
629
+ );
630
+ }
631
+ } catch (error) {
632
+ if (isRelayError(error) && error.code === "submission-closed") {
633
+ return finish(opened, step, draft.file);
634
+ }
635
+ throw error;
636
+ }
637
+ try {
638
+ return await finish(saved, step, draft.file);
639
+ } catch (error) {
640
+ if (isRelayError(error) && error.code === "attempt-pending") {
641
+ await transport.discard(saved.id, sessionId, step());
642
+ const latest = await transport.current(quest, sessionId, step());
643
+ if (latest?.status === "submitted" && latest.attempt) {
644
+ return finish(latest, step);
645
+ }
646
+ }
647
+ throw error;
648
+ }
649
+ }
650
+ const serverHandle = {
651
+ async status(call) {
652
+ const step = steps(call);
653
+ const current = await transport.current(quest, sessionId, step());
654
+ if (!current) {
655
+ return { kind: "empty" };
656
+ }
657
+ if (current.status === "submitted" && current.attempt) {
658
+ return { kind: "submitted", attempt: current.attempt };
659
+ }
660
+ if (current.status !== "open" || !drafted(current)) {
661
+ return { kind: "empty" };
662
+ }
663
+ return outdated(current) ? { kind: "needs-review" } : { kind: "pending", stage: "claim" };
664
+ },
665
+ async resume(call) {
666
+ const step = steps(call);
667
+ const current = await transport.current(quest, sessionId, step());
668
+ if (current?.status === "open" && drafted(current)) {
669
+ if (outdated(current)) {
670
+ throw new QuestUpdatedError(
671
+ "This quest changed. Review it before submitting again.",
672
+ "quest-updated",
673
+ draftOf(current)
674
+ );
675
+ }
676
+ return finish(current, step);
677
+ }
678
+ if (current?.status === "submitted") {
679
+ return finish(current, step);
680
+ }
681
+ throw clientError(
682
+ "There is no saved submission to resume",
683
+ RelayErrorCode.SubmissionConflict
684
+ );
685
+ },
686
+ async discard(call) {
687
+ const step = steps(call);
688
+ const current = await transport.current(quest, sessionId, step());
689
+ if (current?.status === "open") {
690
+ await transport.discard(current.id, sessionId, step());
691
+ }
692
+ }
693
+ };
694
+ const viaLegacy = (server, pick) => async (...args) => legacyHandle && await pendingLegacy() ? pick(legacyHandle)(...args) : server(...args);
695
+ const handle = {
696
+ status: viaLegacy(serverHandle.status, (h) => h.status),
697
+ async resume(call) {
698
+ const state = legacyHandle ? await legacyState() : "none";
699
+ if (legacyHandle && (state === "unfinished" || state === "submitted" && !await transport.current(quest, sessionId, steps(call)()))) {
700
+ return legacyHandle.resume(call);
701
+ }
702
+ return serverHandle.resume(call);
703
+ },
704
+ discard: viaLegacy(serverHandle.discard, (h) => h.discard)
705
+ };
706
+ if (options.input === "text" || options.input === "url") {
707
+ const kind = options.input;
708
+ return {
709
+ ...handle,
710
+ async submit(value, call) {
711
+ if (legacyHandle && await pendingLegacy()) {
712
+ return legacyHandle.submit(value, call);
713
+ }
714
+ const input = parseInput(valueEvidenceSchema, { kind, value });
715
+ return submit({ input }, steps(call));
716
+ }
717
+ };
718
+ }
719
+ if (options.input === "quiz") {
720
+ return {
721
+ ...handle,
722
+ async submit(answers, call = {}) {
723
+ if (legacyHandle && await pendingLegacy()) {
724
+ return legacyHandle.submit(answers, call);
725
+ }
726
+ const { release, ...callOptions } = call;
727
+ const step = steps(callOptions);
728
+ const input = parseInput(quizEvidenceSchema, {
729
+ kind: "quiz",
730
+ answers,
731
+ ...release || options.release ? { release: release ?? options.release } : {}
447
732
  });
733
+ return submit({ input }, step);
734
+ }
735
+ };
736
+ }
737
+ if (options.input === "none") {
738
+ return {
739
+ ...handle,
740
+ async submit(call) {
741
+ if (legacyHandle && await pendingLegacy()) {
742
+ return legacyHandle.submit(call);
743
+ }
744
+ return submit({}, steps(call));
448
745
  }
449
746
  };
450
747
  }
451
748
  return {
452
749
  ...handle,
453
- async submit(file) {
750
+ async submit(file, call) {
751
+ if (legacyHandle && await pendingLegacy()) {
752
+ return legacyHandle.submit(file, call);
753
+ }
754
+ const step = steps(call);
454
755
  if (!["image/jpeg", "image/png"].includes(file.type) || file.size === 0 || file.size > 5 * 1024 * 1024) {
455
756
  throw clientError(
456
757
  "Choose a JPG or PNG photo under 5 MB",
@@ -465,13 +766,7 @@ function submissions(transport, configuredStore, namespace) {
465
766
  RelayErrorCode.InvalidInput
466
767
  );
467
768
  }
468
- return submit({
469
- id: crypto.randomUUID(),
470
- ...options.release ? { release: options.release } : {},
471
- quest,
472
- stage: "upload",
473
- file
474
- });
769
+ return submit({ file }, step);
475
770
  }
476
771
  };
477
772
  }
@@ -493,7 +788,7 @@ var QuestUpdatedError = class extends RelayError {
493
788
  };
494
789
 
495
790
  // src/client.ts
496
- import { z as z2 } from "zod";
791
+ import * as z2 from "zod";
497
792
 
498
793
  // src/progress.ts
499
794
  function sessionEnded(error) {
@@ -680,16 +975,22 @@ function reconcileQuestDraft(view, draft) {
680
975
  }
681
976
  function questActions(submission, latest) {
682
977
  return async (view) => {
683
- async function run(task) {
978
+ async function run(call, task) {
979
+ const step = steps(call);
684
980
  try {
685
981
  return { kind: "submitted", attempt: await task() };
686
982
  } catch (error) {
687
983
  if (!(error instanceof QuestUpdatedError)) {
688
984
  throw error;
689
985
  }
690
- const current = await latest(view.quest).catch(() => {
691
- throw error;
692
- });
986
+ const current = await latest(view.quest, step()).catch(
987
+ (failure) => {
988
+ if (isRelayError(failure) && (failure.kind === "aborted" || failure.kind === "timeout")) {
989
+ throw failure;
990
+ }
991
+ throw error;
992
+ }
993
+ );
693
994
  return {
694
995
  kind: "quest-updated",
695
996
  latest: current,
@@ -710,7 +1011,7 @@ function questActions(submission, latest) {
710
1011
  view,
711
1012
  status: handle.status,
712
1013
  discard: handle.discard,
713
- resume: () => run(handle.resume)
1014
+ resume: (call) => run(call, () => handle.resume(call))
714
1015
  };
715
1016
  }
716
1017
  switch (view.interaction) {
@@ -728,8 +1029,8 @@ function questActions(submission, latest) {
728
1029
  view,
729
1030
  status: handle.status,
730
1031
  discard: handle.discard,
731
- resume: () => run(handle.resume),
732
- submit: (value) => run(() => {
1032
+ resume: (call) => run(call, () => handle.resume(call)),
1033
+ submit: (value, call) => run(call, () => {
733
1034
  if (view.trigger.kind !== "manual" || view.trigger.input !== "text" && view.trigger.input !== "url") {
734
1035
  throw clientError(
735
1036
  "Quest input changed",
@@ -743,7 +1044,7 @@ function questActions(submission, latest) {
743
1044
  if (problem) {
744
1045
  throw clientError(problem, RelayErrorCode.InvalidInput);
745
1046
  }
746
- return handle.submit(value);
1047
+ return handle.submit(value, call);
747
1048
  })
748
1049
  };
749
1050
  }
@@ -759,8 +1060,8 @@ function questActions(submission, latest) {
759
1060
  view,
760
1061
  status: handle.status,
761
1062
  discard: handle.discard,
762
- resume: () => run(handle.resume),
763
- submit: (file) => run(() => handle.submit(file))
1063
+ resume: (call) => run(call, () => handle.resume(call)),
1064
+ submit: (file, call) => run(call, () => handle.submit(file, call))
764
1065
  };
765
1066
  }
766
1067
  case "quiz": {
@@ -775,8 +1076,14 @@ function questActions(submission, latest) {
775
1076
  view,
776
1077
  status: handle.status,
777
1078
  discard: handle.discard,
778
- resume: () => run(handle.resume),
779
- submit: (answers) => run(() => handle.submit(answers, { release: view.release }))
1079
+ resume: (call) => run(call, () => handle.resume(call)),
1080
+ submit: (answers, call) => run(
1081
+ call,
1082
+ () => handle.submit(answers, {
1083
+ ...call,
1084
+ release: view.release
1085
+ })
1086
+ )
780
1087
  };
781
1088
  }
782
1089
  case "claim": {
@@ -791,8 +1098,8 @@ function questActions(submission, latest) {
791
1098
  view,
792
1099
  status: handle.status,
793
1100
  discard: handle.discard,
794
- resume: () => run(handle.resume),
795
- submit: () => run(handle.submit)
1101
+ resume: (call) => run(call, () => handle.resume(call)),
1102
+ submit: (call) => run(call, () => handle.submit(call))
796
1103
  };
797
1104
  }
798
1105
  }
@@ -800,6 +1107,14 @@ function questActions(submission, latest) {
800
1107
  }
801
1108
 
802
1109
  // src/client.ts
1110
+ var authStartSchema = z2.strictObject({
1111
+ provider: authProviderSchema,
1112
+ mode: z2.enum(["sign-in", "connect"])
1113
+ });
1114
+ var emailVerificationSchema = z2.strictObject({
1115
+ challenge: z2.string().min(1).max(200),
1116
+ code: z2.string().transform((code) => code.trim()).pipe(z2.string().regex(/^\d{6}$/, "Enter the 6-digit code"))
1117
+ });
803
1118
  function errorBody(value) {
804
1119
  const body = value !== null && typeof value === "object" ? value : {};
805
1120
  const window = body.window === "tracker" ? "tracker" : body.window === "quest" ? "quest" : void 0;
@@ -900,40 +1215,123 @@ function relayClient(options) {
900
1215
  if (options.timeout !== void 0 && !validTimeout(options.timeout)) {
901
1216
  throw invalidTimeout();
902
1217
  }
903
- const clientTimeout = options.timeout ?? PARTICIPANT_TIMEOUT;
904
- async function send(path, call, life) {
905
- const method = methodOf(call);
906
- const headers = new Headers(call.headers);
907
- headers.set("X-Domino-Client", clientIdentity());
908
- let body2 = call.body;
1218
+ if (options.token !== void 0 && options.getToken) {
1219
+ throw clientError(
1220
+ "Pass token or getToken, not both",
1221
+ RelayErrorCode.InvalidInput
1222
+ );
1223
+ }
1224
+ try {
1225
+ const headers = new Headers(options.headers);
909
1226
  if (options.token) {
910
1227
  headers.set("Authorization", `Bearer ${options.token}`);
911
1228
  }
912
- if (options.organization) {
913
- headers.set("X-Relay-Organization", options.organization);
914
- }
915
- if (options.project) {
916
- headers.set("X-Relay-Project", options.project);
917
- }
918
- if (options.environment) {
919
- headers.set("X-Relay-Environment", options.environment);
1229
+ headers.set("X-Relay-Organization", options.organization ?? "");
1230
+ headers.set("X-Relay-Project", options.project ?? "");
1231
+ } catch (error) {
1232
+ throw clientError(
1233
+ "headers, token, organization and project must be valid HTTP header values",
1234
+ RelayErrorCode.InvalidInput,
1235
+ { cause: error }
1236
+ );
1237
+ }
1238
+ const clientTimeout = options.timeout ?? PARTICIPANT_TIMEOUT;
1239
+ async function credential(call, life) {
1240
+ if (!options.getToken) {
1241
+ return options.token;
920
1242
  }
921
- if (call.options?.session) {
922
- headers.set("X-Relay-Session", call.options.session);
1243
+ const getToken = options.getToken;
1244
+ try {
1245
+ return await new Promise((resolve, reject) => {
1246
+ const stop = () => reject(life.signal.reason);
1247
+ life.signal.addEventListener("abort", stop, { once: true });
1248
+ getToken().then(resolve, reject).finally(() => life.signal.removeEventListener("abort", stop));
1249
+ });
1250
+ } catch (error) {
1251
+ if (life.signal.aborted) {
1252
+ throw interrupted(call, life, error, { sent: false });
1253
+ }
1254
+ throw new RelayError("Could not get a Relay credential", {
1255
+ // A token source that could not reach its own service failed like a network call.
1256
+ kind: error instanceof TypeError ? "network" : "client",
1257
+ code: RelayErrorCode.Credential,
1258
+ operationId: call.operationId,
1259
+ replaySafe: call.replaySafe,
1260
+ cause: error
1261
+ });
923
1262
  }
924
- if (call.json !== void 0) {
925
- headers.set("Content-Type", "application/json");
926
- body2 = JSON.stringify(call.json);
1263
+ }
1264
+ async function send(path, call, life) {
1265
+ if (life.signal.aborted) {
1266
+ throw interrupted(call, life, life.reason(), { sent: false });
927
1267
  }
928
- if (method !== "GET") {
929
- headers.set("X-Relay-CSRF", "1");
1268
+ const method = methodOf(call);
1269
+ const token = await credential(call, life);
1270
+ let headers;
1271
+ let body2 = call.body;
1272
+ try {
1273
+ headers = new Headers(options.headers);
1274
+ new Headers(call.headers).forEach(
1275
+ (value, name) => headers.set(name, value)
1276
+ );
1277
+ headers.set("X-Domino-Client", clientIdentity());
1278
+ if (token) {
1279
+ try {
1280
+ headers.set("Authorization", `Bearer ${token}`);
1281
+ } catch (error) {
1282
+ throw new RelayError(
1283
+ "getToken returned a value that is not a token",
1284
+ {
1285
+ kind: "client",
1286
+ code: RelayErrorCode.Credential,
1287
+ operationId: call.operationId,
1288
+ replaySafe: call.replaySafe,
1289
+ cause: error
1290
+ }
1291
+ );
1292
+ }
1293
+ } else if (options.getToken) {
1294
+ headers.delete("Authorization");
1295
+ }
1296
+ if (options.organization) {
1297
+ headers.set("X-Relay-Organization", options.organization);
1298
+ }
1299
+ if (options.project) {
1300
+ headers.set("X-Relay-Project", options.project);
1301
+ }
1302
+ if (options.environment) {
1303
+ headers.set("X-Relay-Environment", options.environment);
1304
+ }
1305
+ if (call.options?.session) {
1306
+ headers.set("X-Relay-Session", call.options.session);
1307
+ }
1308
+ if (call.json !== void 0) {
1309
+ headers.set("Content-Type", "application/json");
1310
+ body2 = JSON.stringify(call.json);
1311
+ }
1312
+ if (method !== "GET") {
1313
+ headers.set("X-Relay-CSRF", "1");
1314
+ }
1315
+ } catch (error) {
1316
+ if (isRelayError(error)) {
1317
+ throw error;
1318
+ }
1319
+ throw clientError(
1320
+ "The request has a header or body that cannot be sent",
1321
+ RelayErrorCode.InvalidInput,
1322
+ {
1323
+ operationId: call.operationId,
1324
+ replaySafe: call.replaySafe,
1325
+ cause: error
1326
+ }
1327
+ );
930
1328
  }
931
1329
  if (life.signal.aborted) {
932
1330
  throw interrupted(call, life, life.reason(), { sent: false });
933
1331
  }
934
1332
  let response;
935
1333
  try {
936
- response = await fetch(options.baseUrl + path, {
1334
+ response = await (options.fetch ?? fetch)(options.baseUrl + path, {
937
1335
  method,
938
1336
  headers,
939
1337
  body: body2,
@@ -1078,23 +1476,65 @@ function relayClient(options) {
1078
1476
  }
1079
1477
  const submission = submissions(
1080
1478
  {
1081
- session: () => readSession(),
1082
- upload: (file, action, session) => request("/v1/me/evidence", evidenceSchema, {
1479
+ session: (call) => readSession(call),
1480
+ current: (quest, session, call) => request(
1481
+ `/v1/me/quests/${quest}/submission`,
1482
+ questSubmissionSchema.nullable(),
1483
+ { options: { ...call, session } }
1484
+ ),
1485
+ // Opening, saving, submitting and discarding apply once: each repeat returns the same
1486
+ // submission or attempt, so all are replay-safe.
1487
+ open: (quest, session, call) => request(`/v1/me/quests/${quest}/submission`, questSubmissionSchema, {
1488
+ json: {},
1489
+ options: { ...call, session },
1490
+ replaySafe: true
1491
+ }),
1492
+ save: (id, update, session, call) => request(`/v1/me/submissions/${id}`, questSubmissionSchema, {
1493
+ json: update,
1494
+ options: { ...call, session },
1495
+ replaySafe: true
1496
+ }),
1497
+ submit: (id, session, call) => request(
1498
+ `/v1/me/submissions/${id}/submit`,
1499
+ questSubmissionResultSchema,
1500
+ { json: {}, options: { ...call, session }, operationId: id }
1501
+ ),
1502
+ discard: (id, session, call) => request(`/v1/me/submissions/${id}/discard`, questSubmissionSchema, {
1503
+ json: {},
1504
+ options: { ...call, session },
1505
+ operationId: id
1506
+ }),
1507
+ // Relay derives the upload ID from the submission and the bytes: a repeat attaches the same photo.
1508
+ upload: (id, file, session, call) => request(`/v1/me/submissions/${id}/evidence`, questSubmissionSchema, {
1083
1509
  method: "POST",
1084
1510
  body: file,
1085
- headers: { "Content-Type": file.type, "X-Relay-Upload-ID": action },
1086
- options: { session },
1087
- operationId: action
1088
- }),
1089
- claim: async (claim, session) => request("/v1/me/attempts", attemptSchema, {
1090
- json: claim,
1091
- options: { session },
1092
- operationId: claim.actionId
1511
+ headers: { "Content-Type": file.type },
1512
+ options: { ...call, session },
1513
+ replaySafe: true
1093
1514
  }),
1094
- retry: (attempt, session) => request(`/v1/attempts/${attempt}/retry`, attemptSchema, {
1515
+ retry: (attempt, session, call) => request(`/v1/attempts/${attempt}/retry`, attemptSchema, {
1095
1516
  json: {},
1096
- options: { session }
1097
- })
1517
+ options: { ...call, session }
1518
+ }),
1519
+ legacy: {
1520
+ session: (call) => readSession(call),
1521
+ upload: (file, action, session, call) => request("/v1/me/evidence", evidenceSchema, {
1522
+ method: "POST",
1523
+ body: file,
1524
+ headers: { "Content-Type": file.type, "X-Relay-Upload-ID": action },
1525
+ options: { ...call, session },
1526
+ operationId: action
1527
+ }),
1528
+ claim: async (claim, session, call) => request("/v1/me/attempts", attemptSchema, {
1529
+ json: claim,
1530
+ options: { ...call, session },
1531
+ operationId: claim.actionId
1532
+ }),
1533
+ retry: (attempt, session, call) => request(`/v1/attempts/${attempt}/retry`, attemptSchema, {
1534
+ json: {},
1535
+ options: { ...call, session }
1536
+ })
1537
+ }
1098
1538
  },
1099
1539
  options.submissionStore,
1100
1540
  options.baseUrl
@@ -1124,6 +1564,26 @@ function relayClient(options) {
1124
1564
  }
1125
1565
  return quest;
1126
1566
  }
1567
+ async function authStart(start, ...rest) {
1568
+ const [value, requestOptions] = typeof start === "string" ? [{ provider: start, mode: rest[0] }, rest[1]] : [start, rest[0]];
1569
+ return request("/v1/auth/start", z2.object({ url: z2.string() }), {
1570
+ json: parseInput(authStartSchema, value),
1571
+ options: requestOptions
1572
+ });
1573
+ }
1574
+ async function emailVerify(verification, ...rest) {
1575
+ const [value, requestOptions] = typeof verification === "string" ? [
1576
+ { challenge: verification, code: rest[0] },
1577
+ rest[1]
1578
+ ] : [verification, rest[0]];
1579
+ const body2 = parseInput(emailVerificationSchema, value);
1580
+ return changeSession(
1581
+ () => request("/v1/auth/email/verify", participantSessionSchema, {
1582
+ json: body2,
1583
+ options: requestOptions
1584
+ })
1585
+ );
1586
+ }
1127
1587
  const ok = z2.object({ ok: z2.literal(true) });
1128
1588
  const acknowledged = z2.object({ ok: z2.boolean() });
1129
1589
  return {
@@ -1159,10 +1619,13 @@ function relayClient(options) {
1159
1619
  options: requestOptions
1160
1620
  }),
1161
1621
  /** Redeems one offer. Persist `actionId` to retry after an uncertain result. */
1162
- redeem: (offer, input = {}, requestOptions) => {
1163
- const actionId = identifier.parse(input.actionId ?? crypto.randomUUID());
1622
+ redeem: async (offer, value = {}, requestOptions) => {
1623
+ const actionId = parseInput(
1624
+ identifier,
1625
+ value.actionId ?? crypto.randomUUID()
1626
+ );
1164
1627
  return request(
1165
- `/v1/offers/${identifier.parse(offer)}/redeem`,
1628
+ `/v1/offers/${parseInput(identifier, offer)}/redeem`,
1166
1629
  redemptionSchema,
1167
1630
  {
1168
1631
  json: { actionId },
@@ -1252,7 +1715,7 @@ function relayClient(options) {
1252
1715
  * `@domino-sdk/relay/avatar`.
1253
1716
  */
1254
1717
  profiles: {
1255
- badges: (profile, options2) => request(
1718
+ badges: async (profile, options2) => request(
1256
1719
  `/v1/profiles/${parseInput(identifier, profile)}/badges`,
1257
1720
  memberBadgesSchema,
1258
1721
  { options: options2 }
@@ -1412,7 +1875,7 @@ function relayClient(options) {
1412
1875
  }
1413
1876
  return collection;
1414
1877
  },
1415
- open: questActions(submission, (id) => getQuest(id)),
1878
+ open: questActions(submission, (id, options2) => getQuest(id, options2)),
1416
1879
  observe: (listener, observeOptions) => {
1417
1880
  const observer = observeProgress(
1418
1881
  {
@@ -1530,32 +1993,17 @@ function relayClient(options) {
1530
1993
  }),
1531
1994
  { options: requestOptions }
1532
1995
  ),
1533
- start: async (provider, mode, requestOptions) => request("/v1/auth/start", z2.object({ url: z2.string() }), {
1996
+ start: authStart,
1997
+ emailStart: async (email2, requestOptions) => request("/v1/auth/email/start", z2.object({ challenge: z2.string() }), {
1534
1998
  json: {
1535
- provider: parseInput(authProviderSchema, provider),
1536
- mode: parseInput(z2.enum(["sign-in", "connect"]), mode)
1999
+ email: parseInput(
2000
+ z2.email().max(254),
2001
+ typeof email2 === "string" ? email2.trim() : email2
2002
+ )
1537
2003
  },
1538
2004
  options: requestOptions
1539
2005
  }),
1540
- emailStart: async (email, requestOptions) => request("/v1/auth/email/start", z2.object({ challenge: z2.string() }), {
1541
- json: { email: parseInput(z2.email().max(254), email.trim()) },
1542
- options: requestOptions
1543
- }),
1544
- emailVerify: async (challenge, code, requestOptions) => {
1545
- const body2 = {
1546
- challenge: parseInput(z2.string().min(1).max(200), challenge),
1547
- code: parseInput(
1548
- z2.string().regex(/^\d{6}$/, "Enter the 6-digit code"),
1549
- code.trim()
1550
- )
1551
- };
1552
- return changeSession(
1553
- () => request("/v1/auth/email/verify", participantSessionSchema, {
1554
- json: body2,
1555
- options: requestOptions
1556
- })
1557
- );
1558
- },
2006
+ emailVerify,
1559
2007
  /**
1560
2008
  * Sign in, or sign up, with a Solana, EVM or Cosmos wallet where the project enabled it
1561
2009
  * (`providers().wallets`). A wallet the member linked while signed in cannot sign in.
@@ -1652,7 +2100,7 @@ function relayClient(options) {
1652
2100
  })
1653
2101
  ),
1654
2102
  /** Links the Telegram user who opened the Mini App to the signed-in member. */
1655
- connect: (initData, requestOptions) => request("/v1/auth/telegram/mini-app", acknowledged, {
2103
+ connect: async (initData, requestOptions) => request("/v1/auth/telegram/mini-app", acknowledged, {
1656
2104
  json: parseInput(telegramMiniAppSignInSchema, {
1657
2105
  initData,
1658
2106
  mode: "connect"
@@ -1685,7 +2133,7 @@ function relayClient(options) {
1685
2133
  me: {
1686
2134
  badges: (options2) => request("/v1/me/badges", memberBadgesSchema, { options: options2 }),
1687
2135
  activity: {
1688
- list: (query = {}, options2) => {
2136
+ list: async (query = {}, options2) => {
1689
2137
  const parsed = parseInput(participantActivityQuerySchema, query);
1690
2138
  return request(
1691
2139
  withQuery("/v1/me/activity", parsed),
@@ -1801,7 +2249,7 @@ function relayClient(options) {
1801
2249
  });
1802
2250
  },
1803
2251
  /** Uploads evidence with a generated key; reuse `actionId` and the same file to replay. */
1804
- upload: (file, requestOptions = {}) => {
2252
+ upload: async (file, requestOptions = {}) => {
1805
2253
  const { actionId, uploadId, ...options2 } = requestOptions;
1806
2254
  const operationId = parseInput(
1807
2255
  identifier,