@domino-sdk/relay 1.0.0-next.7 → 1.0.0-next.8

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 (30) hide show
  1. package/dist/authoring.d.ts +11 -11
  2. package/dist/authoring.js +2 -2
  3. package/dist/avatar.d.ts +2 -2
  4. package/dist/browser.d.ts +217 -2285
  5. package/dist/browser.js +6 -6
  6. package/dist/{chunk-AWLLGCKU.js → chunk-6A6K5RUD.js} +22 -8
  7. package/dist/{chunk-SKETUPST.js → chunk-77H7APVD.js} +1 -1
  8. package/dist/{chunk-UFXBQ6FN.js → chunk-MRIURJE3.js} +2 -2
  9. package/dist/{chunk-ADSP377G.js → chunk-QBOHLT7M.js} +26 -5
  10. package/dist/{chunk-X6FJIPXR.js → chunk-V5R2DMGY.js} +553 -435
  11. package/dist/{chunk-DJZB5RY5.js → chunk-V7EZUZ3Z.js} +345 -243
  12. package/dist/{chunk-UW6R2QGT.js → chunk-VARGF4JS.js} +619 -610
  13. package/dist/{chunk-7HSTFBMO.js → chunk-ZWY3R6B3.js} +5 -3
  14. package/dist/{client-BufGakEt.d.ts → client-CK4nwt7V.d.ts} +307 -2253
  15. package/dist/{images-e6FW9QSx.d.ts → images--vZ9U0kr.d.ts} +2 -2
  16. package/dist/index.d.ts +6 -6
  17. package/dist/index.js +19 -7
  18. package/dist/internal.d.ts +16 -5
  19. package/dist/internal.js +12 -5
  20. package/dist/{catalog-C09gnPYT.d.ts → pages-BzqJuI69.d.ts} +79 -21
  21. package/dist/portal-proxy.js +6 -6
  22. package/dist/{profiles-BtOXMGcC.d.ts → profiles-DDMFBRKe.d.ts} +1 -1
  23. package/dist/react/index.d.ts +156 -2224
  24. package/dist/react/index.js +12 -10
  25. package/dist/{schema-DCapSPHz.d.ts → schema-pnuWqvGn.d.ts} +8 -8
  26. package/dist/schema.d.ts +64 -64
  27. package/dist/schema.js +15 -5
  28. package/dist/{submission-input-Bter6XiI.d.ts → submission-input-C9qm9ixd.d.ts} +8 -8
  29. package/dist/{wallet-Bu6sqJvQ.d.ts → wallet-BGOfFkG9.d.ts} +1 -1
  30. package/package.json +1 -1
@@ -3,10 +3,13 @@ import {
3
3
  pointLedgerReferenceSchema,
4
4
  questExperienceSchema,
5
5
  rewardDeliveriesSchema
6
- } from "./chunk-UFXBQ6FN.js";
6
+ } from "./chunk-MRIURJE3.js";
7
7
  import {
8
8
  AVATAR_UPLOAD_LIMIT,
9
+ RelayError,
10
+ RelayErrorCode,
9
11
  avatarUploadTypes,
12
+ clientError,
10
13
  engagementBoundaryPageSchema,
11
14
  engagementPageQuerySchema,
12
15
  engagementPageSchema,
@@ -16,6 +19,7 @@ import {
16
19
  leaderboardQuerySchema,
17
20
  linkedWalletSchema,
18
21
  ownProfileSchema,
22
+ parseInput,
19
23
  profileCompletionPageSchema,
20
24
  profileCompletionQuerySchema,
21
25
  profileReferralSchema,
@@ -27,20 +31,18 @@ import {
27
31
  referralSummarySchema,
28
32
  walletAddressInput,
29
33
  walletAddressInputSchema,
30
- walletChallengeInputSchema,
31
- walletChallengeSchema,
32
- walletConnectionSchema,
33
34
  walletMessageChallengeSchema,
34
- walletProofSchema,
35
35
  walletSignInNetworksSchema,
36
36
  walletSignatureSchema,
37
37
  walletUnlinkInputSchema
38
- } from "./chunk-DJZB5RY5.js";
38
+ } from "./chunk-V7EZUZ3Z.js";
39
39
  import {
40
40
  exchangeResultSchema,
41
+ exchangeSchema,
41
42
  memberSummarySchema,
42
- sessionSchema
43
- } from "./chunk-ADSP377G.js";
43
+ participantSessionSchema,
44
+ sessionMember
45
+ } from "./chunk-QBOHLT7M.js";
44
46
  import {
45
47
  accountConnectionSchema,
46
48
  attemptSchema,
@@ -55,33 +57,17 @@ import {
55
57
  contentQualificationPageSchema,
56
58
  contentQualificationQuerySchema,
57
59
  contentReleaseInputSchema,
58
- entitlementSchema,
59
- errorSchema,
60
60
  evidenceSchema,
61
61
  identifier,
62
62
  memberProgressSchema,
63
- publishedReleaseSchema,
63
+ pageQuerySchema,
64
64
  quizEvidenceSchema,
65
- snapshotSchema,
66
65
  submissionEvidenceSchema,
67
- templateSchema,
66
+ submitSchema,
68
67
  tieredRewardReferenceSchema,
69
68
  validateSubmissionValue,
70
69
  valueEvidenceSchema
71
- } from "./chunk-UW6R2QGT.js";
72
-
73
- // src/errors.ts
74
- var RelayError = class extends Error {
75
- constructor(message, status, missing = [], code) {
76
- super(message);
77
- this.status = status;
78
- this.missing = missing;
79
- this.code = code;
80
- }
81
- status;
82
- missing;
83
- code;
84
- };
70
+ } from "./chunk-VARGF4JS.js";
85
71
 
86
72
  // src/submissions.ts
87
73
  import { z } from "zod";
@@ -130,15 +116,20 @@ function submissions(transport, configuredStore, namespace) {
130
116
  input: "photo"
131
117
  }) {
132
118
  if (!configuredStore) {
133
- throw new Error(
134
- "Submissions require durable storage. Use createBrowserRelayClient or provide a SubmissionStore."
119
+ throw clientError(
120
+ "Submissions require durable storage. Use createBrowserRelayClient or provide a SubmissionStore.",
121
+ RelayErrorCode.SubmissionStorage
135
122
  );
136
123
  }
137
124
  const store = configuredStore;
138
- identifier.parse(quest);
125
+ parseInput(identifier, quest);
139
126
  const session = await transport.session();
140
- if (session?.principal.kind !== "member") {
141
- throw new Error("Sign in to submit an activity");
127
+ const member = sessionMember(session);
128
+ if (!session || !member) {
129
+ throw clientError(
130
+ "Sign in to submit an activity",
131
+ RelayErrorCode.NotSignedIn
132
+ );
142
133
  }
143
134
  const sessionId = session.id;
144
135
  const key = JSON.stringify([
@@ -147,14 +138,15 @@ function submissions(transport, configuredStore, namespace) {
147
138
  session.organization,
148
139
  session.environment,
149
140
  session.project,
150
- session.principal.member,
141
+ member,
151
142
  quest
152
143
  ]);
153
144
  async function currentSession() {
154
145
  const current = await transport.session();
155
146
  if (current?.id !== sessionId) {
156
- throw new Error(
157
- "Your session changed. Reopen the submission before continuing."
147
+ throw clientError(
148
+ "Your session changed. Reopen the submission before continuing.",
149
+ RelayErrorCode.StaleSession
158
150
  );
159
151
  }
160
152
  }
@@ -165,15 +157,17 @@ function submissions(transport, configuredStore, namespace) {
165
157
  }
166
158
  const parsed = recordSchema.safeParse(saved);
167
159
  if (!parsed.success || parsed.data.quest !== quest) {
168
- throw new Error(
169
- "Saved submission could not be read. Do not create another claim until it is recovered."
160
+ throw clientError(
161
+ "Saved submission could not be read. Do not create another claim until it is recovered.",
162
+ RelayErrorCode.SubmissionStorage
170
163
  );
171
164
  }
172
165
  const record = parsed.data;
173
166
  const input = record.stage === "review" ? record.draft?.kind : "input" in record ? record.input.kind : "photo";
174
167
  if (!options.recoverPrevious && input !== options.input) {
175
- throw new Error(
176
- "Saved submission uses a different input type. Reopen it with its original type to recover it."
168
+ throw clientError(
169
+ "Saved submission uses a different input type. Reopen it with its original type to recover it.",
170
+ RelayErrorCode.SubmissionConflict
177
171
  );
178
172
  }
179
173
  return record;
@@ -222,11 +216,12 @@ function submissions(transport, configuredStore, namespace) {
222
216
  await store.write(key, null);
223
217
  throw error;
224
218
  }
225
- if (error instanceof RelayError && error.status === 409 && (error.code === "quest-updated" || error.code === "stale-quiz") && record.stage === "claim") {
219
+ const changed = !(error instanceof RelayError) || error.status !== 409 ? null : error.code === "quest-updated" ? "quest-updated" : error.code === "stale-quiz" ? "stale-quiz" : null;
220
+ if (changed && error instanceof RelayError && record.stage === "claim") {
226
221
  const draft = "input" in record ? record.input : "file" in record && record.file ? { kind: "photo", file: record.file } : null;
227
222
  await store.write(
228
223
  key,
229
- error.code === "quest-updated" ? {
224
+ changed === "quest-updated" ? {
230
225
  id: record.id,
231
226
  quest,
232
227
  release: record.release,
@@ -234,7 +229,7 @@ function submissions(transport, configuredStore, namespace) {
234
229
  draft
235
230
  } : null
236
231
  );
237
- throw new QuestUpdatedError(error.message, error.code, draft);
232
+ throw new QuestUpdatedError(error.message, changed, draft, error);
238
233
  }
239
234
  throw error;
240
235
  }
@@ -260,8 +255,9 @@ function submissions(transport, configuredStore, namespace) {
260
255
  );
261
256
  }
262
257
  if (existing && existing.stage !== "submitted" && existing.stage !== "review") {
263
- throw new Error(
264
- "A submission is unfinished. Resume it before choosing new input."
258
+ throw clientError(
259
+ "A submission is unfinished. Resume it before choosing new input.",
260
+ RelayErrorCode.SubmissionConflict
265
261
  );
266
262
  }
267
263
  if (existing?.stage === "submitted") {
@@ -286,14 +282,16 @@ function submissions(transport, configuredStore, namespace) {
286
282
  return;
287
283
  }
288
284
  if (record.stage !== "submitted") {
289
- throw new Error(
290
- "Resume the unfinished submission before starting over."
285
+ throw clientError(
286
+ "Resume the unfinished submission before starting over.",
287
+ RelayErrorCode.SubmissionConflict
291
288
  );
292
289
  }
293
290
  const attempt = await claim(record);
294
291
  if (attempt.status !== "failed" && attempt.status !== "rejected" && !(options.repeatable && attempt.status === "accepted")) {
295
- throw new Error(
296
- "Only a failed or rejected submission can be discarded."
292
+ throw clientError(
293
+ "Only a failed or rejected submission can be discarded.",
294
+ RelayErrorCode.SubmissionConflict
297
295
  );
298
296
  }
299
297
  await store.write(key, null);
@@ -315,7 +313,10 @@ function submissions(transport, configuredStore, namespace) {
315
313
  await currentSession();
316
314
  const record = await read();
317
315
  if (!record) {
318
- throw new Error("There is no saved submission to resume");
316
+ throw clientError(
317
+ "There is no saved submission to resume",
318
+ RelayErrorCode.SubmissionConflict
319
+ );
319
320
  }
320
321
  return resumeRecord(record);
321
322
  });
@@ -326,7 +327,7 @@ function submissions(transport, configuredStore, namespace) {
326
327
  return {
327
328
  ...handle,
328
329
  submit(value) {
329
- const input = valueEvidenceSchema.parse({ kind, value });
330
+ const input = parseInput(valueEvidenceSchema, { kind, value });
330
331
  return submit({
331
332
  id: crypto.randomUUID(),
332
333
  quest,
@@ -341,7 +342,7 @@ function submissions(transport, configuredStore, namespace) {
341
342
  return {
342
343
  ...handle,
343
344
  submit(answers, version) {
344
- const input = quizEvidenceSchema.parse({
345
+ const input = parseInput(quizEvidenceSchema, {
345
346
  kind: "quiz",
346
347
  answers,
347
348
  ...version || options.release ? { release: version?.release ?? options.release } : {}
@@ -374,12 +375,18 @@ function submissions(transport, configuredStore, namespace) {
374
375
  ...handle,
375
376
  async submit(file) {
376
377
  if (!["image/jpeg", "image/png"].includes(file.type) || file.size === 0 || file.size > 5 * 1024 * 1024) {
377
- throw new Error("Choose a JPG or PNG photo under 5 MB");
378
+ throw clientError(
379
+ "Choose a JPG or PNG photo under 5 MB",
380
+ RelayErrorCode.InvalidInput
381
+ );
378
382
  }
379
383
  const bytes = new Uint8Array(await file.slice(0, 4).arrayBuffer());
380
384
  const signature = file.type === "image/png" ? [137, 80, 78, 71] : [255, 216, 255];
381
385
  if (!signature.every((byte, index) => bytes[index] === byte)) {
382
- throw new Error("Image content does not match its file type");
386
+ throw clientError(
387
+ "Image content does not match its file type",
388
+ RelayErrorCode.InvalidInput
389
+ );
383
390
  }
384
391
  return submit({
385
392
  id: crypto.randomUUID(),
@@ -394,11 +401,18 @@ function submissions(transport, configuredStore, namespace) {
394
401
  return open;
395
402
  }
396
403
  var QuestUpdatedError = class extends RelayError {
397
- constructor(message, code, draft) {
398
- super(message, 409, [], code);
404
+ constructor(message, code, draft, source) {
405
+ super(message, {
406
+ kind: source?.kind ?? "client",
407
+ status: source?.status,
408
+ code,
409
+ operationId: source?.operationId,
410
+ cause: source
411
+ });
399
412
  this.draft = draft;
400
413
  }
401
414
  draft;
415
+ name = "QuestUpdatedError";
402
416
  };
403
417
 
404
418
  // src/client.ts
@@ -410,8 +424,9 @@ function observeProgress(transport, listener, options = {}, onDispose = () => {
410
424
  const interval = options.intervalMs ?? 1500;
411
425
  const maximum = options.maxBackoffMs ?? 3e4;
412
426
  if (!Number.isFinite(interval) || interval <= 0 || !Number.isFinite(maximum) || maximum < interval) {
413
- throw new Error(
414
- "Progress polling requires a positive interval and a maximum at least as large"
427
+ throw clientError(
428
+ "Progress polling requires a positive interval and a maximum at least as large",
429
+ RelayErrorCode.InvalidInput
415
430
  );
416
431
  }
417
432
  let disposed = false;
@@ -449,7 +464,8 @@ function observeProgress(transport, listener, options = {}, onDispose = () => {
449
464
  sessionId = session?.id ?? null;
450
465
  listener({ kind: "session", session });
451
466
  }
452
- if (session?.principal.kind === "member") {
467
+ const member = sessionMember(session);
468
+ if (session && member) {
453
469
  const progress = await transport.progress(session.id, request.signal);
454
470
  if (!current()) {
455
471
  return;
@@ -458,7 +474,7 @@ function observeProgress(transport, listener, options = {}, onDispose = () => {
458
474
  if (!current()) {
459
475
  return;
460
476
  }
461
- if (confirmed?.id !== session.id || progress.member !== session.principal.member) {
477
+ if (confirmed?.id !== session.id || progress.member !== member) {
462
478
  sessionId = void 0;
463
479
  listener({ kind: "loading" });
464
480
  return;
@@ -581,14 +597,17 @@ function questActions(submission, latest) {
581
597
  resume: () => run(handle.resume),
582
598
  submit: (value) => run(() => {
583
599
  if (view.trigger.kind !== "manual" || view.trigger.input !== "text" && view.trigger.input !== "url") {
584
- throw new Error("Quest input changed");
600
+ throw clientError(
601
+ "Quest input changed",
602
+ RelayErrorCode.QuestUpdated
603
+ );
585
604
  }
586
605
  const problem = validateSubmissionValue(
587
606
  { kind, value },
588
607
  view.trigger
589
608
  );
590
609
  if (problem) {
591
- throw new Error(problem);
610
+ throw clientError(problem, RelayErrorCode.InvalidInput);
592
611
  }
593
612
  return handle.submit(value);
594
613
  })
@@ -647,9 +666,76 @@ function questActions(submission, latest) {
647
666
  }
648
667
 
649
668
  // src/client.ts
669
+ var TIMEOUT_MS = 15e3;
670
+ var missingEntrySchema = z2.object({ quest: identifier, title: z2.string() });
671
+ function errorBody(value) {
672
+ const body = value !== null && typeof value === "object" ? value : {};
673
+ return {
674
+ message: typeof body.error === "string" && body.error.trim() ? body.error : void 0,
675
+ code: typeof body.code === "string" && body.code.trim() ? body.code : void 0,
676
+ missing: Array.isArray(body.missing) ? body.missing.flatMap((entry) => {
677
+ const parsed = missingEntrySchema.safeParse(entry);
678
+ return parsed.success ? [parsed.data] : [];
679
+ }) : []
680
+ };
681
+ }
682
+ async function fromSigner(step) {
683
+ try {
684
+ return await step();
685
+ } catch (error) {
686
+ if (error instanceof RelayError) {
687
+ throw error;
688
+ }
689
+ throw clientError(
690
+ error instanceof Error && error.message ? error.message : "The wallet did not complete the request",
691
+ RelayErrorCode.WalletSigner,
692
+ { cause: error }
693
+ );
694
+ }
695
+ }
696
+ var uncertainStatuses = /* @__PURE__ */ new Set([500, 502, 504]);
697
+ var methodOf = (call) => call.method ?? (call.json === void 0 ? "GET" : "POST");
698
+ function withQuery(path, values) {
699
+ const params = new URLSearchParams();
700
+ for (const [key, value] of Object.entries(values)) {
701
+ if (value !== void 0) {
702
+ params.set(key, String(value));
703
+ }
704
+ }
705
+ const search = params.toString();
706
+ return search ? path + "?" + search : path;
707
+ }
708
+ function interrupted(call, error, stage) {
709
+ const details = {
710
+ status: stage.status,
711
+ operationId: call.operationId,
712
+ uncertain: stage.sent && methodOf(call) !== "GET",
713
+ cause: error
714
+ };
715
+ if (call.options?.signal?.aborted) {
716
+ return new RelayError("The request was cancelled", {
717
+ ...details,
718
+ kind: "aborted",
719
+ code: RelayErrorCode.Aborted
720
+ });
721
+ }
722
+ if (call.deadline?.aborted) {
723
+ return new RelayError("Relay did not respond in time", {
724
+ ...details,
725
+ kind: "timeout",
726
+ code: RelayErrorCode.Timeout
727
+ });
728
+ }
729
+ return new RelayError(
730
+ stage.status === void 0 ? "Could not reach Relay" : "Relay's response was cut off",
731
+ { ...details, kind: "network", code: RelayErrorCode.Network }
732
+ );
733
+ }
650
734
  function createRelayClient(options) {
651
- async function send(path, init = {}) {
652
- const headers = new Headers(init.headers);
735
+ async function send(path, call = {}) {
736
+ const method = methodOf(call);
737
+ const headers = new Headers(call.headers);
738
+ let body2 = call.body;
653
739
  if (options.token) {
654
740
  headers.set("Authorization", `Bearer ${options.token}`);
655
741
  }
@@ -662,38 +748,97 @@ function createRelayClient(options) {
662
748
  if (options.environment) {
663
749
  headers.set("X-Relay-Environment", options.environment);
664
750
  }
665
- if (init.method && init.method !== "GET") {
751
+ if (call.options?.session) {
752
+ headers.set("X-Relay-Session", call.options.session);
753
+ }
754
+ if (call.json !== void 0) {
755
+ headers.set("Content-Type", "application/json");
756
+ body2 = JSON.stringify(call.json);
757
+ }
758
+ if (method !== "GET") {
666
759
  headers.set("X-Relay-CSRF", "1");
667
760
  }
668
- const response = await fetch(options.baseUrl + path, {
669
- ...init,
670
- signal: init.signal ? AbortSignal.any([init.signal, AbortSignal.timeout(15e3)]) : AbortSignal.timeout(15e3),
671
- headers
672
- });
761
+ const caller = call.options?.signal;
762
+ const deadline = AbortSignal.timeout(TIMEOUT_MS);
763
+ call.deadline = deadline;
764
+ if (caller?.aborted) {
765
+ throw interrupted(call, caller.reason, { sent: false });
766
+ }
767
+ let response;
768
+ try {
769
+ response = await fetch(options.baseUrl + path, {
770
+ method,
771
+ headers,
772
+ body: body2,
773
+ signal: caller ? AbortSignal.any([caller, deadline]) : deadline
774
+ });
775
+ } catch (error) {
776
+ throw interrupted(call, error, { sent: true });
777
+ }
673
778
  if (!response.ok) {
674
- const value = await response.json().catch(() => null);
675
- const error = errorSchema.safeParse(value);
676
- throw new RelayError(
677
- error.success ? error.data.error : `Request failed (${response.status})`,
678
- response.status,
679
- error.success ? error.data.missing : [],
680
- error.success ? error.data.code : void 0
681
- );
779
+ const text = await response.text().catch(() => "");
780
+ let value = null;
781
+ try {
782
+ value = JSON.parse(text);
783
+ } catch {
784
+ }
785
+ const { message, code, missing } = errorBody(value);
786
+ throw new RelayError(message ?? `Request failed (${response.status})`, {
787
+ kind: "http",
788
+ status: response.status,
789
+ code,
790
+ missing,
791
+ operationId: call.operationId,
792
+ uncertain: methodOf(call) !== "GET" && uncertainStatuses.has(response.status)
793
+ });
682
794
  }
683
795
  return response;
684
796
  }
685
- async function request(path, schema, init = {}) {
686
- const response = await send(path, init);
687
- const value = await response.json();
688
- return schema.parse(value);
797
+ async function body(response, call, reader) {
798
+ try {
799
+ return await reader(response);
800
+ } catch (error) {
801
+ throw interrupted(call, error, { sent: true, status: response.status });
802
+ }
803
+ }
804
+ function invalidResponse(status, call, cause) {
805
+ return new RelayError(
806
+ "Relay sent a response this SDK version cannot read. Update @domino-sdk/relay.",
807
+ {
808
+ kind: "invalid-response",
809
+ code: RelayErrorCode.InvalidResponse,
810
+ status,
811
+ operationId: call.operationId,
812
+ // Relay accepted the write, but this client cannot tell what it did.
813
+ uncertain: methodOf(call) !== "GET",
814
+ cause
815
+ }
816
+ );
689
817
  }
690
- async function walletRequest(path, schema, init = {}) {
818
+ async function read(response, schema, call) {
819
+ const text = await body(response, call, (value2) => value2.text());
820
+ let value;
821
+ try {
822
+ value = JSON.parse(text);
823
+ } catch (error) {
824
+ throw invalidResponse(response.status, call, error);
825
+ }
826
+ const parsed = schema.safeParse(value);
827
+ if (!parsed.success) {
828
+ throw invalidResponse(response.status, call, parsed.error);
829
+ }
830
+ return parsed.data;
831
+ }
832
+ async function request(path, schema, call = {}) {
833
+ return read(await send(path, call), schema, call);
834
+ }
835
+ async function walletRequest(path, schema, call = {}) {
691
836
  for (let attempt = 0; ; attempt++) {
692
837
  let response;
693
838
  try {
694
- response = await send(path, init);
839
+ response = await send(path, call);
695
840
  } catch (error) {
696
- const transient = error instanceof RelayError && [500, 502, 503, 504].includes(error.status) || error instanceof TypeError || error instanceof DOMException && error.name === "TimeoutError";
841
+ const transient = error instanceof RelayError && (error.kind === "network" || error.kind === "timeout" || error.kind === "http" && [500, 502, 503, 504].includes(error.status ?? 0));
697
842
  if (!transient || attempt === 2) {
698
843
  throw error;
699
844
  }
@@ -702,54 +847,40 @@ function createRelayClient(options) {
702
847
  );
703
848
  continue;
704
849
  }
705
- const value = await response.json();
706
- return schema.parse(value);
850
+ return read(response, schema, call);
707
851
  }
708
852
  }
709
- function query(path, values) {
710
- const params = new URLSearchParams();
711
- for (const [key, value] of Object.entries(values)) {
712
- if (value !== void 0) {
713
- params.set(key, String(value));
714
- }
853
+ const readSession = (requestOptions) => request("/v1/auth/session", participantSessionSchema.nullable(), {
854
+ options: { signal: requestOptions?.signal }
855
+ });
856
+ async function bound(requestOptions = {}) {
857
+ if (requestOptions.session) {
858
+ return requestOptions;
715
859
  }
716
- return path + "?" + params;
717
- }
718
- function json(body) {
719
- return {
720
- method: "POST",
721
- headers: { "Content-Type": "application/json" },
722
- body: JSON.stringify(body)
723
- };
724
- }
725
- function sessionHeaders(session) {
726
- return { "Content-Type": "application/json", "X-Relay-Session": session };
860
+ const session = await readSession(requestOptions);
861
+ if (session?.principal.kind !== "member") {
862
+ throw clientError("Sign in to continue", RelayErrorCode.NotSignedIn);
863
+ }
864
+ return { ...requestOptions, session: session.id };
727
865
  }
728
866
  const submission = submissions(
729
867
  {
730
- session: () => request("/v1/auth/session", sessionSchema.nullable()),
868
+ session: () => readSession(),
731
869
  upload: (file, action, session) => request("/v1/me/evidence", evidenceSchema, {
732
870
  method: "POST",
733
- headers: {
734
- "Content-Type": file.type,
735
- "X-Relay-Upload-ID": action,
736
- "X-Relay-Session": session
737
- },
738
- body: file
871
+ body: file,
872
+ headers: { "Content-Type": file.type, "X-Relay-Upload-ID": action },
873
+ options: { session },
874
+ operationId: action
739
875
  }),
740
- claim: (input, session) => request("/v1/me/attempts", attemptSchema, {
741
- ...json(input),
742
- headers: {
743
- "Content-Type": "application/json",
744
- "X-Relay-Session": session
745
- }
876
+ claim: async (claim, session) => request("/v1/me/attempts", attemptSchema, {
877
+ json: claim,
878
+ options: { session },
879
+ operationId: claim.actionId
746
880
  }),
747
881
  retry: (attempt, session) => request(`/v1/attempts/${attempt}/retry`, attemptSchema, {
748
- ...json({}),
749
- headers: {
750
- "Content-Type": "application/json",
751
- "X-Relay-Session": session
752
- }
882
+ json: {},
883
+ options: { session }
753
884
  })
754
885
  },
755
886
  options.submissionStore,
@@ -768,180 +899,205 @@ function createRelayClient(options) {
768
899
  }
769
900
  }
770
901
  }
771
- const experience = () => request("/v1/me/experience", questExperienceSchema);
772
- async function getQuest(id) {
773
- const quest = (await experience()).quests.find(
902
+ const experience = (requestOptions) => request("/v1/me/experience", questExperienceSchema, {
903
+ options: requestOptions
904
+ });
905
+ async function getQuest(id, requestOptions) {
906
+ const quest = (await experience(requestOptions)).quests.find(
774
907
  (q) => q.quest === id || q.key === id
775
908
  );
776
909
  if (!quest) {
777
- throw new RelayError("Quest not found", 404);
910
+ throw clientError("Quest not found", RelayErrorCode.NotFound);
778
911
  }
779
912
  return quest;
780
913
  }
914
+ const ok = z2.object({ ok: z2.literal(true) });
915
+ const acknowledged = z2.object({ ok: z2.boolean() });
781
916
  return {
782
917
  referrals: {
783
- list: () => request("/v1/referrals", z2.array(referralDefinitionSchema)),
784
- summary: () => request("/v1/referrals/me", referralSummarySchema),
785
- history: (before) => request(
786
- query("/v1/referrals/history", { before }),
787
- referralHistorySchema
918
+ list: (requestOptions) => request("/v1/referrals", z2.array(referralDefinitionSchema), {
919
+ options: requestOptions
920
+ }),
921
+ summary: (requestOptions) => request("/v1/referrals/me", referralSummarySchema, {
922
+ options: requestOptions
923
+ }),
924
+ /** The member's referral point history, newest first. */
925
+ history: async (query = {}, requestOptions) => request(
926
+ withQuery("/v1/referrals/history", parseInput(pageQuerySchema, query)),
927
+ referralHistorySchema,
928
+ { options: requestOptions }
788
929
  ),
789
- accept: (input) => request("/v1/referrals/accept", z2.object({ ok: z2.literal(true) }), {
790
- method: "POST",
791
- body: JSON.stringify(referralAcceptSchema.parse(input))
792
- })
930
+ accept: async (value, requestOptions) => {
931
+ const body2 = parseInput(referralAcceptSchema, value);
932
+ return request("/v1/referrals/accept", ok, {
933
+ json: body2,
934
+ options: requestOptions,
935
+ operationId: body2.actionId
936
+ });
937
+ }
793
938
  },
794
939
  tieredRewards: {
795
940
  /**
796
941
  * The project's tiered rewards with their keys. Entitlements and reward
797
942
  * definitions name them by ID: use this to find the ID for a key.
798
943
  */
799
- list: () => request("/v1/tiered-rewards", z2.array(tieredRewardReferenceSchema))
944
+ list: (requestOptions) => request("/v1/tiered-rewards", z2.array(tieredRewardReferenceSchema), {
945
+ options: requestOptions
946
+ })
800
947
  },
801
948
  pointLedgers: {
802
949
  /**
803
950
  * The project's point ledgers with their keys and names. Reads return
804
951
  * ledger IDs: use this to find the ID for a key, or a ledger's name.
805
952
  */
806
- list: () => request("/v1/point-ledgers", z2.array(pointLedgerReferenceSchema))
953
+ list: (requestOptions) => request("/v1/point-ledgers", z2.array(pointLedgerReferenceSchema), {
954
+ options: requestOptions
955
+ })
807
956
  },
808
957
  leaderboards: {
809
- list: () => request("/v1/leaderboards", z2.array(leaderboardDefinitionSchema)),
810
- standings: (board, options2 = {}) => {
811
- const parsed = leaderboardQuerySchema.parse(options2);
812
- return request(
813
- query(`/v1/leaderboards/${identifier.parse(board)}`, parsed),
814
- leaderboardPageSchema
815
- );
816
- },
958
+ list: (requestOptions) => request("/v1/leaderboards", z2.array(leaderboardDefinitionSchema), {
959
+ options: requestOptions
960
+ }),
961
+ standings: async (board, query = {}, requestOptions) => request(
962
+ withQuery(
963
+ `/v1/leaderboards/${parseInput(identifier, board)}`,
964
+ parseInput(leaderboardQuerySchema, query)
965
+ ),
966
+ leaderboardPageSchema,
967
+ { options: requestOptions }
968
+ ),
817
969
  /**
818
970
  * A profile's current rank and score on `board` (`profile.id` from a leaderboard row or
819
971
  * `profiles.mine()`). Needs the board's access, like `standings`; a profile this viewer
820
972
  * cannot see is a 404, as in `profiles.get`.
821
973
  */
822
- placement: (board, profile) => request(
823
- `/v1/leaderboards/${identifier.parse(board)}/profiles/${identifier.parse(profile)}`,
824
- leaderboardPlacementSchema
974
+ placement: async (board, profile, requestOptions) => request(
975
+ `/v1/leaderboards/${parseInput(identifier, board)}/profiles/${parseInput(identifier, profile)}`,
976
+ leaderboardPlacementSchema,
977
+ { options: requestOptions }
825
978
  )
826
979
  },
827
980
  /**
828
981
  * Participant profiles: the display name and avatar other participants see where the project
829
- * enables profiles in Console. Render an avatar with `avatarSrc(profile.avatar)`.
982
+ * enables profiles in Console. Render an avatar with `avatarSrc(profile.avatar)` from
983
+ * `@domino-sdk/relay/avatar`.
830
984
  */
831
985
  profiles: {
832
986
  /** The signed-in member's own profile, whatever the project shows to others. */
833
- mine: () => request("/v1/me/profile", ownProfileSchema),
987
+ mine: (requestOptions) => request("/v1/me/profile", ownProfileSchema, {
988
+ options: requestOptions
989
+ }),
834
990
  /**
835
991
  * Changes the member's own name, avatar choice or visibility; omitted fields stay, and null
836
- * returns to the default. `session` (the ID the app read) makes a changed session a 409.
992
+ * returns to the default.
837
993
  */
838
- update: async (input, options2 = {}) => request("/v1/me/profile", ownProfileSchema, {
839
- method: "POST",
840
- headers: {
841
- "Content-Type": "application/json",
842
- ...options2.session ? { "X-Relay-Session": options2.session } : {}
843
- },
844
- body: JSON.stringify(profileUpdateSchema.parse(input))
994
+ update: async (value, requestOptions) => request("/v1/me/profile", ownProfileSchema, {
995
+ json: parseInput(profileUpdateSchema, value),
996
+ options: requestOptions
845
997
  }),
846
998
  /**
847
999
  * Uploads a JPEG, PNG or WebP of at most 5 MB as the member's avatar. The server crops it to
848
1000
  * a 512×512 WebP without metadata. Retrying with the same `uploadId` is idempotent.
849
1001
  */
850
- uploadAvatar: async (file, options2 = {}) => {
1002
+ uploadAvatar: async (file, requestOptions = {}) => {
851
1003
  if (!avatarUploadTypes.includes(file.type)) {
852
- throw new RelayError("Use a JPEG, PNG or WebP image", 400);
1004
+ throw clientError(
1005
+ "Use a JPEG, PNG or WebP image",
1006
+ RelayErrorCode.InvalidInput
1007
+ );
853
1008
  }
854
1009
  if (file.size > AVATAR_UPLOAD_LIMIT) {
855
- throw new RelayError("Use an image smaller than 5 MB", 413);
1010
+ throw clientError(
1011
+ "Use an image smaller than 5 MB",
1012
+ RelayErrorCode.InvalidInput
1013
+ );
856
1014
  }
1015
+ const { uploadId, ...rest } = requestOptions;
1016
+ const operationId = parseInput(identifier, uploadId ?? crypto.randomUUID());
857
1017
  return request("/v1/me/profile/avatar", ownProfileSchema, {
858
1018
  method: "POST",
1019
+ body: file,
859
1020
  headers: {
860
1021
  "Content-Type": file.type,
861
- "X-Relay-Upload-ID": identifier.parse(
862
- options2.uploadId ?? crypto.randomUUID()
863
- ),
864
- ...options2.session ? { "X-Relay-Session": options2.session } : {}
1022
+ "X-Relay-Upload-ID": operationId
865
1023
  },
866
- body: file
1024
+ options: rest,
1025
+ operationId
867
1026
  });
868
1027
  },
869
1028
  /**
870
1029
  * A profile's name and avatar by its public ID (`profile.id` from a leaderboard row or
871
1030
  * `mine()`). A profile that is missing, hidden or not visible to this viewer is a 404.
872
1031
  */
873
- get: (profile) => request(
874
- `/v1/profiles/${identifier.parse(profile)}`,
875
- profileSummarySchema
1032
+ get: async (profile, requestOptions) => request(
1033
+ `/v1/profiles/${parseInput(identifier, profile)}`,
1034
+ profileSummarySchema,
1035
+ { options: requestOptions }
876
1036
  ),
877
1037
  /**
878
1038
  * A page of the profile's completions of quests that show on profiles, most recently
879
- * completed first. Pass the page's `nextCursor` as `cursor` for the next one.
1039
+ * completed first.
880
1040
  */
881
- completions: (profile, options2 = {}) => request(
882
- query(
883
- `/v1/profiles/${identifier.parse(profile)}/completions`,
884
- profileCompletionQuerySchema.parse(options2)
1041
+ completions: async (profile, query = {}, requestOptions) => request(
1042
+ withQuery(
1043
+ `/v1/profiles/${parseInput(identifier, profile)}/completions`,
1044
+ parseInput(profileCompletionQuerySchema, query)
885
1045
  ),
886
- profileCompletionPageSchema
1046
+ profileCompletionPageSchema,
1047
+ { options: requestOptions }
887
1048
  ),
888
1049
  /**
889
1050
  * The profile's invited and qualified counts and milestones in one referral program. Needs
890
1051
  * a signed-in participant, as referral programs do.
891
1052
  */
892
- referral: (profile, program) => request(
893
- `/v1/profiles/${identifier.parse(profile)}/referrals/${identifier.parse(program)}`,
894
- profileReferralSchema
1053
+ referral: async (profile, program, requestOptions) => request(
1054
+ `/v1/profiles/${parseInput(identifier, profile)}/referrals/${parseInput(identifier, program)}`,
1055
+ profileReferralSchema,
1056
+ { options: requestOptions }
895
1057
  )
896
1058
  },
897
1059
  quests: {
898
1060
  get: getQuest,
899
- retry: async (view) => {
1061
+ /** Retries verification of the quest's failed attempt. */
1062
+ retry: async (view, requestOptions) => {
900
1063
  if (view.trigger.kind === "automatic") {
901
- throw new Error(
902
- "Automatic quests recover without participant retries."
1064
+ throw clientError(
1065
+ "Automatic quests recover without participant retries.",
1066
+ RelayErrorCode.RetryUnavailable
903
1067
  );
904
1068
  }
905
1069
  if (!view.attempt || view.attempt.status !== "failed") {
906
- throw new Error("There is no failed attempt to retry.");
907
- }
908
- const session = await request(
909
- "/v1/auth/session",
910
- sessionSchema.nullable()
911
- );
912
- if (session?.principal.kind !== "member") {
913
- throw new Error("Sign in to retry verification.");
1070
+ throw clientError(
1071
+ "There is no failed attempt to retry.",
1072
+ RelayErrorCode.RetryUnavailable
1073
+ );
914
1074
  }
915
1075
  return request(`/v1/attempts/${view.attempt.id}/retry`, attemptSchema, {
916
- ...json({}),
917
- headers: {
918
- "Content-Type": "application/json",
919
- "X-Relay-Session": session.id
920
- }
1076
+ json: {},
1077
+ options: await bound(requestOptions)
921
1078
  });
922
1079
  },
923
1080
  experience,
924
- collection: async (id) => {
925
- const collection = (await experience()).collections.find(
1081
+ collection: async (id, requestOptions) => {
1082
+ const collection = (await experience(requestOptions)).collections.find(
926
1083
  (c) => c.id === id || c.key === id
927
1084
  );
928
1085
  if (!collection) {
929
- throw new RelayError("Collection not found", 404);
1086
+ throw clientError("Collection not found", RelayErrorCode.NotFound);
930
1087
  }
931
1088
  return collection;
932
1089
  },
933
- open: questActions(submission, getQuest),
934
- observe: (listener, options2) => {
1090
+ open: questActions(submission, (id) => getQuest(id)),
1091
+ observe: (listener, observeOptions) => {
935
1092
  const observer = observeProgress(
936
1093
  {
937
- session: (signal) => request("/v1/auth/session", sessionSchema.nullable(), { signal }),
1094
+ session: (signal) => readSession({ signal }),
938
1095
  progress: (session, signal) => request("/v1/me/experience", questExperienceSchema, {
939
- signal,
940
- headers: { "X-Relay-Session": session }
1096
+ options: { session, signal }
941
1097
  })
942
1098
  },
943
1099
  listener,
944
- options2,
1100
+ observeOptions,
945
1101
  () => observers.delete(observer)
946
1102
  );
947
1103
  observers.add(observer);
@@ -952,75 +1108,59 @@ function createRelayClient(options) {
952
1108
  },
953
1109
  /** The signed-in member's linked Solana, EVM and Cosmos wallets for this project. */
954
1110
  wallets: {
955
- list: () => walletRequest(
1111
+ list: (requestOptions) => walletRequest(
956
1112
  "/v1/me/wallets",
957
- z2.object({ wallets: z2.array(linkedWalletSchema) })
1113
+ z2.object({ wallets: z2.array(linkedWalletSchema) }),
1114
+ { options: requestOptions }
958
1115
  ).then((result) => result.wallets),
959
- challenge: (input, session) => walletRequest(
1116
+ challenge: async (value, requestOptions) => walletRequest(
960
1117
  "/v1/me/wallets/challenge",
961
1118
  walletMessageChallengeSchema,
962
1119
  {
963
- ...json(walletAddressInputSchema.parse(input)),
964
- headers: sessionHeaders(session)
1120
+ json: parseInput(walletAddressInputSchema, value),
1121
+ options: await bound(requestOptions)
965
1122
  }
966
1123
  ),
967
- verify: (input, session) => walletRequest("/v1/me/wallets/verify", linkedWalletSchema, {
968
- ...json(walletSignatureSchema.parse(input)),
969
- headers: sessionHeaders(session)
1124
+ verify: async (value, requestOptions) => walletRequest("/v1/me/wallets/verify", linkedWalletSchema, {
1125
+ json: parseInput(walletSignatureSchema, value),
1126
+ options: await bound(requestOptions)
970
1127
  }),
971
1128
  /** The address stays bound to this member: another member still cannot link it. */
972
- unlink: (wallet) => request(
973
- "/v1/me/wallets/unlink",
974
- z2.object({ ok: z2.literal(true) }),
975
- json(
976
- walletUnlinkInputSchema.parse({
977
- chain: wallet.chain,
978
- address: wallet.address
979
- })
980
- )
981
- ),
1129
+ unlink: async (wallet, requestOptions) => request("/v1/me/wallets/unlink", ok, {
1130
+ json: parseInput(walletUnlinkInputSchema, {
1131
+ chain: wallet.chain,
1132
+ address: wallet.address
1133
+ }),
1134
+ options: requestOptions
1135
+ }),
982
1136
  /** Asks the signer for its address, signs the returned challenge, and links the wallet. */
983
- link: async (signer) => {
984
- const session = await request(
985
- "/v1/auth/session",
986
- sessionSchema.nullable()
987
- );
988
- if (!session) {
989
- throw new RelayError("Sign in to continue", 401);
990
- }
1137
+ link: async (signer, requestOptions) => {
1138
+ const session = await bound(requestOptions);
991
1139
  const challenge = await walletRequest(
992
1140
  "/v1/me/wallets/challenge",
993
1141
  walletMessageChallengeSchema,
994
1142
  {
995
- ...json(
996
- walletAddressInputSchema.parse(await walletAddressInput(signer))
1143
+ json: parseInput(
1144
+ walletAddressInputSchema,
1145
+ await fromSigner(() => walletAddressInput(signer))
997
1146
  ),
998
- headers: sessionHeaders(session.id)
1147
+ options: session
999
1148
  }
1000
1149
  );
1001
- const proof = await signer.sign(challenge.message, challenge.address);
1150
+ const proof = await fromSigner(
1151
+ () => signer.sign(challenge.message, challenge.address)
1152
+ );
1002
1153
  return walletRequest("/v1/me/wallets/verify", linkedWalletSchema, {
1003
- ...json(
1004
- walletSignatureSchema.parse({ challenge: challenge.id, ...proof })
1005
- ),
1006
- headers: sessionHeaders(session.id)
1154
+ json: parseInput(walletSignatureSchema, {
1155
+ challenge: challenge.id,
1156
+ ...proof
1157
+ }),
1158
+ options: session
1007
1159
  });
1008
1160
  }
1009
1161
  },
1010
- /** @deprecated Use `wallets` (multi-chain). This reads and replaces the newest Solana wallet. */
1011
- wallet: {
1012
- connection: () => walletRequest("/v1/me/wallet", walletConnectionSchema.nullable()),
1013
- challenge: (input, session) => walletRequest("/v1/me/wallet/challenge", walletChallengeSchema, {
1014
- ...json(walletChallengeInputSchema.parse(input)),
1015
- headers: sessionHeaders(session)
1016
- }),
1017
- verify: (input, session) => walletRequest("/v1/me/wallet/verify", walletConnectionSchema, {
1018
- ...json(walletProofSchema.parse(input)),
1019
- headers: sessionHeaders(session)
1020
- })
1021
- },
1022
1162
  auth: {
1023
- providers: () => request(
1163
+ providers: (requestOptions) => request(
1024
1164
  "/v1/auth/providers",
1025
1165
  z2.object({
1026
1166
  providers: z2.array(
@@ -1033,108 +1173,116 @@ function createRelayClient(options) {
1033
1173
  /** The wallet sign-in this project enabled; older servers omit it. */
1034
1174
  wallets: walletSignInNetworksSchema.default([]),
1035
1175
  demo: z2.boolean()
1036
- })
1037
- ),
1038
- start: (provider, mode) => request(
1039
- "/v1/auth/start",
1040
- z2.object({ url: z2.string() }),
1041
- json({ provider, mode })
1042
- ),
1043
- emailStart: (email) => request(
1044
- "/v1/auth/email/start",
1045
- z2.object({ challenge: z2.string() }),
1046
- json({ email })
1047
- ),
1048
- emailVerify: (challenge, code) => changeSession(
1049
- () => request(
1050
- "/v1/auth/email/verify",
1051
- sessionSchema,
1052
- json({ challenge, code })
1053
- )
1176
+ }),
1177
+ { options: requestOptions }
1054
1178
  ),
1179
+ start: async (provider, mode, requestOptions) => request("/v1/auth/start", z2.object({ url: z2.string() }), {
1180
+ json: {
1181
+ provider: parseInput(authProviderSchema, provider),
1182
+ mode: parseInput(z2.enum(["sign-in", "connect"]), mode)
1183
+ },
1184
+ options: requestOptions
1185
+ }),
1186
+ emailStart: async (email, requestOptions) => request("/v1/auth/email/start", z2.object({ challenge: z2.string() }), {
1187
+ json: { email: parseInput(z2.email().max(254), email.trim()) },
1188
+ options: requestOptions
1189
+ }),
1190
+ emailVerify: async (challenge, code, requestOptions) => {
1191
+ const body2 = {
1192
+ challenge: parseInput(z2.string().min(1).max(200), challenge),
1193
+ code: parseInput(
1194
+ z2.string().regex(/^\d{6}$/, "Enter the 6-digit code"),
1195
+ code.trim()
1196
+ )
1197
+ };
1198
+ return changeSession(
1199
+ () => request("/v1/auth/email/verify", participantSessionSchema, {
1200
+ json: body2,
1201
+ options: requestOptions
1202
+ })
1203
+ );
1204
+ },
1055
1205
  /**
1056
1206
  * Sign in, or sign up, with a Solana, EVM or Cosmos wallet where the project enabled it
1057
1207
  * (`providers().wallets`). A wallet the member linked while signed in cannot sign in.
1058
1208
  */
1059
1209
  wallet: {
1060
1210
  /** A sign-in challenge for the wallet's address; binds the flow to this browser. */
1061
- start: (input) => request(
1062
- "/v1/auth/wallet/start",
1063
- walletMessageChallengeSchema,
1064
- json(walletAddressInputSchema.parse(input))
1065
- ),
1211
+ start: async (value, requestOptions) => request("/v1/auth/wallet/start", walletMessageChallengeSchema, {
1212
+ json: parseInput(walletAddressInputSchema, value),
1213
+ options: requestOptions
1214
+ }),
1066
1215
  /** Verifies the signed challenge and starts the session. Not retried: start again instead. */
1067
- verify: (proof) => changeSession(
1068
- () => request(
1069
- "/v1/auth/wallet/verify",
1070
- sessionSchema,
1071
- json(walletSignatureSchema.parse(proof))
1072
- )
1216
+ verify: (proof, requestOptions) => changeSession(
1217
+ () => request("/v1/auth/wallet/verify", participantSessionSchema, {
1218
+ json: parseInput(walletSignatureSchema, proof),
1219
+ options: requestOptions
1220
+ })
1073
1221
  ),
1074
1222
  /**
1075
- * Asks the signer for its address, signs the challenge and starts the session. Throws a
1076
- * `RelayError` with status 403 when the project has not enabled this wallet family.
1223
+ * Asks the signer for its address, signs the challenge and starts the session. Rejects with
1224
+ * an `http` `RelayError` (status 403) when the project has not enabled this wallet family.
1077
1225
  */
1078
- signIn: async (signer) => {
1226
+ signIn: async (signer, requestOptions) => {
1079
1227
  const challenge = await request(
1080
1228
  "/v1/auth/wallet/start",
1081
1229
  walletMessageChallengeSchema,
1082
- json(
1083
- walletAddressInputSchema.parse(await walletAddressInput(signer))
1084
- )
1230
+ {
1231
+ json: parseInput(
1232
+ walletAddressInputSchema,
1233
+ await fromSigner(() => walletAddressInput(signer))
1234
+ ),
1235
+ options: requestOptions
1236
+ }
1237
+ );
1238
+ const proof = await fromSigner(
1239
+ () => signer.sign(challenge.message, challenge.address)
1085
1240
  );
1086
- const proof = await signer.sign(challenge.message, challenge.address);
1087
1241
  return changeSession(
1088
- () => request(
1089
- "/v1/auth/wallet/verify",
1090
- sessionSchema,
1091
- json(
1092
- walletSignatureSchema.parse({
1093
- challenge: challenge.id,
1094
- ...proof
1095
- })
1096
- )
1097
- )
1242
+ () => request("/v1/auth/wallet/verify", participantSessionSchema, {
1243
+ json: parseInput(walletSignatureSchema, {
1244
+ challenge: challenge.id,
1245
+ ...proof
1246
+ }),
1247
+ options: requestOptions
1248
+ })
1098
1249
  );
1099
1250
  }
1100
1251
  },
1101
- accounts: () => request("/v1/auth/accounts", z2.array(accountConnectionSchema)),
1102
- disconnect: (provider) => request(
1103
- "/v1/auth/accounts/disconnect",
1104
- z2.object({ ok: z2.boolean() }),
1105
- json({ provider })
1106
- ),
1107
- connections: () => request(
1108
- "/v1/auth/connections",
1109
- z2.array(
1110
- z2.object({
1111
- provider: z2.string(),
1112
- subject: z2.string(),
1113
- signInEnabled: z2.boolean()
1114
- })
1115
- )
1116
- ),
1117
- session: () => request("/v1/auth/session", sessionSchema.nullable()),
1118
- signOut: () => changeSession(
1119
- () => request("/v1/auth/logout", z2.object({ ok: z2.boolean() }), json({}))
1252
+ /** The member's connected sign-in accounts, one per provider. */
1253
+ accounts: (requestOptions) => request("/v1/auth/accounts", z2.array(accountConnectionSchema), {
1254
+ options: requestOptions
1255
+ }),
1256
+ disconnect: async (provider, requestOptions) => request("/v1/auth/accounts/disconnect", acknowledged, {
1257
+ json: { provider: parseInput(authProviderSchema, provider) },
1258
+ options: requestOptions
1259
+ }),
1260
+ session: (requestOptions) => readSession(requestOptions),
1261
+ signOut: (requestOptions) => changeSession(
1262
+ () => request("/v1/auth/logout", acknowledged, {
1263
+ json: {},
1264
+ options: requestOptions
1265
+ })
1120
1266
  ),
1121
1267
  /** Local development only. Existing hosted apps use createParticipantBackend from /portal-proxy. */
1122
- exchange: (input) => request("/v1/auth/exchange", exchangeResultSchema, json(input))
1268
+ exchange: async (value, requestOptions) => request("/v1/auth/exchange", exchangeResultSchema, {
1269
+ json: parseInput(exchangeSchema, value),
1270
+ options: requestOptions
1271
+ })
1123
1272
  },
1124
1273
  me: {
1125
1274
  /** The signed-in member, and the seed of their generated avatar: `defaultAvatar(avatarSeed)`. */
1126
- get: () => request("/v1/me", memberSummarySchema),
1127
- observeProgress: (listener, options2) => {
1275
+ get: async (requestOptions) => request("/v1/me", memberSummarySchema, { options: requestOptions }),
1276
+ observeProgress: (listener, observeOptions) => {
1128
1277
  const observer = observeProgress(
1129
1278
  {
1130
- session: (signal) => request("/v1/auth/session", sessionSchema.nullable(), { signal }),
1279
+ session: (signal) => readSession({ signal }),
1131
1280
  progress: (session, signal) => request("/v1/me/progress", memberProgressSchema, {
1132
- signal,
1133
- headers: { "X-Relay-Session": session }
1281
+ options: { session, signal }
1134
1282
  })
1135
1283
  },
1136
1284
  listener,
1137
- options2,
1285
+ observeOptions,
1138
1286
  () => observers.delete(observer)
1139
1287
  );
1140
1288
  observers.add(observer);
@@ -1143,147 +1291,117 @@ function createRelayClient(options) {
1143
1291
  return { refresh: observer.refresh, dispose: observer.dispose };
1144
1292
  },
1145
1293
  submission,
1146
- collectionPass: (entitlement, session) => request("/v1/me/collection-pass", collectionPassSchema, {
1147
- ...json({ entitlement: identifier.parse(entitlement) }),
1148
- headers: {
1149
- "Content-Type": "application/json",
1150
- "X-Relay-Session": session
1151
- }
1294
+ collectionPass: async (entitlement, requestOptions) => request("/v1/me/collection-pass", collectionPassSchema, {
1295
+ json: { entitlement: parseInput(identifier, entitlement) },
1296
+ options: await bound(requestOptions)
1152
1297
  }),
1153
- /** @deprecated The participant API does not verify passes. Verify them on your server and call `POST /management/v1/passes/verify`. */
1154
- linkPass: (code) => request("/v1/me/pass", attemptSchema, json({ code })),
1155
- progress: () => request("/v1/me/progress", memberProgressSchema),
1156
- rewardDeliveries: () => request("/v1/reward-deliveries", rewardDeliveriesSchema),
1157
- claim: (input, options2) => request("/v1/me/attempts", attemptSchema, {
1158
- ...json(input),
1159
- headers: {
1160
- "Content-Type": "application/json",
1161
- ...options2 ? { "X-Relay-Session": options2.session } : {}
1162
- }
1298
+ progress: (requestOptions) => request("/v1/me/progress", memberProgressSchema, {
1299
+ options: requestOptions
1163
1300
  }),
1164
- upload: (file) => request("/v1/me/evidence", evidenceSchema, {
1301
+ rewardDeliveries: (requestOptions) => request("/v1/reward-deliveries", rewardDeliveriesSchema, {
1302
+ options: requestOptions
1303
+ }),
1304
+ /** Claims a quest for the signed-in member. Quest controllers (`quests.open`) call this. */
1305
+ claim: async (value, requestOptions) => {
1306
+ const claim = parseInput(submitSchema.omit({ member: true }), value);
1307
+ return request("/v1/me/attempts", attemptSchema, {
1308
+ json: claim,
1309
+ options: requestOptions,
1310
+ operationId: claim.actionId
1311
+ });
1312
+ },
1313
+ /** Uploads a photo as the signed-in member's evidence. */
1314
+ upload: (file, requestOptions) => request("/v1/me/evidence", evidenceSchema, {
1165
1315
  method: "POST",
1316
+ body: file,
1166
1317
  headers: { "Content-Type": file.type },
1167
- body: file
1318
+ options: requestOptions
1168
1319
  })
1169
1320
  },
1170
1321
  evidence: {
1171
1322
  async download(id, requestOptions) {
1323
+ const call = { options: requestOptions };
1172
1324
  const response = await send(
1173
- `/v1/evidence/${identifier.parse(id)}`,
1174
- requestOptions
1325
+ `/v1/evidence/${parseInput(identifier, id)}`,
1326
+ call
1175
1327
  );
1176
1328
  const type = response.headers.get("Content-Type");
1177
1329
  if (type !== "image/jpeg" && type !== "image/png") {
1178
- throw new RelayError("Unexpected evidence content type", 502);
1330
+ throw invalidResponse(
1331
+ response.status,
1332
+ call,
1333
+ new Error("Unexpected evidence content type")
1334
+ );
1179
1335
  }
1180
- return response.blob();
1336
+ return body(response, call, (value) => value.blob());
1181
1337
  }
1182
1338
  },
1183
- /** @deprecated The participant API accepts only member sessions and rejects this staff call. Use the management API with a management key instead. */
1184
- lookupMember: (issuer, subject) => request(
1185
- "/v1/members/lookup",
1186
- z2.object({ member: z2.string() }),
1187
- json({ issuer, subject })
1188
- ),
1189
- /** @deprecated Removed from the participant API. Build quests with `domino` and `@domino-sdk/relay/build`. */
1190
- journeyTemplate: () => request("/v1/journey-template", z2.array(templateSchema)),
1191
- /** @deprecated Removed from the participant API. Build quests with `domino` and `@domino-sdk/relay/build`. */
1192
- template: () => request("/v1/template", templateSchema),
1193
- /** @deprecated The participant API accepts only member sessions and rejects this staff call. Use the management API with a management key instead. */
1194
- snapshot: () => request("/v1/snapshot", snapshotSchema),
1195
- /** @deprecated The participant API rejects publication. Publish with `domino deploy` or the management API. */
1196
- publish: (release) => request("/v1/releases", publishedReleaseSchema, json(release)),
1197
- /** @deprecated The participant API rejects publication. Publish with `domino deploy` or the management API. */
1198
- publishBatch: (releases) => request(
1199
- "/v1/releases/batch",
1200
- z2.array(publishedReleaseSchema),
1201
- json({ releases })
1202
- ),
1203
- progress: (member) => request(
1204
- `/v1/members/${encodeURIComponent(member)}/progress`,
1205
- memberProgressSchema
1206
- ),
1207
- /** @deprecated The participant API accepts only member sessions and rejects this staff call. Use the management API with a management key instead. */
1208
- beginHandover: (id, input) => request(`/v1/entitlements/${id}/begin`, entitlementSchema, json(input)),
1209
- /** @deprecated The participant API accepts only member sessions and rejects this staff call. Use the management API with a management key instead. */
1210
- confirmHandover: (id, input) => request(`/v1/entitlements/${id}/confirm`, entitlementSchema, json(input)),
1211
- upload: (member, file) => request(
1212
- `/v1/evidence?member=${encodeURIComponent(member)}`,
1213
- evidenceSchema,
1214
- {
1215
- method: "POST",
1216
- headers: { "Content-Type": file.type },
1217
- body: file
1218
- }
1219
- ),
1220
1339
  engagement: {
1221
- boundaries: (input) => request(
1222
- "/v1/engagement/boundaries?" + new URLSearchParams(
1223
- Object.entries(engagementPageQuerySchema.parse(input)).flatMap(
1224
- ([key, value]) => value === void 0 ? [] : [[key, String(value)]]
1225
- )
1226
- ).toString(),
1227
- engagementBoundaryPageSchema
1340
+ /** The member's engagement settlements for one post under one rule, newest first. */
1341
+ page: async (query, requestOptions) => request(
1342
+ withQuery(
1343
+ "/v1/engagement/history",
1344
+ parseInput(engagementPageQuerySchema, query)
1345
+ ),
1346
+ engagementPageSchema,
1347
+ { options: requestOptions }
1228
1348
  ),
1229
- page: (input) => request(
1230
- "/v1/engagement/history?" + new URLSearchParams(
1231
- Object.entries(engagementPageQuerySchema.parse(input)).flatMap(
1232
- ([key, value]) => value === void 0 ? [] : [[key, String(value)]]
1233
- )
1234
- ).toString(),
1235
- engagementPageSchema
1349
+ /** The formula boundaries the member's settlements for one post crossed, newest first. */
1350
+ boundaries: async (query, requestOptions) => request(
1351
+ withQuery(
1352
+ "/v1/engagement/boundaries",
1353
+ parseInput(engagementPageQuerySchema, query)
1354
+ ),
1355
+ engagementBoundaryPageSchema,
1356
+ { options: requestOptions }
1236
1357
  )
1237
1358
  },
1238
1359
  content: {
1239
- performance: (input) => request(
1240
- "/v1/content/performance?" + new URLSearchParams(
1241
- Object.entries(
1242
- contentPerformanceQuerySchema.parse(input)
1243
- ).flatMap(
1244
- ([key, value]) => value === void 0 ? [] : [[key, String(value)]]
1245
- )
1246
- ).toString(),
1247
- contentPerformancePageSchema
1248
- ),
1249
- qualifications: (input) => request(
1250
- "/v1/content/qualifications?" + new URLSearchParams(
1251
- Object.entries(
1252
- contentQualificationQuerySchema.parse(input)
1253
- ).flatMap(
1254
- ([key, value]) => value === void 0 ? [] : [[key, String(value)]]
1255
- )
1256
- ).toString(),
1257
- contentQualificationPageSchema
1360
+ performance: async (query, requestOptions) => request(
1361
+ withQuery(
1362
+ "/v1/content/performance",
1363
+ parseInput(contentPerformanceQuerySchema, query)
1364
+ ),
1365
+ contentPerformancePageSchema,
1366
+ { options: requestOptions }
1258
1367
  ),
1259
- admissions: (input) => request(
1260
- "/v1/content/admissions?" + new URLSearchParams(
1261
- Object.entries(contentAdmissionQuerySchema.parse(input)).flatMap(
1262
- ([key, value]) => value === void 0 ? [] : [[key, value]]
1263
- )
1264
- ).toString(),
1265
- contentAdmissionPageSchema
1368
+ qualifications: async (query, requestOptions) => request(
1369
+ withQuery(
1370
+ "/v1/content/qualifications",
1371
+ parseInput(contentQualificationQuerySchema, query)
1372
+ ),
1373
+ contentQualificationPageSchema,
1374
+ { options: requestOptions }
1266
1375
  ),
1267
- enroll: (input) => request(
1268
- "/v1/content/enrollments",
1269
- contentEnrollmentSchema,
1270
- json(contentEnrollmentInputSchema.parse(input))
1376
+ admissions: async (query, requestOptions) => request(
1377
+ withQuery(
1378
+ "/v1/content/admissions",
1379
+ parseInput(contentAdmissionQuerySchema, query)
1380
+ ),
1381
+ contentAdmissionPageSchema,
1382
+ { options: requestOptions }
1271
1383
  ),
1272
- claim: (input) => request(
1273
- "/v1/content/claims",
1274
- contentAdmissionSchema,
1275
- json(contentReleaseInputSchema.parse(input))
1276
- )
1277
- },
1278
- submit: (input) => request("/v1/attempts", attemptSchema, json(input)),
1279
- retry: (id) => request(`/v1/attempts/${id}/retry`, attemptSchema, json({})),
1280
- /** @deprecated The participant API accepts only member sessions and rejects this staff call. Use the management API with a management key instead. */
1281
- decide: (id, input) => request(`/v1/attempts/${id}/review`, attemptSchema, json(input))
1384
+ enroll: async (value, requestOptions) => {
1385
+ const body2 = parseInput(contentEnrollmentInputSchema, value);
1386
+ return request("/v1/content/enrollments", contentEnrollmentSchema, {
1387
+ json: body2,
1388
+ options: requestOptions,
1389
+ operationId: body2.actionId
1390
+ });
1391
+ },
1392
+ claim: async (value, requestOptions) => {
1393
+ const body2 = parseInput(contentReleaseInputSchema, value);
1394
+ return request("/v1/content/claims", contentAdmissionSchema, {
1395
+ json: body2,
1396
+ options: requestOptions,
1397
+ operationId: body2.actionId
1398
+ });
1399
+ }
1400
+ }
1282
1401
  };
1283
1402
  }
1284
1403
 
1285
1404
  export {
1286
- RelayError,
1287
1405
  QuestUpdatedError,
1288
1406
  createRelayClient
1289
1407
  };