@domino-sdk/relay 1.0.0-next.2 → 1.0.0-next.34

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 (61) hide show
  1. package/README.md +13 -1
  2. package/build.mjs +18 -1
  3. package/dist/activity-B8k9Sa_a.d.ts +9112 -0
  4. package/dist/authoring.d.ts +1355 -182
  5. package/dist/authoring.js +658 -225
  6. package/dist/automatic-outcomes-B1xrETxm.d.ts +758 -0
  7. package/dist/avatar.d.ts +29 -0
  8. package/dist/avatar.js +21 -0
  9. package/dist/browser-J7tUpXxy.d.ts +4154 -0
  10. package/dist/browser.d.ts +11 -4580
  11. package/dist/browser.js +8 -6
  12. package/dist/capability-error-DNMwKmlL.d.ts +458 -0
  13. package/dist/chunk-36HTX2BM.js +1223 -0
  14. package/dist/chunk-6QJ3LADK.js +112 -0
  15. package/dist/chunk-BGGKNIRP.js +171 -0
  16. package/dist/chunk-EYH5RDIE.js +496 -0
  17. package/dist/chunk-GQKYAQM6.js +84 -0
  18. package/dist/chunk-GUP7YRR4.js +42 -0
  19. package/dist/chunk-GZ6FJAXN.js +1659 -0
  20. package/dist/chunk-JON5SESL.js +2986 -0
  21. package/dist/chunk-MC6V6VMG.js +49 -0
  22. package/dist/chunk-NPGENI7K.js +37 -0
  23. package/dist/chunk-V7UKZLHA.js +35 -0
  24. package/dist/chunk-VFHHLN6A.js +284 -0
  25. package/dist/chunk-WJSSJ7AF.js +151 -0
  26. package/dist/{chunk-VH4OQ5VX.js → chunk-XFMSUPNS.js} +26 -5
  27. package/dist/{chunk-FDWRBMB3.js → chunk-ZVIRG24J.js} +150 -33
  28. package/dist/client-XiuFnug_.d.ts +4262 -0
  29. package/dist/groups-Ck-OxzjD.d.ts +2449 -0
  30. package/dist/identity-integration-GxAHdKR2.d.ts +58 -0
  31. package/dist/images-Dv-P5VBw.d.ts +234 -0
  32. package/dist/index.d.ts +15 -13115
  33. package/dist/index.js +30 -689
  34. package/dist/internal.d.ts +159 -0
  35. package/dist/internal.js +79 -0
  36. package/dist/offers-Cy-e4o5x.d.ts +431 -0
  37. package/dist/outcomes-kVB-Juzs.d.ts +4902 -0
  38. package/dist/pages-BBYD21IE.d.ts +359 -0
  39. package/dist/participant-rewards-3duLb8YJ.d.ts +668 -0
  40. package/dist/portal-proxy.js +26 -9
  41. package/dist/profiles-D2aFcOxu.d.ts +297 -0
  42. package/dist/react/index.d.ts +3378 -3318
  43. package/dist/react/index.js +1635 -783
  44. package/dist/request-error-DWduJCGV.d.ts +62 -0
  45. package/dist/schema-DfJzH2ci.d.ts +5644 -0
  46. package/dist/schema.d.ts +15 -3388
  47. package/dist/schema.js +790 -3
  48. package/dist/server/index.d.ts +81498 -0
  49. package/dist/server/index.js +2520 -0
  50. package/dist/streaks-CWyq8Px5.d.ts +71 -0
  51. package/dist/submission-input-BZkm6ohU.d.ts +290 -0
  52. package/dist/units-CwgsPB_r.d.ts +9 -0
  53. package/dist/vite.d.ts +1 -1
  54. package/dist/vite.js +1 -0
  55. package/dist/wallet-YssWxGg8.d.ts +228 -0
  56. package/package.json +24 -5
  57. package/dist/chunk-4KNTMNXA.js +0 -1847
  58. package/dist/chunk-B76BAJ25.js +0 -624
  59. package/dist/chunk-NKGJEYZM.js +0 -1833
  60. package/dist/chunk-ONDBQSMU.js +0 -373
  61. package/dist/submission-input-JDwq3EVK.d.ts +0 -4986
@@ -1,1847 +0,0 @@
1
- import {
2
- authoringIdentityShape,
3
- engagementArtifactSchema,
4
- engagementBoundaryPageSchema,
5
- engagementDeploymentEntrySchema,
6
- engagementPageQuerySchema,
7
- engagementPageSchema,
8
- leaderboardArtifactSchema,
9
- leaderboardDefinitionSchema,
10
- leaderboardPageSchema,
11
- leaderboardPlacementSchema,
12
- leaderboardPreviewSchema,
13
- leaderboardQuerySchema,
14
- normalizeAuthoringIdentity,
15
- publishedReferralSchema,
16
- referralAcceptSchema,
17
- referralArtifactSchema,
18
- referralDefinitionSchema,
19
- referralHistorySchema,
20
- referralSummarySchema,
21
- validateAuthoringIdentity
22
- } from "./chunk-B76BAJ25.js";
23
- import {
24
- authScopeSchema,
25
- exchangeResultSchema,
26
- memberSummarySchema,
27
- sessionSchema
28
- } from "./chunk-VH4OQ5VX.js";
29
- import {
30
- AVATAR_UPLOAD_LIMIT,
31
- avatarUploadTypes,
32
- linkedWalletSchema,
33
- ownProfileSchema,
34
- profileCompletionPageSchema,
35
- profileCompletionQuerySchema,
36
- profileReferralSchema,
37
- profileSummarySchema,
38
- profileUpdateSchema,
39
- walletAddressInput,
40
- walletAddressInputSchema,
41
- walletChallengeInputSchema,
42
- walletChallengeSchema,
43
- walletConnectionSchema,
44
- walletMessageChallengeSchema,
45
- walletProofSchema,
46
- walletSignInNetworksSchema,
47
- walletSignatureSchema,
48
- walletUnlinkInputSchema
49
- } from "./chunk-ONDBQSMU.js";
50
- import {
51
- accountConnectionSchema,
52
- attemptSchema,
53
- authProviderSchema,
54
- contentAdmissionPageSchema,
55
- contentAdmissionQuerySchema,
56
- contentAdmissionSchema,
57
- contentEnrollmentInputSchema,
58
- contentEnrollmentSchema,
59
- contentPerformancePageSchema,
60
- contentPerformanceQuerySchema,
61
- contentQualificationPageSchema,
62
- contentQualificationQuerySchema,
63
- contentReleaseInputSchema,
64
- entitlementSchema,
65
- errorSchema,
66
- evidenceSchema,
67
- identifier,
68
- memberProgressSchema,
69
- photoResultSchema,
70
- pickupAvailabilitySchema,
71
- pickupSelectionSchema,
72
- publishedReleaseSchema,
73
- publishedTrackerSchema,
74
- questDescriptionSchema,
75
- quizEvidenceSchema,
76
- releaseSchema,
77
- reviewModeSchema,
78
- settingsValuesSchema,
79
- snapshotSchema,
80
- storedReviewModeSchema,
81
- submissionEvidenceSchema,
82
- templateSchema,
83
- tieredRewardReferenceSchema,
84
- trackerArtifactSchema,
85
- validateSubmissionValue,
86
- valueEvidenceSchema,
87
- xTweetSchema
88
- } from "./chunk-NKGJEYZM.js";
89
-
90
- // src/point-ledgers.ts
91
- import { z } from "zod";
92
- var pointLedgerPropertiesSchema = z.object({
93
- name: z.string().trim().min(1).max(120),
94
- description: z.string().trim().max(1e3).default("")
95
- }).strict();
96
- var createPointLedgerSchema = pointLedgerPropertiesSchema.extend({ actionId: identifier, key: identifier.optional() }).strict();
97
- var pointLedgerSchema = pointLedgerPropertiesSchema.extend({
98
- id: identifier
99
- });
100
- var pointLedgerDeclarationSchema = pointLedgerPropertiesSchema.extend({ key: identifier }).strict();
101
- var pointLedgerReferenceSchema = z.object({
102
- id: identifier,
103
- key: identifier.nullable(),
104
- name: z.string()
105
- });
106
-
107
- // src/reward-deliveries.ts
108
- import { z as z2 } from "zod";
109
- var rewardDeliverySchema = z2.object({
110
- id: z2.string(),
111
- quest: z2.string(),
112
- completion: z2.string(),
113
- member: z2.string(),
114
- provider: z2.literal("discord"),
115
- action: z2.literal("grant-role"),
116
- roleId: z2.string(),
117
- /**
118
- * `failed` is terminal: Discord rejected the grant permanently (unknown role,
119
- * member left the server, missing permissions) or retries ran out after
120
- * about a day. An operator can retry it once the cause is fixed.
121
- */
122
- status: z2.enum(["pending", "delivered", "failed"]),
123
- /** Provider attempts in the current round; an operator retry starts a new round at zero. */
124
- attempts: z2.number().int(),
125
- /** While pending with a `lastError`, when the next attempt is scheduled. */
126
- nextAt: z2.number(),
127
- lastError: z2.string().nullable(),
128
- createdAt: z2.number(),
129
- deliveredAt: z2.number().nullable()
130
- });
131
- var rewardDeliveriesSchema = z2.object({
132
- deliveries: z2.array(rewardDeliverySchema)
133
- });
134
-
135
- // src/catalog.ts
136
- import { z as z3 } from "zod";
137
- var interactionSchema = z3.enum([
138
- "photo",
139
- "text",
140
- "url",
141
- "quiz",
142
- "claim",
143
- "staff",
144
- "automatic"
145
- ]);
146
- function questInteraction(trigger) {
147
- return trigger.kind === "automatic" ? "automatic" : trigger.actor === "staff" ? "staff" : trigger.input === "none" ? "claim" : trigger.input;
148
- }
149
- var questTypeVersionSchema = z3.object({
150
- key: identifier.optional(),
151
- id: identifier,
152
- version: identifier,
153
- code: z3.string(),
154
- definition: questDescriptionSchema,
155
- provider: storedReviewModeSchema,
156
- createdAt: z3.number()
157
- });
158
- var collectionSlotSchema = z3.object({
159
- id: identifier,
160
- key: identifier.optional(),
161
- title: z3.string().trim().min(1).max(120)
162
- }).strict();
163
- var collectionSlotDeclarationSchema = collectionSlotSchema.omit({ id: true }).extend(authoringIdentityShape).strict().superRefine(validateAuthoringIdentity).transform(normalizeAuthoringIdentity);
164
- var questCollectionSchema = collectionSlotSchema.extend({
165
- quests: z3.array(identifier).max(100).refine(
166
- (v) => new Set(v).size === v.length,
167
- "A collection can contain a quest only once"
168
- )
169
- });
170
- var questInstanceSchema = z3.object({
171
- id: identifier,
172
- title: z3.string().trim().min(1).max(120),
173
- typeVersion: identifier,
174
- settings: settingsValuesSchema,
175
- requires: z3.array(identifier).max(30),
176
- status: z3.enum(["active", "paused", "archived"])
177
- }).strict();
178
- var questDraftChangesSchema = z3.object({
179
- quests: z3.array(
180
- questInstanceSchema.extend({ expectedRelease: identifier.nullable() })
181
- ).max(50),
182
- collections: z3.array(questCollectionSchema).max(50)
183
- }).strict();
184
- var draftBase = z3.object({
185
- id: identifier,
186
- revision: z3.number().int().positive(),
187
- baseRevision: z3.number().int().nonnegative(),
188
- changes: questDraftChangesSchema
189
- });
190
- var questDraftSchema = z3.discriminatedUnion("status", [
191
- draftBase.extend({ status: z3.literal("draft") }),
192
- draftBase.extend({
193
- status: z3.literal("published"),
194
- releaseIds: z3.array(identifier)
195
- })
196
- ]);
197
- var questCatalogSchema = z3.object({
198
- revision: z3.number().int().nonnegative(),
199
- types: z3.array(questTypeVersionSchema),
200
- collections: z3.array(questCollectionSchema),
201
- quests: z3.array(questInstanceSchema),
202
- drafts: z3.array(questDraftSchema),
203
- releases: z3.array(publishedReleaseSchema).default([]),
204
- supportedInteractions: z3.array(interactionSchema)
205
- });
206
- var catalogDeploymentSchema = z3.object({
207
- expectedRevision: z3.number().int().nonnegative(),
208
- types: z3.array(
209
- z3.object({
210
- code: z3.string().min(1).max(1e6),
211
- provider: reviewModeSchema
212
- })
213
- ).max(50).default([]),
214
- collections: z3.array(collectionSlotDeclarationSchema).max(50).default([]),
215
- supportedInteractions: z3.array(interactionSchema).min(1).optional()
216
- }).strict();
217
- var catalogDeploymentPreviewSchema = z3.object({
218
- revision: z3.number().int().nonnegative(),
219
- types: z3.array(questTypeVersionSchema),
220
- collections: z3.array(collectionSlotSchema),
221
- supportedInteractions: z3.array(interactionSchema)
222
- });
223
- var saveQuestDraftSchema = z3.object({
224
- id: identifier.optional(),
225
- actionId: identifier,
226
- expectedRevision: z3.number().int().nonnegative(),
227
- baseRevision: z3.number().int().nonnegative(),
228
- changes: questDraftChangesSchema
229
- }).strict();
230
- var publishQuestDraftSchema = z3.object({ id: identifier, expectedRevision: z3.number().int().positive() }).strict();
231
- var questRewardViewSchema = z3.discriminatedUnion("kind", [
232
- z3.object({
233
- kind: z3.literal("points"),
234
- label: z3.string(),
235
- amount: z3.number().int().nonnegative(),
236
- ledger: pointLedgerReferenceSchema
237
- }),
238
- z3.object({
239
- kind: z3.literal("tier"),
240
- label: z3.string(),
241
- reward: tieredRewardReferenceSchema.extend({ title: z3.string() }),
242
- tier: identifier
243
- }),
244
- z3.object({ kind: z3.literal("integration"), label: z3.string() })
245
- ]);
246
- var questViewSchema = memberProgressSchema.shape.quests.element.extend(
247
- {
248
- release: identifier,
249
- interaction: interactionSchema,
250
- lifecycle: z3.enum(["active", "paused", "archived"]),
251
- rewards: z3.array(questRewardViewSchema)
252
- }
253
- );
254
- var collectionViewSchema = z3.object({
255
- key: identifier.optional(),
256
- id: identifier,
257
- title: z3.string(),
258
- quests: z3.array(questViewSchema)
259
- });
260
- var questExperienceSchema = z3.object({
261
- member: identifier,
262
- quests: z3.array(questViewSchema),
263
- collections: z3.array(collectionViewSchema)
264
- });
265
- var questPublicationPreviewSchema = z3.object({
266
- draft: questDraftSchema,
267
- releases: z3.array(publishedReleaseSchema),
268
- affectedCollections: z3.array(questCollectionSchema)
269
- });
270
- var allocateQuestSchema = z3.object({ actionId: identifier }).strict();
271
- var allocatedQuestSchema = z3.object({ id: identifier });
272
-
273
- // src/errors.ts
274
- var RelayError = class extends Error {
275
- constructor(message, status, missing = [], code) {
276
- super(message);
277
- this.status = status;
278
- this.missing = missing;
279
- this.code = code;
280
- }
281
- status;
282
- missing;
283
- code;
284
- };
285
-
286
- // src/submissions.ts
287
- import { z as z4 } from "zod";
288
- var common = {
289
- id: identifier,
290
- quest: identifier,
291
- release: identifier.optional()
292
- };
293
- var claimInputSchema = z4.union([
294
- submissionEvidenceSchema,
295
- z4.object({ kind: z4.literal("none") }).strict()
296
- ]);
297
- var draftSchema = z4.union([
298
- claimInputSchema,
299
- z4.object({ kind: z4.literal("photo"), file: z4.instanceof(Blob) })
300
- ]);
301
- var recordSchema = z4.union([
302
- z4.object({
303
- ...common,
304
- stage: z4.literal("review"),
305
- draft: draftSchema.nullable()
306
- }),
307
- z4.object({ ...common, stage: z4.literal("upload"), file: z4.instanceof(Blob) }),
308
- z4.object({
309
- ...common,
310
- stage: z4.literal("claim"),
311
- evidence: identifier,
312
- file: z4.instanceof(Blob).optional()
313
- }),
314
- z4.object({
315
- ...common,
316
- stage: z4.literal("submitted"),
317
- evidence: identifier,
318
- attempt: identifier
319
- }),
320
- z4.object({ ...common, stage: z4.literal("claim"), input: claimInputSchema }),
321
- z4.object({
322
- ...common,
323
- stage: z4.literal("submitted"),
324
- input: claimInputSchema,
325
- attempt: identifier
326
- })
327
- ]);
328
- function submissions(transport, configuredStore, namespace) {
329
- async function open(quest, options = {
330
- input: "photo"
331
- }) {
332
- if (!configuredStore) {
333
- throw new Error(
334
- "Submissions require durable storage. Use createBrowserRelayClient or provide a SubmissionStore."
335
- );
336
- }
337
- const store = configuredStore;
338
- identifier.parse(quest);
339
- const session = await transport.session();
340
- if (session?.principal.kind !== "member") {
341
- throw new Error("Sign in to submit an activity");
342
- }
343
- const sessionId = session.id;
344
- const key = JSON.stringify([
345
- "relay-submission-v1",
346
- namespace,
347
- session.organization,
348
- session.environment,
349
- session.project,
350
- session.principal.member,
351
- quest
352
- ]);
353
- async function currentSession() {
354
- const current = await transport.session();
355
- if (current?.id !== sessionId) {
356
- throw new Error(
357
- "Your session changed. Reopen the submission before continuing."
358
- );
359
- }
360
- }
361
- async function read() {
362
- const saved = await store.read(key);
363
- if (saved === void 0 || saved === null) {
364
- return null;
365
- }
366
- const parsed = recordSchema.safeParse(saved);
367
- if (!parsed.success || parsed.data.quest !== quest) {
368
- throw new Error(
369
- "Saved submission could not be read. Do not create another claim until it is recovered."
370
- );
371
- }
372
- const record = parsed.data;
373
- const input = record.stage === "review" ? record.draft?.kind : "input" in record ? record.input.kind : "photo";
374
- if (!options.recoverPrevious && input !== options.input) {
375
- throw new Error(
376
- "Saved submission uses a different input type. Reopen it with its original type to recover it."
377
- );
378
- }
379
- return record;
380
- }
381
- function claim(record) {
382
- return transport.claim(
383
- {
384
- quest,
385
- actionId: record.id,
386
- ...record.release ? { release: record.release } : {},
387
- ..."evidence" in record ? { evidence: record.evidence } : record.input.kind !== "none" ? { input: record.input } : {}
388
- },
389
- sessionId
390
- );
391
- }
392
- async function resumeRecord(saved) {
393
- if (saved.stage === "review") {
394
- throw new QuestUpdatedError(
395
- "This quest changed. Review it before submitting again.",
396
- "quest-updated",
397
- saved.draft
398
- );
399
- }
400
- let record = saved;
401
- if (record.stage === "upload") {
402
- const evidence = await transport.upload(
403
- record.file,
404
- record.id,
405
- sessionId
406
- );
407
- record = {
408
- id: record.id,
409
- quest,
410
- ...record.release ? { release: record.release } : {},
411
- file: record.file,
412
- stage: "claim",
413
- evidence: evidence.id
414
- };
415
- await store.write(key, record);
416
- }
417
- let attempt;
418
- try {
419
- attempt = await claim(record);
420
- } catch (error) {
421
- if (error instanceof RelayError && error.status === 422 && error.code === "invalid-submission" && record.stage === "claim") {
422
- await store.write(key, null);
423
- throw error;
424
- }
425
- if (error instanceof RelayError && error.status === 409 && (error.code === "quest-updated" || error.code === "stale-quiz") && record.stage === "claim") {
426
- const draft = "input" in record ? record.input : "file" in record && record.file ? { kind: "photo", file: record.file } : null;
427
- await store.write(
428
- key,
429
- error.code === "quest-updated" ? {
430
- id: record.id,
431
- quest,
432
- release: record.release,
433
- stage: "review",
434
- draft
435
- } : null
436
- );
437
- throw new QuestUpdatedError(error.message, error.code, draft);
438
- }
439
- throw error;
440
- }
441
- await store.write(key, {
442
- ...record,
443
- stage: "submitted",
444
- attempt: attempt.id
445
- });
446
- if (attempt.status === "failed") {
447
- attempt = await transport.retry(attempt.id, sessionId);
448
- }
449
- return attempt;
450
- }
451
- async function submit(record) {
452
- return store.exclusive(key, async () => {
453
- await currentSession();
454
- const existing = await read();
455
- if (existing?.stage === "review" && existing.release === record.release) {
456
- throw new QuestUpdatedError(
457
- "Review the latest quest before submitting again.",
458
- "quest-updated",
459
- existing.draft
460
- );
461
- }
462
- if (existing && existing.stage !== "submitted" && existing.stage !== "review") {
463
- throw new Error(
464
- "A submission is unfinished. Resume it before choosing new input."
465
- );
466
- }
467
- if (existing?.stage === "submitted") {
468
- const previous = await claim(existing);
469
- if (previous.status === "failed") {
470
- return transport.retry(previous.id, sessionId);
471
- }
472
- if (previous.status !== "rejected" && !(options.repeatable && previous.status === "accepted")) {
473
- return previous;
474
- }
475
- }
476
- await store.write(key, record);
477
- return resumeRecord(record);
478
- });
479
- }
480
- const handle = {
481
- async discard() {
482
- return store.exclusive(key, async () => {
483
- await currentSession();
484
- const record = await read();
485
- if (!record) {
486
- return;
487
- }
488
- if (record.stage !== "submitted") {
489
- throw new Error(
490
- "Resume the unfinished submission before starting over."
491
- );
492
- }
493
- const attempt = await claim(record);
494
- if (attempt.status !== "failed" && attempt.status !== "rejected" && !(options.repeatable && attempt.status === "accepted")) {
495
- throw new Error(
496
- "Only a failed or rejected submission can be discarded."
497
- );
498
- }
499
- await store.write(key, null);
500
- });
501
- },
502
- async status() {
503
- await currentSession();
504
- const record = await read();
505
- if (!record) {
506
- return { kind: "empty" };
507
- }
508
- if (record.stage === "review") {
509
- return { kind: "needs-review" };
510
- }
511
- return record.stage === "submitted" ? { kind: "submitted", attempt: record.attempt } : { kind: "pending", stage: record.stage };
512
- },
513
- async resume() {
514
- return store.exclusive(key, async () => {
515
- await currentSession();
516
- const record = await read();
517
- if (!record) {
518
- throw new Error("There is no saved submission to resume");
519
- }
520
- return resumeRecord(record);
521
- });
522
- }
523
- };
524
- if (options.input === "text" || options.input === "url") {
525
- const kind = options.input;
526
- return {
527
- ...handle,
528
- submit(value) {
529
- const input = valueEvidenceSchema.parse({ kind, value });
530
- return submit({
531
- id: crypto.randomUUID(),
532
- quest,
533
- release: options.release,
534
- stage: "claim",
535
- input
536
- });
537
- }
538
- };
539
- }
540
- if (options.input === "quiz") {
541
- return {
542
- ...handle,
543
- submit(answers, version) {
544
- const input = quizEvidenceSchema.parse({
545
- kind: "quiz",
546
- answers,
547
- ...version || options.release ? { release: version?.release ?? options.release } : {}
548
- });
549
- return submit({
550
- id: crypto.randomUUID(),
551
- ...options.release ? { release: options.release } : {},
552
- quest,
553
- stage: "claim",
554
- input
555
- });
556
- }
557
- };
558
- }
559
- if (options.input === "none") {
560
- return {
561
- ...handle,
562
- submit() {
563
- return submit({
564
- id: crypto.randomUUID(),
565
- ...options.release ? { release: options.release } : {},
566
- quest,
567
- stage: "claim",
568
- input: { kind: "none" }
569
- });
570
- }
571
- };
572
- }
573
- return {
574
- ...handle,
575
- async submit(file) {
576
- if (!["image/jpeg", "image/png"].includes(file.type) || file.size === 0 || file.size > 5 * 1024 * 1024) {
577
- throw new Error("Choose a JPG or PNG photo under 5 MB");
578
- }
579
- const bytes = new Uint8Array(await file.slice(0, 4).arrayBuffer());
580
- const signature = file.type === "image/png" ? [137, 80, 78, 71] : [255, 216, 255];
581
- if (!signature.every((byte, index) => bytes[index] === byte)) {
582
- throw new Error("Image content does not match its file type");
583
- }
584
- return submit({
585
- id: crypto.randomUUID(),
586
- ...options.release ? { release: options.release } : {},
587
- quest,
588
- stage: "upload",
589
- file
590
- });
591
- }
592
- };
593
- }
594
- return open;
595
- }
596
- var QuestUpdatedError = class extends RelayError {
597
- constructor(message, code, draft) {
598
- super(message, 409, [], code);
599
- this.draft = draft;
600
- }
601
- draft;
602
- };
603
-
604
- // src/quest-experience.ts
605
- function reconcileQuestDraft(view, draft) {
606
- if (!draft) {
607
- return null;
608
- }
609
- if (draft.kind === "text" || draft.kind === "url") {
610
- return view.interaction === draft.kind ? draft : null;
611
- }
612
- if (draft.kind === "photo") {
613
- return view.interaction === "photo" ? draft : null;
614
- }
615
- if (draft.kind === "none") {
616
- return view.interaction === "claim" ? draft : null;
617
- }
618
- if (view.interaction !== "quiz") {
619
- return null;
620
- }
621
- const answers = Object.fromEntries(
622
- (view.presentation.quiz ?? []).flatMap((q) => {
623
- const answer = draft.answers[q.id];
624
- return q.choices.some((c) => c.id === answer) ? [[q.id, answer]] : [];
625
- })
626
- );
627
- return { kind: "quiz", release: view.release, answers };
628
- }
629
- function questActions(submission, latest) {
630
- return async (view) => {
631
- async function run(task) {
632
- try {
633
- return { kind: "submitted", attempt: await task() };
634
- } catch (error) {
635
- if (!(error instanceof QuestUpdatedError)) {
636
- throw error;
637
- }
638
- const current = await latest(view.quest).catch(() => {
639
- throw error;
640
- });
641
- return {
642
- kind: "quest-updated",
643
- latest: current,
644
- originalDraft: error.draft,
645
- draft: reconcileQuestDraft(current, error.draft)
646
- };
647
- }
648
- }
649
- if (view.lifecycle !== "active" || view.interaction === "staff" || view.interaction === "automatic") {
650
- const handle = await submission(view.quest, {
651
- input: "none",
652
- release: view.release,
653
- recoverPrevious: true,
654
- repeatable: view.completion.kind === "keyed"
655
- });
656
- return {
657
- kind: "status",
658
- view,
659
- status: handle.status,
660
- discard: handle.discard,
661
- resume: () => run(handle.resume)
662
- };
663
- }
664
- switch (view.interaction) {
665
- case "text":
666
- case "url": {
667
- const kind = view.interaction;
668
- const handle = await submission(view.quest, {
669
- input: kind,
670
- release: view.release,
671
- recoverPrevious: true,
672
- repeatable: view.completion.kind === "keyed"
673
- });
674
- return {
675
- kind,
676
- view,
677
- status: handle.status,
678
- discard: handle.discard,
679
- resume: () => run(handle.resume),
680
- submit: (value) => run(() => {
681
- if (view.trigger.kind !== "manual" || view.trigger.input !== "text" && view.trigger.input !== "url") {
682
- throw new Error("Quest input changed");
683
- }
684
- const problem = validateSubmissionValue(
685
- { kind, value },
686
- view.trigger
687
- );
688
- if (problem) {
689
- throw new Error(problem);
690
- }
691
- return handle.submit(value);
692
- })
693
- };
694
- }
695
- case "photo": {
696
- const handle = await submission(view.quest, {
697
- input: "photo",
698
- release: view.release,
699
- recoverPrevious: true,
700
- repeatable: view.completion.kind === "keyed"
701
- });
702
- return {
703
- kind: "photo",
704
- view,
705
- status: handle.status,
706
- discard: handle.discard,
707
- resume: () => run(handle.resume),
708
- submit: (file) => run(() => handle.submit(file))
709
- };
710
- }
711
- case "quiz": {
712
- const handle = await submission(view.quest, {
713
- input: "quiz",
714
- release: view.release,
715
- recoverPrevious: true,
716
- repeatable: view.completion.kind === "keyed"
717
- });
718
- return {
719
- kind: "quiz",
720
- view,
721
- status: handle.status,
722
- discard: handle.discard,
723
- resume: () => run(handle.resume),
724
- submit: (answers) => run(() => handle.submit(answers, { release: view.release }))
725
- };
726
- }
727
- case "claim": {
728
- const handle = await submission(view.quest, {
729
- input: "none",
730
- release: view.release,
731
- recoverPrevious: true,
732
- repeatable: view.completion.kind === "keyed"
733
- });
734
- return {
735
- kind: "claim",
736
- view,
737
- status: handle.status,
738
- discard: handle.discard,
739
- resume: () => run(handle.resume),
740
- submit: () => run(handle.submit)
741
- };
742
- }
743
- }
744
- };
745
- }
746
-
747
- // src/progress.ts
748
- function observeProgress(transport, listener, options = {}, onDispose = () => {
749
- }) {
750
- const interval = options.intervalMs ?? 1500;
751
- const maximum = options.maxBackoffMs ?? 3e4;
752
- if (!Number.isFinite(interval) || interval <= 0 || !Number.isFinite(maximum) || maximum < interval) {
753
- throw new Error(
754
- "Progress polling requires a positive interval and a maximum at least as large"
755
- );
756
- }
757
- let disposed = false;
758
- let controller;
759
- let timer;
760
- let sessionId;
761
- let failures = 0;
762
- function clear() {
763
- controller?.abort();
764
- if (timer !== void 0) {
765
- clearTimeout(timer);
766
- }
767
- sessionId = void 0;
768
- if (!disposed) {
769
- listener({ kind: "loading" });
770
- }
771
- }
772
- async function refresh() {
773
- if (disposed) {
774
- return;
775
- }
776
- controller?.abort();
777
- if (timer !== void 0) {
778
- clearTimeout(timer);
779
- }
780
- const request = new AbortController();
781
- controller = request;
782
- const current = () => !disposed && !request.signal.aborted;
783
- try {
784
- const session = await transport.session(request.signal);
785
- if (!current()) {
786
- return;
787
- }
788
- if (sessionId !== (session?.id ?? null)) {
789
- sessionId = session?.id ?? null;
790
- listener({ kind: "session", session });
791
- }
792
- if (session?.principal.kind === "member") {
793
- const progress = await transport.progress(session.id, request.signal);
794
- if (!current()) {
795
- return;
796
- }
797
- const confirmed = await transport.session(request.signal);
798
- if (!current()) {
799
- return;
800
- }
801
- if (confirmed?.id !== session.id || progress.member !== session.principal.member) {
802
- sessionId = void 0;
803
- listener({ kind: "loading" });
804
- return;
805
- }
806
- listener({ kind: "ready", session: confirmed, progress });
807
- }
808
- failures = 0;
809
- } catch (error) {
810
- if (!current()) {
811
- return;
812
- }
813
- failures = Math.min(failures + 1, 20);
814
- sessionId = void 0;
815
- listener({
816
- kind: "error",
817
- error: error instanceof Error ? error : new Error("Could not refresh progress")
818
- });
819
- } finally {
820
- if (current()) {
821
- timer = setTimeout(
822
- () => void refresh(),
823
- Math.min(maximum, interval * 2 ** failures)
824
- );
825
- }
826
- }
827
- }
828
- const observer = {
829
- refresh,
830
- clear,
831
- dispose() {
832
- if (disposed) {
833
- return;
834
- }
835
- disposed = true;
836
- controller?.abort();
837
- if (timer !== void 0) {
838
- clearTimeout(timer);
839
- }
840
- onDispose();
841
- }
842
- };
843
- return observer;
844
- }
845
-
846
- // src/index.ts
847
- import { z as z10 } from "zod";
848
-
849
- // src/avatar.ts
850
- import { createAvatar } from "@dicebear/core";
851
- import * as shapes from "@dicebear/shapes";
852
- function avatarSrc(avatar) {
853
- return avatar.source === "generated" ? defaultAvatar(avatar.seed) : avatar.url;
854
- }
855
- function defaultAvatar(seed) {
856
- return createAvatar(shapes, { seed }).toDataUri();
857
- }
858
- function defaultAvatarSvg(seed) {
859
- return createAvatar(shapes, { seed }).toString();
860
- }
861
- function walletAvatarSeed(wallet) {
862
- return `${wallet.chain}:${wallet.chain === "solana" ? wallet.address : wallet.address.toLowerCase()}`;
863
- }
864
-
865
- // src/collection.ts
866
- import { z as z5 } from "zod";
867
- var collectionCodeSchema = z5.string().regex(/^relay:collect:[a-f0-9]{64}$/);
868
- var collectionPassSchema = z5.object({
869
- code: collectionCodeSchema,
870
- expiresAt: z5.number(),
871
- entitlement: identifier
872
- });
873
- var collectionResolveSchema = z5.object({ code: collectionCodeSchema }).strict();
874
- var collectionBeginSchema = collectionResolveSchema.extend({
875
- actionId: identifier,
876
- revision: z5.number().int().positive(),
877
- selection: pickupSelectionSchema.optional()
878
- });
879
- var collectionPreviewSchema = z5.object({
880
- entitlement: entitlementSchema,
881
- inventory: pickupAvailabilitySchema,
882
- expiresAt: z5.number()
883
- });
884
-
885
- // src/configuration.ts
886
- function questConfiguration(release) {
887
- return release.configuration ?? {
888
- defaults: Object.fromEntries(
889
- Object.entries(release.fields).map(([key, field]) => [
890
- key,
891
- field.default
892
- ])
893
- ),
894
- overrides: { ...release.values },
895
- preserved: Object.keys(release.values)
896
- };
897
- }
898
-
899
- // src/deployments.ts
900
- import { z as z8 } from "zod";
901
-
902
- // src/images.ts
903
- import { z as z7 } from "zod";
904
-
905
- // src/x.ts
906
- import { z as z6 } from "zod";
907
- var xIdSchema = z6.string().regex(/^[0-9]{1,20}$/);
908
- var xConnectionSchema = z6.object({ userId: xIdSchema });
909
- var xProfileSchema = z6.object({
910
- id: xIdSchema,
911
- username: z6.string(),
912
- name: z6.string(),
913
- bio: z6.string().nullable(),
914
- profileImageUrl: z6.string().url().nullable(),
915
- followersCount: z6.number().int().nonnegative().nullable(),
916
- verified: z6.boolean().nullable()
917
- });
918
- var xPostSchema = xTweetSchema.extend({
919
- quotedTweetId: xIdSchema.nullish().transform((v) => v ?? null),
920
- inReplyToUserId: xIdSchema.nullish().transform((v) => v ?? null),
921
- inReplyToStatusId: xIdSchema.nullish().transform((v) => v ?? null),
922
- retweetedTweetId: xIdSchema.nullish().transform((v) => v ?? null),
923
- /**
924
- * Attached media in post order. Videos and GIFs carry their preview image URL. Like the tracker
925
- * projection (migration 0056), a list with any malformed item is dropped whole, so positions
926
- * never shift and a bad entry never fails the post read.
927
- */
928
- media: z6.unknown().transform(
929
- (value) => z6.array(z6.object({ type: z6.string(), url: z6.string() })).safeParse(value).data ?? []
930
- )
931
- });
932
- var xRequestSchema = z6.discriminatedUnion("operation", [
933
- z6.object({ operation: z6.literal("connection") }).strict(),
934
- z6.object({ operation: z6.literal("profile") }).strict(),
935
- z6.object({ operation: z6.literal("post"), postId: xIdSchema }).strict(),
936
- z6.object({ operation: z6.literal("follows"), userId: xIdSchema }).strict(),
937
- z6.object({ operation: z6.literal("reposted"), postId: xIdSchema }).strict()
938
- ]);
939
-
940
- // src/images.ts
941
- var imageReferenceSchema = z7.union([
942
- z7.strictObject({ evidence: z7.string().min(1).max(256) }),
943
- z7.strictObject({ x: z7.literal("profile") }),
944
- z7.strictObject({
945
- x: z7.literal("post"),
946
- postId: xIdSchema,
947
- index: z7.number().int().min(0).max(3).default(0)
948
- }),
949
- z7.strictObject({ asset: identifier })
950
- ]);
951
- var criteria = z7.string().trim().min(1).max(2e3);
952
- var imageFieldSchema = z7.strictObject({
953
- type: z7.enum(["string", "number", "boolean"]),
954
- description: z7.string().trim().min(1).max(500)
955
- });
956
- var imageFieldsSchema = z7.record(z7.string().regex(/^[a-zA-Z][a-zA-Z0-9_]{0,39}$/), imageFieldSchema).refine((fields) => {
957
- const count = Object.keys(fields).length;
958
- return count >= 1 && count <= 12;
959
- }, "Declare between 1 and 12 fields");
960
- var imageRequestSchema = z7.discriminatedUnion("operation", [
961
- z7.strictObject({
962
- operation: z7.literal("inspect"),
963
- image: imageReferenceSchema
964
- }),
965
- z7.strictObject({
966
- operation: z7.literal("compare"),
967
- images: z7.tuple([imageReferenceSchema, imageReferenceSchema])
968
- }),
969
- z7.strictObject({
970
- operation: z7.literal("review"),
971
- images: z7.array(imageReferenceSchema).min(1).max(4),
972
- criteria
973
- }),
974
- z7.strictObject({
975
- operation: z7.literal("extract"),
976
- image: imageReferenceSchema,
977
- fields: imageFieldsSchema
978
- }),
979
- z7.strictObject({
980
- operation: z7.literal("seen"),
981
- image: imageReferenceSchema,
982
- maxDistance: z7.number().int().min(0).max(8).default(8)
983
- })
984
- ]);
985
- var hash = z7.string().regex(/^[0-9a-f]{16}$/);
986
- var imageInspectionSchema = z7.strictObject({
987
- width: z7.number().int().positive(),
988
- height: z7.number().int().positive(),
989
- format: z7.enum(["jpeg", "png", "webp", "gif", "avif"]),
990
- bytes: z7.number().int().positive(),
991
- sha256: z7.string().regex(/^[0-9a-f]{64}$/),
992
- phash: hash,
993
- dhash: hash
994
- });
995
- var distance = z7.number().int().min(0).max(64);
996
- var imageComparisonSchema = z7.strictObject({
997
- identical: z7.boolean(),
998
- phashDistance: distance,
999
- dhashDistance: distance
1000
- });
1001
- var imageExtractionSchema = z7.strictObject({
1002
- values: z7.record(
1003
- z7.string(),
1004
- z7.union([z7.string().max(2e3), z7.number(), z7.boolean(), z7.null()])
1005
- ),
1006
- reason: z7.string().min(1).max(2e3)
1007
- });
1008
- var imageSightingsSchema = z7.strictObject({
1009
- matches: z7.number().int().nonnegative(),
1010
- closestDistance: distance.nullable(),
1011
- firstSeenAt: z7.number().int().nonnegative().nullable()
1012
- });
1013
- var sha256 = z7.string().regex(/^[0-9a-f]{64}$/);
1014
- var imageSize = z7.number().int().positive().max(5 * 1024 * 1024);
1015
- var imageAssetArtifactSchema = z7.strictObject({
1016
- key: identifier,
1017
- sha256,
1018
- size: imageSize,
1019
- path: z7.string().min(1).max(500).optional()
1020
- });
1021
- var imageUploadRequestSchema = z7.strictObject({
1022
- images: z7.array(
1023
- z7.strictObject({
1024
- sha256,
1025
- size: imageSize,
1026
- type: z7.enum(["image/jpeg", "image/png", "image/webp"])
1027
- })
1028
- ).min(1).max(10)
1029
- });
1030
- var imageUploadsSchema = z7.strictObject({
1031
- images: z7.array(
1032
- z7.strictObject({
1033
- sha256,
1034
- upload: z7.strictObject({
1035
- url: z7.string().url(),
1036
- headers: z7.record(z7.string(), z7.string()),
1037
- expiresAt: z7.number().int().positive()
1038
- }).nullable()
1039
- })
1040
- )
1041
- });
1042
- var publishedImageSchema = z7.strictObject({
1043
- key: identifier,
1044
- sha256,
1045
- type: z7.enum(["image/jpeg", "image/png", "image/webp"]),
1046
- size: imageSize,
1047
- width: z7.number().int().positive(),
1048
- height: z7.number().int().positive()
1049
- });
1050
- function parseImageExtraction(raw, value) {
1051
- const result = imageExtractionSchema.parse(value);
1052
- const fields = imageFieldsSchema.parse(raw);
1053
- for (const [key, field] of Object.entries(fields)) {
1054
- const extracted = result.values[key];
1055
- if (extracted === void 0) {
1056
- throw new Error("Image extraction omitted " + key);
1057
- }
1058
- if (extracted !== null && typeof extracted !== field.type) {
1059
- throw new Error("Image extraction returned the wrong type for " + key);
1060
- }
1061
- }
1062
- if (Object.keys(result.values).some((key) => !Object.hasOwn(fields, key))) {
1063
- throw new Error("Image extraction returned an undeclared field");
1064
- }
1065
- return result;
1066
- }
1067
- function parseImageResult(request, value) {
1068
- switch (request.operation) {
1069
- case "inspect":
1070
- return imageInspectionSchema.parse(value);
1071
- case "compare":
1072
- return imageComparisonSchema.parse(value);
1073
- case "review":
1074
- return photoResultSchema.parse(value);
1075
- case "extract":
1076
- return parseImageExtraction(request.fields, value);
1077
- case "seen":
1078
- return imageSightingsSchema.parse(value);
1079
- }
1080
- }
1081
-
1082
- // src/deployments.ts
1083
- var projectCatalogArtifactSchema = catalogDeploymentSchema.omit({ expectedRevision: true }).extend({
1084
- engagement: z8.array(engagementArtifactSchema).max(50).default([]),
1085
- trackers: z8.array(trackerArtifactSchema).max(50).default([]),
1086
- pointLedgers: z8.array(pointLedgerDeclarationSchema).max(50).default([]),
1087
- referrals: z8.array(referralArtifactSchema).max(50).default([]),
1088
- leaderboards: z8.array(leaderboardArtifactSchema).max(50).default([]),
1089
- images: z8.array(imageAssetArtifactSchema).max(10).default([])
1090
- });
1091
- var projectDeploymentSchema = projectCatalogArtifactSchema.extend({ releases: z8.array(releaseSchema).max(50).default([]) }).refine(
1092
- (value) => value.engagement.length > 0 || value.trackers.length > 0 || value.pointLedgers.length > 0 || value.releases.length > 0 || value.leaderboards.length > 0 || value.referrals.length > 0 || value.images.length > 0 || value.types.length > 0 || value.collections.length > 0 || value.supportedInteractions !== void 0,
1093
- "No resources configured. Add engagement, trackers, quests, questTypes, pointLedgers, leaderboards, referrals, images, collections, or supportedInteractions to relay.json."
1094
- );
1095
- var deploymentPreviewSchema = z8.object({
1096
- id: identifier,
1097
- engagement: z8.array(engagementDeploymentEntrySchema).default([]),
1098
- trackers: z8.array(publishedTrackerSchema).default([]),
1099
- baseRevision: z8.number().int().nonnegative(),
1100
- pointLedgers: z8.array(pointLedgerSchema).default([]),
1101
- releases: z8.array(publishedReleaseSchema),
1102
- types: z8.array(questTypeVersionSchema),
1103
- collections: z8.array(questCollectionSchema),
1104
- supportedInteractions: z8.array(interactionSchema),
1105
- referrals: z8.array(publishedReferralSchema).default([]),
1106
- leaderboards: z8.array(leaderboardPreviewSchema).default([]),
1107
- images: z8.array(publishedImageSchema).default([])
1108
- });
1109
- var publishDeploymentSchema = z8.object({ id: identifier }).strict();
1110
- var deploymentResultSchema = deploymentPreviewSchema.extend({
1111
- revision: z8.number().int().positive()
1112
- });
1113
-
1114
- // src/identity-integration.ts
1115
- import { z as z9 } from "zod";
1116
- var identityIntegrationInputSchema = z9.object({
1117
- name: z9.string().trim().min(1).max(80),
1118
- origin: z9.string().url().refine((value) => {
1119
- const url = new URL(value);
1120
- return url.protocol === "https:" && url.origin === value;
1121
- }, "Use an exact HTTPS application origin")
1122
- }).strict();
1123
- var identityIntegrationSchema = authScopeSchema.extend({
1124
- id: z9.string().uuid(),
1125
- name: z9.string(),
1126
- origin: z9.string(),
1127
- expiresAt: z9.number()
1128
- });
1129
- var identityIntegrationSecretSchema = identityIntegrationSchema.extend(
1130
- {
1131
- token: z9.string()
1132
- }
1133
- );
1134
- var participantExchangeSchema = z9.object({
1135
- subject: z9.string().min(1).max(200),
1136
- previousToken: z9.string().max(200).optional()
1137
- }).strict();
1138
-
1139
- // src/index.ts
1140
- function createRelayClient(options) {
1141
- async function send(path, init = {}) {
1142
- const headers = new Headers(init.headers);
1143
- if (options.token) {
1144
- headers.set("Authorization", `Bearer ${options.token}`);
1145
- }
1146
- if (options.organization) {
1147
- headers.set("X-Relay-Organization", options.organization);
1148
- }
1149
- if (options.project) {
1150
- headers.set("X-Relay-Project", options.project);
1151
- }
1152
- if (options.environment) {
1153
- headers.set("X-Relay-Environment", options.environment);
1154
- }
1155
- if (init.method && init.method !== "GET") {
1156
- headers.set("X-Relay-CSRF", "1");
1157
- }
1158
- const response = await fetch(options.baseUrl + path, {
1159
- ...init,
1160
- signal: init.signal ? AbortSignal.any([init.signal, AbortSignal.timeout(15e3)]) : AbortSignal.timeout(15e3),
1161
- headers
1162
- });
1163
- if (!response.ok) {
1164
- const value = await response.json().catch(() => null);
1165
- const error = errorSchema.safeParse(value);
1166
- throw new RelayError(
1167
- error.success ? error.data.error : `Request failed (${response.status})`,
1168
- response.status,
1169
- error.success ? error.data.missing : [],
1170
- error.success ? error.data.code : void 0
1171
- );
1172
- }
1173
- return response;
1174
- }
1175
- async function request(path, schema, init = {}) {
1176
- const response = await send(path, init);
1177
- const value = await response.json();
1178
- return schema.parse(value);
1179
- }
1180
- async function walletRequest(path, schema, init = {}) {
1181
- for (let attempt = 0; ; attempt++) {
1182
- let response;
1183
- try {
1184
- response = await send(path, init);
1185
- } catch (error) {
1186
- const transient = error instanceof RelayError && [500, 502, 503, 504].includes(error.status) || error instanceof TypeError || error instanceof DOMException && error.name === "TimeoutError";
1187
- if (!transient || attempt === 2) {
1188
- throw error;
1189
- }
1190
- await new Promise(
1191
- (resolve) => setTimeout(resolve, attempt === 0 ? 250 : 750)
1192
- );
1193
- continue;
1194
- }
1195
- const value = await response.json();
1196
- return schema.parse(value);
1197
- }
1198
- }
1199
- function query(path, values) {
1200
- const params = new URLSearchParams();
1201
- for (const [key, value] of Object.entries(values)) {
1202
- if (value !== void 0) {
1203
- params.set(key, String(value));
1204
- }
1205
- }
1206
- return path + "?" + params;
1207
- }
1208
- function json(body) {
1209
- return {
1210
- method: "POST",
1211
- headers: { "Content-Type": "application/json" },
1212
- body: JSON.stringify(body)
1213
- };
1214
- }
1215
- function sessionHeaders(session) {
1216
- return { "Content-Type": "application/json", "X-Relay-Session": session };
1217
- }
1218
- const submission = submissions(
1219
- {
1220
- session: () => request("/v1/auth/session", sessionSchema.nullable()),
1221
- upload: (file, action, session) => request("/v1/me/evidence", evidenceSchema, {
1222
- method: "POST",
1223
- headers: {
1224
- "Content-Type": file.type,
1225
- "X-Relay-Upload-ID": action,
1226
- "X-Relay-Session": session
1227
- },
1228
- body: file
1229
- }),
1230
- claim: (input, session) => request("/v1/me/attempts", attemptSchema, {
1231
- ...json(input),
1232
- headers: {
1233
- "Content-Type": "application/json",
1234
- "X-Relay-Session": session
1235
- }
1236
- }),
1237
- retry: (attempt, session) => request(`/v1/attempts/${attempt}/retry`, attemptSchema, {
1238
- ...json({}),
1239
- headers: {
1240
- "Content-Type": "application/json",
1241
- "X-Relay-Session": session
1242
- }
1243
- })
1244
- },
1245
- options.submissionStore,
1246
- options.baseUrl
1247
- );
1248
- const observers = /* @__PURE__ */ new Set();
1249
- async function changeSession(task) {
1250
- for (const observer of observers) {
1251
- observer.clear();
1252
- }
1253
- try {
1254
- return await task();
1255
- } finally {
1256
- for (const observer of observers) {
1257
- void observer.refresh();
1258
- }
1259
- }
1260
- }
1261
- const experience = () => request("/v1/me/experience", questExperienceSchema);
1262
- async function getQuest(id) {
1263
- const quest = (await experience()).quests.find(
1264
- (q) => q.quest === id || q.key === id
1265
- );
1266
- if (!quest) {
1267
- throw new RelayError("Quest not found", 404);
1268
- }
1269
- return quest;
1270
- }
1271
- return {
1272
- referrals: {
1273
- list: () => request("/v1/referrals", z10.array(referralDefinitionSchema)),
1274
- summary: () => request("/v1/referrals/me", referralSummarySchema),
1275
- history: (before) => request(
1276
- query("/v1/referrals/history", { before }),
1277
- referralHistorySchema
1278
- ),
1279
- accept: (input) => request("/v1/referrals/accept", z10.object({ ok: z10.literal(true) }), {
1280
- method: "POST",
1281
- body: JSON.stringify(referralAcceptSchema.parse(input))
1282
- })
1283
- },
1284
- tieredRewards: {
1285
- /**
1286
- * The project's tiered rewards with their keys. Entitlements and reward
1287
- * definitions name them by ID: use this to find the ID for a key.
1288
- */
1289
- list: () => request("/v1/tiered-rewards", z10.array(tieredRewardReferenceSchema))
1290
- },
1291
- pointLedgers: {
1292
- /**
1293
- * The project's point ledgers with their keys and names. Reads return
1294
- * ledger IDs: use this to find the ID for a key, or a ledger's name.
1295
- */
1296
- list: () => request("/v1/point-ledgers", z10.array(pointLedgerReferenceSchema))
1297
- },
1298
- leaderboards: {
1299
- list: () => request("/v1/leaderboards", z10.array(leaderboardDefinitionSchema)),
1300
- standings: (board, options2 = {}) => {
1301
- const parsed = leaderboardQuerySchema.parse(options2);
1302
- return request(
1303
- query(`/v1/leaderboards/${identifier.parse(board)}`, parsed),
1304
- leaderboardPageSchema
1305
- );
1306
- },
1307
- /**
1308
- * A profile's current rank and score on `board` (`profile.id` from a leaderboard row or
1309
- * `profiles.mine()`). Needs the board's access, like `standings`; a profile this viewer
1310
- * cannot see is a 404, as in `profiles.get`.
1311
- */
1312
- placement: (board, profile) => request(
1313
- `/v1/leaderboards/${identifier.parse(board)}/profiles/${identifier.parse(profile)}`,
1314
- leaderboardPlacementSchema
1315
- )
1316
- },
1317
- /**
1318
- * Participant profiles: the display name and avatar other participants see where the project
1319
- * enables profiles in Console. Render an avatar with `avatarSrc(profile.avatar)`.
1320
- */
1321
- profiles: {
1322
- /** The signed-in member's own profile, whatever the project shows to others. */
1323
- mine: () => request("/v1/me/profile", ownProfileSchema),
1324
- /**
1325
- * Changes the member's own name, avatar choice or visibility; omitted fields stay, and null
1326
- * returns to the default. `session` (the ID the app read) makes a changed session a 409.
1327
- */
1328
- update: async (input, options2 = {}) => request("/v1/me/profile", ownProfileSchema, {
1329
- method: "POST",
1330
- headers: {
1331
- "Content-Type": "application/json",
1332
- ...options2.session ? { "X-Relay-Session": options2.session } : {}
1333
- },
1334
- body: JSON.stringify(profileUpdateSchema.parse(input))
1335
- }),
1336
- /**
1337
- * Uploads a JPEG, PNG or WebP of at most 5 MB as the member's avatar. The server crops it to
1338
- * a 512×512 WebP without metadata. Retrying with the same `uploadId` is idempotent.
1339
- */
1340
- uploadAvatar: async (file, options2 = {}) => {
1341
- if (!avatarUploadTypes.includes(file.type)) {
1342
- throw new RelayError("Use a JPEG, PNG or WebP image", 400);
1343
- }
1344
- if (file.size > AVATAR_UPLOAD_LIMIT) {
1345
- throw new RelayError("Use an image smaller than 5 MB", 413);
1346
- }
1347
- return request("/v1/me/profile/avatar", ownProfileSchema, {
1348
- method: "POST",
1349
- headers: {
1350
- "Content-Type": file.type,
1351
- "X-Relay-Upload-ID": identifier.parse(
1352
- options2.uploadId ?? crypto.randomUUID()
1353
- ),
1354
- ...options2.session ? { "X-Relay-Session": options2.session } : {}
1355
- },
1356
- body: file
1357
- });
1358
- },
1359
- /**
1360
- * A profile's name and avatar by its public ID (`profile.id` from a leaderboard row or
1361
- * `mine()`). A profile that is missing, hidden or not visible to this viewer is a 404.
1362
- */
1363
- get: (profile) => request(
1364
- `/v1/profiles/${identifier.parse(profile)}`,
1365
- profileSummarySchema
1366
- ),
1367
- /**
1368
- * A page of the profile's completions of quests that show on profiles, most recently
1369
- * completed first. Pass the page's `nextCursor` as `cursor` for the next one.
1370
- */
1371
- completions: (profile, options2 = {}) => request(
1372
- query(
1373
- `/v1/profiles/${identifier.parse(profile)}/completions`,
1374
- profileCompletionQuerySchema.parse(options2)
1375
- ),
1376
- profileCompletionPageSchema
1377
- ),
1378
- /**
1379
- * The profile's invited and qualified counts and milestones in one referral program. Needs
1380
- * a signed-in participant, as referral programs do.
1381
- */
1382
- referral: (profile, program) => request(
1383
- `/v1/profiles/${identifier.parse(profile)}/referrals/${identifier.parse(program)}`,
1384
- profileReferralSchema
1385
- )
1386
- },
1387
- quests: {
1388
- get: getQuest,
1389
- retry: async (view) => {
1390
- if (view.trigger.kind === "automatic") {
1391
- throw new Error(
1392
- "Automatic quests recover without participant retries."
1393
- );
1394
- }
1395
- if (!view.attempt || view.attempt.status !== "failed") {
1396
- throw new Error("There is no failed attempt to retry.");
1397
- }
1398
- const session = await request(
1399
- "/v1/auth/session",
1400
- sessionSchema.nullable()
1401
- );
1402
- if (session?.principal.kind !== "member") {
1403
- throw new Error("Sign in to retry verification.");
1404
- }
1405
- return request(`/v1/attempts/${view.attempt.id}/retry`, attemptSchema, {
1406
- ...json({}),
1407
- headers: {
1408
- "Content-Type": "application/json",
1409
- "X-Relay-Session": session.id
1410
- }
1411
- });
1412
- },
1413
- experience,
1414
- collection: async (id) => {
1415
- const collection = (await experience()).collections.find(
1416
- (c) => c.id === id || c.key === id
1417
- );
1418
- if (!collection) {
1419
- throw new RelayError("Collection not found", 404);
1420
- }
1421
- return collection;
1422
- },
1423
- open: questActions(submission, getQuest),
1424
- observe: (listener, options2) => {
1425
- const observer = observeProgress(
1426
- {
1427
- session: (signal) => request("/v1/auth/session", sessionSchema.nullable(), { signal }),
1428
- progress: (session, signal) => request("/v1/me/experience", questExperienceSchema, {
1429
- signal,
1430
- headers: { "X-Relay-Session": session }
1431
- })
1432
- },
1433
- listener,
1434
- options2,
1435
- () => observers.delete(observer)
1436
- );
1437
- observers.add(observer);
1438
- observer.clear();
1439
- void observer.refresh();
1440
- return { refresh: observer.refresh, dispose: observer.dispose };
1441
- }
1442
- },
1443
- /** The signed-in member's linked Solana, EVM and Cosmos wallets for this project. */
1444
- wallets: {
1445
- list: () => walletRequest(
1446
- "/v1/me/wallets",
1447
- z10.object({ wallets: z10.array(linkedWalletSchema) })
1448
- ).then((result) => result.wallets),
1449
- challenge: (input, session) => walletRequest(
1450
- "/v1/me/wallets/challenge",
1451
- walletMessageChallengeSchema,
1452
- {
1453
- ...json(walletAddressInputSchema.parse(input)),
1454
- headers: sessionHeaders(session)
1455
- }
1456
- ),
1457
- verify: (input, session) => walletRequest("/v1/me/wallets/verify", linkedWalletSchema, {
1458
- ...json(walletSignatureSchema.parse(input)),
1459
- headers: sessionHeaders(session)
1460
- }),
1461
- /** The address stays bound to this member: another member still cannot link it. */
1462
- unlink: (wallet) => request(
1463
- "/v1/me/wallets/unlink",
1464
- z10.object({ ok: z10.literal(true) }),
1465
- json(
1466
- walletUnlinkInputSchema.parse({
1467
- chain: wallet.chain,
1468
- address: wallet.address
1469
- })
1470
- )
1471
- ),
1472
- /** Asks the signer for its address, signs the returned challenge, and links the wallet. */
1473
- link: async (signer) => {
1474
- const session = await request(
1475
- "/v1/auth/session",
1476
- sessionSchema.nullable()
1477
- );
1478
- if (!session) {
1479
- throw new RelayError("Sign in to continue", 401);
1480
- }
1481
- const challenge = await walletRequest(
1482
- "/v1/me/wallets/challenge",
1483
- walletMessageChallengeSchema,
1484
- {
1485
- ...json(
1486
- walletAddressInputSchema.parse(await walletAddressInput(signer))
1487
- ),
1488
- headers: sessionHeaders(session.id)
1489
- }
1490
- );
1491
- const proof = await signer.sign(challenge.message, challenge.address);
1492
- return walletRequest("/v1/me/wallets/verify", linkedWalletSchema, {
1493
- ...json(
1494
- walletSignatureSchema.parse({ challenge: challenge.id, ...proof })
1495
- ),
1496
- headers: sessionHeaders(session.id)
1497
- });
1498
- }
1499
- },
1500
- /** @deprecated Use `wallets` (multi-chain). This reads and replaces the newest Solana wallet. */
1501
- wallet: {
1502
- connection: () => walletRequest("/v1/me/wallet", walletConnectionSchema.nullable()),
1503
- challenge: (input, session) => walletRequest("/v1/me/wallet/challenge", walletChallengeSchema, {
1504
- ...json(walletChallengeInputSchema.parse(input)),
1505
- headers: sessionHeaders(session)
1506
- }),
1507
- verify: (input, session) => walletRequest("/v1/me/wallet/verify", walletConnectionSchema, {
1508
- ...json(walletProofSchema.parse(input)),
1509
- headers: sessionHeaders(session)
1510
- })
1511
- },
1512
- auth: {
1513
- providers: () => request(
1514
- "/v1/auth/providers",
1515
- z10.object({
1516
- providers: z10.array(
1517
- z10.object({
1518
- provider: authProviderSchema,
1519
- configured: z10.boolean()
1520
- })
1521
- ),
1522
- email: z10.boolean(),
1523
- /** The wallet sign-in this project enabled; older servers omit it. */
1524
- wallets: walletSignInNetworksSchema.default([]),
1525
- demo: z10.boolean()
1526
- })
1527
- ),
1528
- start: (provider, mode) => request(
1529
- "/v1/auth/start",
1530
- z10.object({ url: z10.string() }),
1531
- json({ provider, mode })
1532
- ),
1533
- emailStart: (email) => request(
1534
- "/v1/auth/email/start",
1535
- z10.object({ challenge: z10.string() }),
1536
- json({ email })
1537
- ),
1538
- emailVerify: (challenge, code) => changeSession(
1539
- () => request(
1540
- "/v1/auth/email/verify",
1541
- sessionSchema,
1542
- json({ challenge, code })
1543
- )
1544
- ),
1545
- /**
1546
- * Sign in, or sign up, with a Solana, EVM or Cosmos wallet where the project enabled it
1547
- * (`providers().wallets`). A wallet the member linked while signed in cannot sign in.
1548
- */
1549
- wallet: {
1550
- /** A sign-in challenge for the wallet's address; binds the flow to this browser. */
1551
- start: (input) => request(
1552
- "/v1/auth/wallet/start",
1553
- walletMessageChallengeSchema,
1554
- json(walletAddressInputSchema.parse(input))
1555
- ),
1556
- /** Verifies the signed challenge and starts the session. Not retried: start again instead. */
1557
- verify: (proof) => changeSession(
1558
- () => request(
1559
- "/v1/auth/wallet/verify",
1560
- sessionSchema,
1561
- json(walletSignatureSchema.parse(proof))
1562
- )
1563
- ),
1564
- /**
1565
- * Asks the signer for its address, signs the challenge and starts the session. Throws a
1566
- * `RelayError` with status 403 when the project has not enabled this wallet family.
1567
- */
1568
- signIn: async (signer) => {
1569
- const challenge = await request(
1570
- "/v1/auth/wallet/start",
1571
- walletMessageChallengeSchema,
1572
- json(
1573
- walletAddressInputSchema.parse(await walletAddressInput(signer))
1574
- )
1575
- );
1576
- const proof = await signer.sign(challenge.message, challenge.address);
1577
- return changeSession(
1578
- () => request(
1579
- "/v1/auth/wallet/verify",
1580
- sessionSchema,
1581
- json(
1582
- walletSignatureSchema.parse({
1583
- challenge: challenge.id,
1584
- ...proof
1585
- })
1586
- )
1587
- )
1588
- );
1589
- }
1590
- },
1591
- accounts: () => request("/v1/auth/accounts", z10.array(accountConnectionSchema)),
1592
- disconnect: (provider) => request(
1593
- "/v1/auth/accounts/disconnect",
1594
- z10.object({ ok: z10.boolean() }),
1595
- json({ provider })
1596
- ),
1597
- connections: () => request(
1598
- "/v1/auth/connections",
1599
- z10.array(
1600
- z10.object({
1601
- provider: z10.string(),
1602
- subject: z10.string(),
1603
- signInEnabled: z10.boolean()
1604
- })
1605
- )
1606
- ),
1607
- session: () => request("/v1/auth/session", sessionSchema.nullable()),
1608
- signOut: () => changeSession(
1609
- () => request("/v1/auth/logout", z10.object({ ok: z10.boolean() }), json({}))
1610
- ),
1611
- /** Local development only. Existing hosted apps use createParticipantBackend from /portal-proxy. */
1612
- exchange: (input) => request("/v1/auth/exchange", exchangeResultSchema, json(input))
1613
- },
1614
- me: {
1615
- /** The signed-in member, and the seed of their generated avatar: `defaultAvatar(avatarSeed)`. */
1616
- get: () => request("/v1/me", memberSummarySchema),
1617
- observeProgress: (listener, options2) => {
1618
- const observer = observeProgress(
1619
- {
1620
- session: (signal) => request("/v1/auth/session", sessionSchema.nullable(), { signal }),
1621
- progress: (session, signal) => request("/v1/me/progress", memberProgressSchema, {
1622
- signal,
1623
- headers: { "X-Relay-Session": session }
1624
- })
1625
- },
1626
- listener,
1627
- options2,
1628
- () => observers.delete(observer)
1629
- );
1630
- observers.add(observer);
1631
- observer.clear();
1632
- void observer.refresh();
1633
- return { refresh: observer.refresh, dispose: observer.dispose };
1634
- },
1635
- submission,
1636
- collectionPass: (entitlement, session) => request("/v1/me/collection-pass", collectionPassSchema, {
1637
- ...json({ entitlement: identifier.parse(entitlement) }),
1638
- headers: {
1639
- "Content-Type": "application/json",
1640
- "X-Relay-Session": session
1641
- }
1642
- }),
1643
- /** @deprecated The participant API does not verify passes. Verify them on your server and call `POST /management/v1/passes/verify`. */
1644
- linkPass: (code) => request("/v1/me/pass", attemptSchema, json({ code })),
1645
- progress: () => request("/v1/me/progress", memberProgressSchema),
1646
- rewardDeliveries: () => request("/v1/reward-deliveries", rewardDeliveriesSchema),
1647
- claim: (input, options2) => request("/v1/me/attempts", attemptSchema, {
1648
- ...json(input),
1649
- headers: {
1650
- "Content-Type": "application/json",
1651
- ...options2 ? { "X-Relay-Session": options2.session } : {}
1652
- }
1653
- }),
1654
- upload: (file) => request("/v1/me/evidence", evidenceSchema, {
1655
- method: "POST",
1656
- headers: { "Content-Type": file.type },
1657
- body: file
1658
- })
1659
- },
1660
- evidence: {
1661
- async download(id, requestOptions) {
1662
- const response = await send(
1663
- `/v1/evidence/${identifier.parse(id)}`,
1664
- requestOptions
1665
- );
1666
- const type = response.headers.get("Content-Type");
1667
- if (type !== "image/jpeg" && type !== "image/png") {
1668
- throw new RelayError("Unexpected evidence content type", 502);
1669
- }
1670
- return response.blob();
1671
- }
1672
- },
1673
- /** @deprecated The participant API accepts only member sessions and rejects this staff call. Use the management API with a management key instead. */
1674
- lookupMember: (issuer, subject) => request(
1675
- "/v1/members/lookup",
1676
- z10.object({ member: z10.string() }),
1677
- json({ issuer, subject })
1678
- ),
1679
- /** @deprecated Removed from the participant API. Build quests with `domino` and `@domino-sdk/relay/build`. */
1680
- journeyTemplate: () => request("/v1/journey-template", z10.array(templateSchema)),
1681
- /** @deprecated Removed from the participant API. Build quests with `domino` and `@domino-sdk/relay/build`. */
1682
- template: () => request("/v1/template", templateSchema),
1683
- /** @deprecated The participant API accepts only member sessions and rejects this staff call. Use the management API with a management key instead. */
1684
- snapshot: () => request("/v1/snapshot", snapshotSchema),
1685
- /** @deprecated The participant API rejects publication. Publish with `domino deploy` or the management API. */
1686
- publish: (release) => request("/v1/releases", publishedReleaseSchema, json(release)),
1687
- /** @deprecated The participant API rejects publication. Publish with `domino deploy` or the management API. */
1688
- publishBatch: (releases) => request(
1689
- "/v1/releases/batch",
1690
- z10.array(publishedReleaseSchema),
1691
- json({ releases })
1692
- ),
1693
- progress: (member) => request(
1694
- `/v1/members/${encodeURIComponent(member)}/progress`,
1695
- memberProgressSchema
1696
- ),
1697
- /** @deprecated The participant API accepts only member sessions and rejects this staff call. Use the management API with a management key instead. */
1698
- beginHandover: (id, input) => request(`/v1/entitlements/${id}/begin`, entitlementSchema, json(input)),
1699
- /** @deprecated The participant API accepts only member sessions and rejects this staff call. Use the management API with a management key instead. */
1700
- confirmHandover: (id, input) => request(`/v1/entitlements/${id}/confirm`, entitlementSchema, json(input)),
1701
- upload: (member, file) => request(
1702
- `/v1/evidence?member=${encodeURIComponent(member)}`,
1703
- evidenceSchema,
1704
- {
1705
- method: "POST",
1706
- headers: { "Content-Type": file.type },
1707
- body: file
1708
- }
1709
- ),
1710
- engagement: {
1711
- boundaries: (input) => request(
1712
- "/v1/engagement/boundaries?" + new URLSearchParams(
1713
- Object.entries(engagementPageQuerySchema.parse(input)).flatMap(
1714
- ([key, value]) => value === void 0 ? [] : [[key, String(value)]]
1715
- )
1716
- ).toString(),
1717
- engagementBoundaryPageSchema
1718
- ),
1719
- page: (input) => request(
1720
- "/v1/engagement/history?" + new URLSearchParams(
1721
- Object.entries(engagementPageQuerySchema.parse(input)).flatMap(
1722
- ([key, value]) => value === void 0 ? [] : [[key, String(value)]]
1723
- )
1724
- ).toString(),
1725
- engagementPageSchema
1726
- )
1727
- },
1728
- content: {
1729
- performance: (input) => request(
1730
- "/v1/content/performance?" + new URLSearchParams(
1731
- Object.entries(
1732
- contentPerformanceQuerySchema.parse(input)
1733
- ).flatMap(
1734
- ([key, value]) => value === void 0 ? [] : [[key, String(value)]]
1735
- )
1736
- ).toString(),
1737
- contentPerformancePageSchema
1738
- ),
1739
- qualifications: (input) => request(
1740
- "/v1/content/qualifications?" + new URLSearchParams(
1741
- Object.entries(
1742
- contentQualificationQuerySchema.parse(input)
1743
- ).flatMap(
1744
- ([key, value]) => value === void 0 ? [] : [[key, String(value)]]
1745
- )
1746
- ).toString(),
1747
- contentQualificationPageSchema
1748
- ),
1749
- admissions: (input) => request(
1750
- "/v1/content/admissions?" + new URLSearchParams(
1751
- Object.entries(contentAdmissionQuerySchema.parse(input)).flatMap(
1752
- ([key, value]) => value === void 0 ? [] : [[key, value]]
1753
- )
1754
- ).toString(),
1755
- contentAdmissionPageSchema
1756
- ),
1757
- enroll: (input) => request(
1758
- "/v1/content/enrollments",
1759
- contentEnrollmentSchema,
1760
- json(contentEnrollmentInputSchema.parse(input))
1761
- ),
1762
- claim: (input) => request(
1763
- "/v1/content/claims",
1764
- contentAdmissionSchema,
1765
- json(contentReleaseInputSchema.parse(input))
1766
- )
1767
- },
1768
- submit: (input) => request("/v1/attempts", attemptSchema, json(input)),
1769
- retry: (id) => request(`/v1/attempts/${id}/retry`, attemptSchema, json({})),
1770
- /** @deprecated The participant API accepts only member sessions and rejects this staff call. Use the management API with a management key instead. */
1771
- decide: (id, input) => request(`/v1/attempts/${id}/review`, attemptSchema, json(input))
1772
- };
1773
- }
1774
-
1775
- export {
1776
- pointLedgerPropertiesSchema,
1777
- createPointLedgerSchema,
1778
- pointLedgerSchema,
1779
- pointLedgerDeclarationSchema,
1780
- pointLedgerReferenceSchema,
1781
- rewardDeliverySchema,
1782
- rewardDeliveriesSchema,
1783
- interactionSchema,
1784
- questInteraction,
1785
- questTypeVersionSchema,
1786
- collectionSlotSchema,
1787
- collectionSlotDeclarationSchema,
1788
- questCollectionSchema,
1789
- questInstanceSchema,
1790
- questDraftChangesSchema,
1791
- questDraftSchema,
1792
- questCatalogSchema,
1793
- catalogDeploymentSchema,
1794
- catalogDeploymentPreviewSchema,
1795
- saveQuestDraftSchema,
1796
- publishQuestDraftSchema,
1797
- questRewardViewSchema,
1798
- questViewSchema,
1799
- collectionViewSchema,
1800
- questExperienceSchema,
1801
- questPublicationPreviewSchema,
1802
- allocateQuestSchema,
1803
- allocatedQuestSchema,
1804
- RelayError,
1805
- QuestUpdatedError,
1806
- reconcileQuestDraft,
1807
- questActions,
1808
- avatarSrc,
1809
- defaultAvatar,
1810
- defaultAvatarSvg,
1811
- walletAvatarSeed,
1812
- collectionCodeSchema,
1813
- collectionPassSchema,
1814
- collectionResolveSchema,
1815
- collectionBeginSchema,
1816
- collectionPreviewSchema,
1817
- questConfiguration,
1818
- xIdSchema,
1819
- xConnectionSchema,
1820
- xProfileSchema,
1821
- xPostSchema,
1822
- xRequestSchema,
1823
- imageReferenceSchema,
1824
- imageFieldSchema,
1825
- imageFieldsSchema,
1826
- imageRequestSchema,
1827
- imageInspectionSchema,
1828
- imageComparisonSchema,
1829
- imageExtractionSchema,
1830
- imageSightingsSchema,
1831
- imageAssetArtifactSchema,
1832
- imageUploadRequestSchema,
1833
- imageUploadsSchema,
1834
- publishedImageSchema,
1835
- parseImageExtraction,
1836
- parseImageResult,
1837
- projectCatalogArtifactSchema,
1838
- projectDeploymentSchema,
1839
- deploymentPreviewSchema,
1840
- publishDeploymentSchema,
1841
- deploymentResultSchema,
1842
- identityIntegrationInputSchema,
1843
- identityIntegrationSchema,
1844
- identityIntegrationSecretSchema,
1845
- participantExchangeSchema,
1846
- createRelayClient
1847
- };