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