@domino-sdk/relay 0.12.0 → 0.14.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.
@@ -2,7 +2,9 @@
2
2
  import { z } from "zod";
3
3
  var communityTimeZoneSchema = z.string().min(1).max(100).refine((zone) => {
4
4
  try {
5
- if (/^[+-]/.test(zone)) return false;
5
+ if (/^[+-]/.test(zone)) {
6
+ return false;
7
+ }
6
8
  new Intl.DateTimeFormat("en", { timeZone: zone });
7
9
  return true;
8
10
  } catch {
@@ -23,12 +25,30 @@ var streakProgressSchema = z.object({
23
25
  todayQualified: z.boolean(),
24
26
  timeZone: communityTimeZoneSchema,
25
27
  nextBoundary: z.number().int(),
26
- pendingTimeZone: z.object({ timeZone: communityTimeZoneSchema, effectiveAt: z.number().int() }).nullable(),
27
- days: z.array(z.object({ day: z.number().int(), date: z.string(), timeZone: communityTimeZoneSchema, occurredAt: z.number().int() })),
28
+ pendingTimeZone: z.object({
29
+ timeZone: communityTimeZoneSchema,
30
+ effectiveAt: z.number().int()
31
+ }).nullable(),
32
+ days: z.array(
33
+ z.object({
34
+ day: z.number().int(),
35
+ date: z.string(),
36
+ timeZone: communityTimeZoneSchema,
37
+ occurredAt: z.number().int()
38
+ })
39
+ ),
28
40
  milestone: z.discriminatedUnion("status", [
29
41
  z.object({ status: z.literal("pending") }),
30
- z.object({ status: z.literal("awarded"), completion: z.string(), achievedAt: z.number().int() }),
31
- z.object({ status: z.literal("budget-exhausted"), budget: z.string(), achievedAt: z.number().int() })
42
+ z.object({
43
+ status: z.literal("awarded"),
44
+ completion: z.string(),
45
+ achievedAt: z.number().int()
46
+ }),
47
+ z.object({
48
+ status: z.literal("budget-exhausted"),
49
+ budget: z.string(),
50
+ achievedAt: z.number().int()
51
+ })
32
52
  ])
33
53
  });
34
54
 
@@ -406,8 +426,9 @@ var rewardSchema = z9.union([
406
426
  integrationRewardSchema
407
427
  ]);
408
428
  function rewardKey(reward) {
409
- if (reward.kind === "integration")
429
+ if (reward.kind === "integration") {
410
430
  return `integration:${reward.provider}:${reward.action}:${reward.roleId}`;
431
+ }
411
432
  return reward.kind === "points" ? "points:" + reward.balance : "tier:" + reward.reward.id;
412
433
  }
413
434
  var triggerSchema = z9.union([
@@ -453,7 +474,9 @@ var rewardBundleSchema = z9.array(rewardSchema).max(10).refine(
453
474
  "Use one entry per balance or shared reward"
454
475
  );
455
476
  var mediaUrlSchema = z9.string().max(2e3).refine((value) => {
456
- if (/^\/(?!\/)[^\\\s]*$/.test(value)) return true;
477
+ if (/^\/(?!\/)[^\\\s]*$/.test(value)) {
478
+ return true;
479
+ }
457
480
  try {
458
481
  return new URL(value).protocol === "https:";
459
482
  } catch {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  identifier
3
- } from "./chunk-3UOTI4IU.js";
3
+ } from "./chunk-FUQYKTOJ.js";
4
4
 
5
5
  // src/auth.ts
6
6
  import { z } from "zod";
@@ -7,18 +7,20 @@ import {
7
7
  leaderboardQuerySchema,
8
8
  normalizeAuthoringIdentity,
9
9
  publishedReferralSchema,
10
+ publishedTrackerSchema,
10
11
  referralAcceptSchema,
11
12
  referralArtifactSchema,
12
13
  referralDefinitionSchema,
13
14
  referralHistorySchema,
14
15
  referralSummarySchema,
16
+ trackerArtifactSchema,
15
17
  validateAuthoringIdentity
16
- } from "./chunk-CE5XVIJQ.js";
18
+ } from "./chunk-CJARNIVP.js";
17
19
  import {
18
20
  authScopeSchema,
19
21
  exchangeResultSchema,
20
22
  sessionSchema
21
- } from "./chunk-VDWQL6HV.js";
23
+ } from "./chunk-I2CCMGN6.js";
22
24
  import {
23
25
  accountConnectionSchema,
24
26
  attemptSchema,
@@ -43,7 +45,7 @@ import {
43
45
  snapshotSchema,
44
46
  storedReviewModeSchema,
45
47
  templateSchema
46
- } from "./chunk-3UOTI4IU.js";
48
+ } from "./chunk-FUQYKTOJ.js";
47
49
 
48
50
  // src/point-ledgers.ts
49
51
  import { z } from "zod";
@@ -265,15 +267,17 @@ function submissions(transport, configuredStore, namespace) {
265
267
  async function open(quest, options = {
266
268
  input: "photo"
267
269
  }) {
268
- if (!configuredStore)
270
+ if (!configuredStore) {
269
271
  throw new Error(
270
272
  "Submissions require durable storage. Use createBrowserRelayClient or provide a SubmissionStore."
271
273
  );
274
+ }
272
275
  const store = configuredStore;
273
276
  identifier.parse(quest);
274
277
  const session = await transport.session();
275
- if (session?.principal.kind !== "member")
278
+ if (session?.principal.kind !== "member") {
276
279
  throw new Error("Sign in to submit an activity");
280
+ }
277
281
  const sessionId = session.id;
278
282
  const key = JSON.stringify([
279
283
  "relay-submission-v1",
@@ -286,25 +290,30 @@ function submissions(transport, configuredStore, namespace) {
286
290
  ]);
287
291
  async function currentSession() {
288
292
  const current = await transport.session();
289
- if (current?.id !== sessionId)
293
+ if (current?.id !== sessionId) {
290
294
  throw new Error(
291
295
  "Your session changed. Reopen the submission before continuing."
292
296
  );
297
+ }
293
298
  }
294
299
  async function read() {
295
300
  const saved = await store.read(key);
296
- if (saved === void 0 || saved === null) return null;
301
+ if (saved === void 0 || saved === null) {
302
+ return null;
303
+ }
297
304
  const parsed = recordSchema.safeParse(saved);
298
- if (!parsed.success || parsed.data.quest !== quest)
305
+ if (!parsed.success || parsed.data.quest !== quest) {
299
306
  throw new Error(
300
307
  "Saved submission could not be read. Do not create another claim until it is recovered."
301
308
  );
309
+ }
302
310
  const record = parsed.data;
303
311
  const input = record.stage === "review" ? record.draft?.kind : "input" in record ? record.input.kind : "photo";
304
- if (!options.recoverPrevious && input !== options.input)
312
+ if (!options.recoverPrevious && input !== options.input) {
305
313
  throw new Error(
306
314
  "Saved submission uses a different input type. Reopen it with its original type to recover it."
307
315
  );
316
+ }
308
317
  return record;
309
318
  }
310
319
  function claim(record) {
@@ -319,12 +328,13 @@ function submissions(transport, configuredStore, namespace) {
319
328
  );
320
329
  }
321
330
  async function resumeRecord(saved) {
322
- if (saved.stage === "review")
331
+ if (saved.stage === "review") {
323
332
  throw new QuestUpdatedError(
324
333
  "This quest changed. Review it before submitting again.",
325
334
  "quest-updated",
326
335
  saved.draft
327
336
  );
337
+ }
328
338
  let record = saved;
329
339
  if (record.stage === "upload") {
330
340
  const evidence = await transport.upload(
@@ -367,30 +377,35 @@ function submissions(transport, configuredStore, namespace) {
367
377
  stage: "submitted",
368
378
  attempt: attempt.id
369
379
  });
370
- if (attempt.status === "failed")
380
+ if (attempt.status === "failed") {
371
381
  attempt = await transport.retry(attempt.id, sessionId);
382
+ }
372
383
  return attempt;
373
384
  }
374
385
  async function submit(record) {
375
386
  return store.exclusive(key, async () => {
376
387
  await currentSession();
377
388
  const existing = await read();
378
- if (existing?.stage === "review" && existing.release === record.release)
389
+ if (existing?.stage === "review" && existing.release === record.release) {
379
390
  throw new QuestUpdatedError(
380
391
  "Review the latest quest before submitting again.",
381
392
  "quest-updated",
382
393
  existing.draft
383
394
  );
384
- if (existing && existing.stage !== "submitted" && existing.stage !== "review")
395
+ }
396
+ if (existing && existing.stage !== "submitted" && existing.stage !== "review") {
385
397
  throw new Error(
386
398
  "A submission is unfinished. Resume it before choosing new input."
387
399
  );
400
+ }
388
401
  if (existing?.stage === "submitted") {
389
402
  const previous = await claim(existing);
390
- if (previous.status === "failed")
403
+ if (previous.status === "failed") {
391
404
  return transport.retry(previous.id, sessionId);
392
- if (previous.status !== "rejected" && !(options.repeatable && previous.status === "accepted"))
405
+ }
406
+ if (previous.status !== "rejected" && !(options.repeatable && previous.status === "accepted")) {
393
407
  return previous;
408
+ }
394
409
  }
395
410
  await store.write(key, record);
396
411
  return resumeRecord(record);
@@ -401,37 +416,46 @@ function submissions(transport, configuredStore, namespace) {
401
416
  return store.exclusive(key, async () => {
402
417
  await currentSession();
403
418
  const record = await read();
404
- if (!record) return;
405
- if (record.stage !== "submitted")
419
+ if (!record) {
420
+ return;
421
+ }
422
+ if (record.stage !== "submitted") {
406
423
  throw new Error(
407
424
  "Resume the unfinished submission before starting over."
408
425
  );
426
+ }
409
427
  const attempt = await claim(record);
410
- if (attempt.status !== "failed" && attempt.status !== "rejected" && !(options.repeatable && attempt.status === "accepted"))
428
+ if (attempt.status !== "failed" && attempt.status !== "rejected" && !(options.repeatable && attempt.status === "accepted")) {
411
429
  throw new Error(
412
430
  "Only a failed or rejected submission can be discarded."
413
431
  );
432
+ }
414
433
  await store.write(key, null);
415
434
  });
416
435
  },
417
436
  async status() {
418
437
  await currentSession();
419
438
  const record = await read();
420
- if (!record) return { kind: "empty" };
421
- if (record.stage === "review") return { kind: "needs-review" };
439
+ if (!record) {
440
+ return { kind: "empty" };
441
+ }
442
+ if (record.stage === "review") {
443
+ return { kind: "needs-review" };
444
+ }
422
445
  return record.stage === "submitted" ? { kind: "submitted", attempt: record.attempt } : { kind: "pending", stage: record.stage };
423
446
  },
424
447
  async resume() {
425
448
  return store.exclusive(key, async () => {
426
449
  await currentSession();
427
450
  const record = await read();
428
- if (!record)
451
+ if (!record) {
429
452
  throw new Error("There is no saved submission to resume");
453
+ }
430
454
  return resumeRecord(record);
431
455
  });
432
456
  }
433
457
  };
434
- if (options.input === "quiz")
458
+ if (options.input === "quiz") {
435
459
  return {
436
460
  ...handle,
437
461
  submit(answers, version) {
@@ -449,7 +473,8 @@ function submissions(transport, configuredStore, namespace) {
449
473
  });
450
474
  }
451
475
  };
452
- if (options.input === "none")
476
+ }
477
+ if (options.input === "none") {
453
478
  return {
454
479
  ...handle,
455
480
  submit() {
@@ -462,15 +487,18 @@ function submissions(transport, configuredStore, namespace) {
462
487
  });
463
488
  }
464
489
  };
490
+ }
465
491
  return {
466
492
  ...handle,
467
493
  async submit(file) {
468
- if (!["image/jpeg", "image/png"].includes(file.type) || file.size === 0 || file.size > 5 * 1024 * 1024)
494
+ if (!["image/jpeg", "image/png"].includes(file.type) || file.size === 0 || file.size > 5 * 1024 * 1024) {
469
495
  throw new Error("Choose a JPG or PNG photo under 5 MB");
496
+ }
470
497
  const bytes = new Uint8Array(await file.slice(0, 4).arrayBuffer());
471
498
  const signature = file.type === "image/png" ? [137, 80, 78, 71] : [255, 216, 255];
472
- if (!signature.every((byte, index) => bytes[index] === byte))
499
+ if (!signature.every((byte, index) => bytes[index] === byte)) {
473
500
  throw new Error("Image content does not match its file type");
501
+ }
474
502
  return submit({
475
503
  id: crypto.randomUUID(),
476
504
  ...options.release ? { release: options.release } : {},
@@ -493,11 +521,18 @@ var QuestUpdatedError = class extends RelayError {
493
521
 
494
522
  // src/quest-experience.ts
495
523
  function reconcileQuestDraft(view, draft) {
496
- if (!draft) return null;
497
- if (draft.kind === "photo")
524
+ if (!draft) {
525
+ return null;
526
+ }
527
+ if (draft.kind === "photo") {
498
528
  return view.interaction === "photo" ? draft : null;
499
- if (draft.kind === "none") return view.interaction === "claim" ? draft : null;
500
- if (view.interaction !== "quiz") return null;
529
+ }
530
+ if (draft.kind === "none") {
531
+ return view.interaction === "claim" ? draft : null;
532
+ }
533
+ if (view.interaction !== "quiz") {
534
+ return null;
535
+ }
501
536
  const answers = Object.fromEntries(
502
537
  (view.presentation.quiz ?? []).flatMap((q) => {
503
538
  const answer = draft.answers[q.id];
@@ -512,7 +547,9 @@ function questActions(submission, latest) {
512
547
  try {
513
548
  return { kind: "submitted", attempt: await task() };
514
549
  } catch (error) {
515
- if (!(error instanceof QuestUpdatedError)) throw error;
550
+ if (!(error instanceof QuestUpdatedError)) {
551
+ throw error;
552
+ }
516
553
  const current = await latest(view.quest).catch(() => {
517
554
  throw error;
518
555
  });
@@ -597,10 +634,11 @@ function observeProgress(transport, listener, options = {}, onDispose = () => {
597
634
  }) {
598
635
  const interval = options.intervalMs ?? 1500;
599
636
  const maximum = options.maxBackoffMs ?? 3e4;
600
- if (!Number.isFinite(interval) || interval <= 0 || !Number.isFinite(maximum) || maximum < interval)
637
+ if (!Number.isFinite(interval) || interval <= 0 || !Number.isFinite(maximum) || maximum < interval) {
601
638
  throw new Error(
602
639
  "Progress polling requires a positive interval and a maximum at least as large"
603
640
  );
641
+ }
604
642
  let disposed = false;
605
643
  let controller;
606
644
  let timer;
@@ -608,29 +646,43 @@ function observeProgress(transport, listener, options = {}, onDispose = () => {
608
646
  let failures = 0;
609
647
  function clear() {
610
648
  controller?.abort();
611
- if (timer !== void 0) clearTimeout(timer);
649
+ if (timer !== void 0) {
650
+ clearTimeout(timer);
651
+ }
612
652
  sessionId = void 0;
613
- if (!disposed) listener({ kind: "loading" });
653
+ if (!disposed) {
654
+ listener({ kind: "loading" });
655
+ }
614
656
  }
615
657
  async function refresh() {
616
- if (disposed) return;
658
+ if (disposed) {
659
+ return;
660
+ }
617
661
  controller?.abort();
618
- if (timer !== void 0) clearTimeout(timer);
662
+ if (timer !== void 0) {
663
+ clearTimeout(timer);
664
+ }
619
665
  const request = new AbortController();
620
666
  controller = request;
621
667
  const current = () => !disposed && !request.signal.aborted;
622
668
  try {
623
669
  const session = await transport.session(request.signal);
624
- if (!current()) return;
670
+ if (!current()) {
671
+ return;
672
+ }
625
673
  if (sessionId !== (session?.id ?? null)) {
626
674
  sessionId = session?.id ?? null;
627
675
  listener({ kind: "session", session });
628
676
  }
629
677
  if (session?.principal.kind === "member") {
630
678
  const progress = await transport.progress(session.id, request.signal);
631
- if (!current()) return;
679
+ if (!current()) {
680
+ return;
681
+ }
632
682
  const confirmed = await transport.session(request.signal);
633
- if (!current()) return;
683
+ if (!current()) {
684
+ return;
685
+ }
634
686
  if (confirmed?.id !== session.id || progress.member !== session.principal.member) {
635
687
  sessionId = void 0;
636
688
  listener({ kind: "loading" });
@@ -640,7 +692,9 @@ function observeProgress(transport, listener, options = {}, onDispose = () => {
640
692
  }
641
693
  failures = 0;
642
694
  } catch (error) {
643
- if (!current()) return;
695
+ if (!current()) {
696
+ return;
697
+ }
644
698
  failures = Math.min(failures + 1, 20);
645
699
  sessionId = void 0;
646
700
  listener({
@@ -648,21 +702,26 @@ function observeProgress(transport, listener, options = {}, onDispose = () => {
648
702
  error: error instanceof Error ? error : new Error("Could not refresh progress")
649
703
  });
650
704
  } finally {
651
- if (current())
705
+ if (current()) {
652
706
  timer = setTimeout(
653
707
  () => void refresh(),
654
708
  Math.min(maximum, interval * 2 ** failures)
655
709
  );
710
+ }
656
711
  }
657
712
  }
658
713
  const observer = {
659
714
  refresh,
660
715
  clear,
661
716
  dispose() {
662
- if (disposed) return;
717
+ if (disposed) {
718
+ return;
719
+ }
663
720
  disposed = true;
664
721
  controller?.abort();
665
- if (timer !== void 0) clearTimeout(timer);
722
+ if (timer !== void 0) {
723
+ clearTimeout(timer);
724
+ }
666
725
  onDispose();
667
726
  }
668
727
  };
@@ -739,16 +798,18 @@ function questConfiguration(release) {
739
798
  // src/deployments.ts
740
799
  import { z as z7 } from "zod";
741
800
  var projectCatalogArtifactSchema = catalogDeploymentSchema.omit({ expectedRevision: true }).extend({
801
+ trackers: z7.array(trackerArtifactSchema).max(50).default([]),
742
802
  pointLedgers: z7.array(pointLedgerDeclarationSchema).max(50).default([]),
743
803
  referrals: z7.array(referralArtifactSchema).max(50).default([]),
744
804
  leaderboards: z7.array(leaderboardArtifactSchema).max(50).default([])
745
805
  });
746
806
  var projectDeploymentSchema = projectCatalogArtifactSchema.extend({ releases: z7.array(releaseSchema).max(50).default([]) }).refine(
747
- (value) => value.pointLedgers.length > 0 || value.releases.length > 0 || value.leaderboards.length > 0 || value.referrals.length > 0 || value.types.length > 0 || value.collections.length > 0 || value.supportedInteractions !== void 0,
748
- "No resources configured. Add quests, questTypes, pointLedgers, leaderboards, referrals, collections, or supportedInteractions to relay.json."
807
+ (value) => value.trackers.length > 0 || value.pointLedgers.length > 0 || value.releases.length > 0 || value.leaderboards.length > 0 || value.referrals.length > 0 || value.types.length > 0 || value.collections.length > 0 || value.supportedInteractions !== void 0,
808
+ "No resources configured. Add trackers, quests, questTypes, pointLedgers, leaderboards, referrals, collections, or supportedInteractions to relay.json."
749
809
  );
750
810
  var deploymentPreviewSchema = z7.object({
751
811
  id: identifier,
812
+ trackers: z7.array(publishedTrackerSchema).default([]),
752
813
  baseRevision: z7.number().int().nonnegative(),
753
814
  pointLedgers: z7.array(pointLedgerSchema).default([]),
754
815
  releases: z7.array(publishedReleaseSchema),
@@ -792,13 +853,21 @@ var participantExchangeSchema = z8.object({
792
853
  function createRelayClient(options) {
793
854
  async function send(path, init = {}) {
794
855
  const headers = new Headers(init.headers);
795
- if (options.token) headers.set("Authorization", `Bearer ${options.token}`);
796
- if (options.organization)
856
+ if (options.token) {
857
+ headers.set("Authorization", `Bearer ${options.token}`);
858
+ }
859
+ if (options.organization) {
797
860
  headers.set("X-Relay-Organization", options.organization);
798
- if (options.project) headers.set("X-Relay-Project", options.project);
799
- if (options.environment)
861
+ }
862
+ if (options.project) {
863
+ headers.set("X-Relay-Project", options.project);
864
+ }
865
+ if (options.environment) {
800
866
  headers.set("X-Relay-Environment", options.environment);
801
- if (init.method && init.method !== "GET") headers.set("X-Relay-CSRF", "1");
867
+ }
868
+ if (init.method && init.method !== "GET") {
869
+ headers.set("X-Relay-CSRF", "1");
870
+ }
802
871
  const response = await fetch(options.baseUrl + path, {
803
872
  ...init,
804
873
  signal: init.signal ? AbortSignal.any([init.signal, AbortSignal.timeout(15e3)]) : AbortSignal.timeout(15e3),
@@ -828,7 +897,9 @@ function createRelayClient(options) {
828
897
  response = await send(path, init);
829
898
  } catch (error) {
830
899
  const transient = error instanceof RelayError && [500, 502, 503, 504].includes(error.status) || error instanceof TypeError || error instanceof DOMException && error.name === "TimeoutError";
831
- if (!transient || attempt === 2) throw error;
900
+ if (!transient || attempt === 2) {
901
+ throw error;
902
+ }
832
903
  await new Promise(
833
904
  (resolve) => setTimeout(resolve, attempt === 0 ? 250 : 750)
834
905
  );
@@ -840,8 +911,11 @@ function createRelayClient(options) {
840
911
  }
841
912
  function query(path, values) {
842
913
  const params = new URLSearchParams();
843
- for (const [key, value] of Object.entries(values))
844
- if (value !== void 0) params.set(key, String(value));
914
+ for (const [key, value] of Object.entries(values)) {
915
+ if (value !== void 0) {
916
+ params.set(key, String(value));
917
+ }
918
+ }
845
919
  return path + "?" + params;
846
920
  }
847
921
  function json(body) {
@@ -883,11 +957,15 @@ function createRelayClient(options) {
883
957
  );
884
958
  const observers = /* @__PURE__ */ new Set();
885
959
  async function changeSession(task) {
886
- for (const observer of observers) observer.clear();
960
+ for (const observer of observers) {
961
+ observer.clear();
962
+ }
887
963
  try {
888
964
  return await task();
889
965
  } finally {
890
- for (const observer of observers) void observer.refresh();
966
+ for (const observer of observers) {
967
+ void observer.refresh();
968
+ }
891
969
  }
892
970
  }
893
971
  const experience = () => request("/v1/me/experience", questExperienceSchema);
@@ -895,7 +973,9 @@ function createRelayClient(options) {
895
973
  const quest = (await experience()).quests.find(
896
974
  (q) => q.quest === id || q.key === id
897
975
  );
898
- if (!quest) throw new RelayError("Quest not found", 404);
976
+ if (!quest) {
977
+ throw new RelayError("Quest not found", 404);
978
+ }
899
979
  return quest;
900
980
  }
901
981
  return {
@@ -924,18 +1004,21 @@ function createRelayClient(options) {
924
1004
  quests: {
925
1005
  get: getQuest,
926
1006
  retry: async (view) => {
927
- if (view.trigger.kind === "automatic")
1007
+ if (view.trigger.kind === "automatic") {
928
1008
  throw new Error(
929
1009
  "Automatic quests recover without participant retries."
930
1010
  );
931
- if (!view.attempt || view.attempt.status !== "failed")
1011
+ }
1012
+ if (!view.attempt || view.attempt.status !== "failed") {
932
1013
  throw new Error("There is no failed attempt to retry.");
1014
+ }
933
1015
  const session = await request(
934
1016
  "/v1/auth/session",
935
1017
  sessionSchema.nullable()
936
1018
  );
937
- if (session?.principal.kind !== "member")
1019
+ if (session?.principal.kind !== "member") {
938
1020
  throw new Error("Sign in to retry verification.");
1021
+ }
939
1022
  return request(`/v1/attempts/${view.attempt.id}/retry`, attemptSchema, {
940
1023
  ...json({}),
941
1024
  headers: {
@@ -949,7 +1032,9 @@ function createRelayClient(options) {
949
1032
  const collection = (await experience()).collections.find(
950
1033
  (c) => c.id === id || c.key === id
951
1034
  );
952
- if (!collection) throw new RelayError("Collection not found", 404);
1035
+ if (!collection) {
1036
+ throw new RelayError("Collection not found", 404);
1037
+ }
953
1038
  return collection;
954
1039
  },
955
1040
  open: questActions(submission, getQuest),
@@ -1110,8 +1195,9 @@ function createRelayClient(options) {
1110
1195
  requestOptions
1111
1196
  );
1112
1197
  const type = response.headers.get("Content-Type");
1113
- if (type !== "image/jpeg" && type !== "image/png")
1198
+ if (type !== "image/jpeg" && type !== "image/png") {
1114
1199
  throw new RelayError("Unexpected evidence content type", 502);
1200
+ }
1115
1201
  return response.blob();
1116
1202
  }
1117
1203
  },