@domino-sdk/relay 1.0.0-next.13 → 1.0.0-next.15

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 (35) hide show
  1. package/README.md +1 -1
  2. package/dist/authoring.d.ts +58 -44
  3. package/dist/authoring.js +58 -45
  4. package/dist/browser.d.ts +111 -151
  5. package/dist/browser.js +6 -6
  6. package/dist/{catalog-C85V1NgW.d.ts → catalog-CVXUHxiO.d.ts} +12 -99
  7. package/dist/{chunk-M64XHC42.js → chunk-76F4LSFP.js} +2 -2
  8. package/dist/{chunk-II7DRZSL.js → chunk-CYD6QMHU.js} +1 -1
  9. package/dist/{chunk-KS33GOUL.js → chunk-EJWDAF7I.js} +4 -4
  10. package/dist/chunk-FJ6O44NE.js +51 -0
  11. package/dist/{chunk-WQ24ILGO.js → chunk-G3YSBMJH.js} +7 -3
  12. package/dist/{chunk-MNQI35OG.js → chunk-HWRBLM7E.js} +3 -3
  13. package/dist/{chunk-CRNQP7NV.js → chunk-J4OYDYJE.js} +693 -676
  14. package/dist/{chunk-OA2KMQVZ.js → chunk-J52HD4AA.js} +11 -1
  15. package/dist/{chunk-EZWNGI5B.js → chunk-RCBJBFLC.js} +4 -4
  16. package/dist/{chunk-FXYJ4OMX.js → chunk-WMTROSYF.js} +1 -1
  17. package/dist/{client-Bf9PNUIi.d.ts → client-dfXT6hhq.d.ts} +7 -47
  18. package/dist/{deployments-CNCP0JnD.d.ts → deployments-C9wkQ06n.d.ts} +109 -125
  19. package/dist/{images-BG4pdO4y.d.ts → images-CRxHUuG5.d.ts} +1 -1
  20. package/dist/index.d.ts +3 -3
  21. package/dist/index.js +5 -5
  22. package/dist/{integrations-EjQzIApL.d.ts → integrations-C1vB6Ry6.d.ts} +93 -1
  23. package/dist/internal.d.ts +13 -4
  24. package/dist/internal.js +10 -4
  25. package/dist/portal-proxy.js +2 -2
  26. package/dist/react/index.d.ts +441 -655
  27. package/dist/react/index.js +1190 -810
  28. package/dist/{schema-BF_TQsmT.d.ts → schema-C4F2K9cm.d.ts} +9 -74
  29. package/dist/schema.d.ts +6 -7
  30. package/dist/schema.js +18 -16
  31. package/dist/server/index.d.ts +125 -233
  32. package/dist/server/index.js +16 -22
  33. package/dist/{trackers-W7nRdRr7.d.ts → trackers-7lxSWp2g.d.ts} +56 -14
  34. package/package.json +4 -2
  35. package/dist/observations-CQ6IcNuQ.d.ts +0 -90
@@ -1,19 +1,87 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { a6 as referralAcceptSchema, c as LeaderboardQuery, G as engagementPageQuerySchema, s as contentQualificationQuerySchema, i as contentAdmissionQuerySchema, k as contentEnrollmentInputSchema, u as contentReleaseInputSchema, af as referralSummarySchema, a as LeaderboardPage } from '../referrals-KQfP4pBf.js';
4
- import { ao as submitSchema, x as contentPerformanceQuerySchema, T as TieredRewardReference } from '../schema-BF_TQsmT.js';
5
- import { a as PageQuery, f as exchangeSchema } from '../pages-KYK1L60W.js';
3
+ import { a6 as referralAcceptSchema, c as LeaderboardQuery, G as engagementPageQuerySchema, s as contentQualificationQuerySchema, i as contentAdmissionQuerySchema, k as contentEnrollmentInputSchema, u as contentReleaseInputSchema, b as LeaderboardPlacement, af as referralSummarySchema, a as LeaderboardPage } from '../referrals-KQfP4pBf.js';
4
+ import { ao as submitSchema, x as contentPerformanceQuerySchema, T as TieredRewardReference } from '../schema-C4F2K9cm.js';
5
+ import { a as PageQuery, f as exchangeSchema, P as Page, c as ParticipantSession, M as MemberSummary } from '../pages-KYK1L60W.js';
6
6
  import { x as walletAddressInputSchema, M as walletSignatureSchema, L as LinkedWallet, f as WalletSigner, d as WalletSignInNetwork } from '../wallet-BtiG0Xhr.js';
7
- import { h as QuestView, J as questExperienceSchema, e as PointHistoryQuery, c as PointEntryKind, P as PointBalance, b as PointEntry, f as PointLedgerReference } from '../catalog-C85V1NgW.js';
8
- import { F as profileUpdateSchema, c as ProfileCompletionQuery, a as ProfileCompletion, e as ProfileSummary, f as ProfileUpdate } from '../profiles-BtOXMGcC.js';
7
+ import { h as QuestView, J as questExperienceSchema, e as PointHistoryQuery, c as PointEntryKind, a as PointBalances, b as PointEntry, f as PointLedgerReference } from '../catalog-CVXUHxiO.js';
8
+ import { F as profileUpdateSchema, c as ProfileCompletionQuery, f as ProfileUpdate, e as ProfileSummary, a as ProfileCompletion, d as ProfileReferral } from '../profiles-BtOXMGcC.js';
9
9
  import * as zod from 'zod';
10
10
  import { z } from 'zod';
11
- import { k as RelayRequestOptions, c as QuestController, a as ProgressObservation, b as ProgressObserver, P as PhotoSubmission, e as QuizSubmission, C as ClaimSubmission, V as ValueSubmission, S as SubmissionDraft } from '../client-Bf9PNUIi.js';
11
+ import { g as RelayError, k as RelayRequestOptions, c as QuestController, a as ProgressObservation, b as ProgressObserver, P as PhotoSubmission, e as QuizSubmission, C as ClaimSubmission, V as ValueSubmission, S as SubmissionDraft } from '../client-dfXT6hhq.js';
12
12
  import { a as AuthProvider, A as AccountConnection } from '../submission-input-CccwAl5Z.js';
13
13
  import { createBrowserRelayClient, SignInMethods, CosmosChains, SignInOption, SignInUnavailable, SignInWallet, DiscoveredWallet } from '../browser.js';
14
14
  export { EmailSignInOption, OAuthSignInOption, SignInIcon, WalletSignInOption } from '../browser.js';
15
15
  import '../request-error-VnTiMXc3.js';
16
16
 
17
+ /**
18
+ * A query's identity: plain JSON values, outermost group first, such as
19
+ * `["leaderboards", "weekly", 25]`. `invalidate(["leaderboards"])` matches every key that starts
20
+ * with those values.
21
+ */
22
+ type QueryKey = readonly (string | number | boolean | null)[];
23
+ /**
24
+ * What every read hook returns, besides `refresh`.
25
+ *
26
+ * - `loading`: nothing read yet for this participant.
27
+ * - `ready`: `data` is the last good value. `error` is set while the latest refresh failed and the
28
+ * value on screen may be out of date; the next successful read clears it.
29
+ * - `error`: the first read failed, or the thing is gone (a 404).
30
+ * - `signed-out`: the hook needs a signed-in member and there is none.
31
+ */
32
+ type QuerySnapshot<T> = {
33
+ status: "loading";
34
+ data: undefined;
35
+ error: null;
36
+ } | {
37
+ status: "ready";
38
+ data: T;
39
+ error: RelayError | null;
40
+ } | {
41
+ status: "error";
42
+ data: undefined;
43
+ error: RelayError;
44
+ };
45
+ type MemberSnapshot<T> = QuerySnapshot<T> | {
46
+ status: "signed-out";
47
+ data: undefined;
48
+ error: null;
49
+ };
50
+ /** What a query's read function receives: pass it on as the client method's last argument. */
51
+ type QueryContext = {
52
+ /** Aborted when the read is superseded, invalidated, or the participant changes. */
53
+ signal: AbortSignal;
54
+ /** The session the cache belongs to. Relay refuses a read for another session (`stale-session`). */
55
+ session?: string;
56
+ };
57
+ type QueryRead<T> = (context: QueryContext) => Promise<T>;
58
+ type QueryOptions = {
59
+ /** Read again this often while the page is visible. Polling pauses while the tab is hidden. */
60
+ intervalMs?: number;
61
+ /** Read again when the window regains focus. */
62
+ refetchOnFocus?: boolean;
63
+ };
64
+
65
+ type Collection = {
66
+ id: string;
67
+ /** The collection's authoring key, when it has one. */
68
+ key?: string;
69
+ title: string;
70
+ quests: QuestView[];
71
+ };
72
+ /** The participant's quests and collections, as `useExperience().data` holds them. */
73
+ type ExperienceData = {
74
+ quests: QuestView[];
75
+ collections: Collection[];
76
+ };
77
+ /**
78
+ * Everything to list, in display order: collections as Console arranged them,
79
+ * then any quest nobody has placed yet, sorted by `order`: authoring keys
80
+ * (the `key` in quests/*.quest.ts), or quest IDs for quests without one.
81
+ * Archived quests are left out.
82
+ */
83
+ declare function questGroups(experience: ExperienceData, order?: string[]): Collection[];
84
+
17
85
  type RelayClient = ReturnType<typeof createBrowserRelayClient>;
18
86
  /** The participant client, for calls no hook covers. */
19
87
  declare function useRelay(): {
@@ -552,10 +620,6 @@ declare function useRelay(): {
552
620
  connections: ("google" | "apple" | "x" | "discord")[];
553
621
  trigger: {
554
622
  kind: "automatic";
555
- observation?: {
556
- source: string;
557
- type: string;
558
- } | undefined;
559
623
  content?: {
560
624
  tracker: string;
561
625
  window: {
@@ -568,7 +632,7 @@ declare function useRelay(): {
568
632
  } | undefined;
569
633
  tracker?: {
570
634
  tracker: string;
571
- source: "custom" | "x-search";
635
+ source: string;
572
636
  beneficiary: string;
573
637
  on?: "new-record" | "new-observation" | "data-changed" | undefined;
574
638
  where?: {
@@ -644,10 +708,6 @@ declare function useRelay(): {
644
708
  kind: "staff-required";
645
709
  } | {
646
710
  kind: "automatic";
647
- observation?: {
648
- source: string;
649
- type: string;
650
- } | undefined;
651
711
  } | {
652
712
  kind: "checking";
653
713
  attempt: string;
@@ -866,10 +926,6 @@ declare function useRelay(): {
866
926
  connections: ("google" | "apple" | "x" | "discord")[];
867
927
  trigger: {
868
928
  kind: "automatic";
869
- observation?: {
870
- source: string;
871
- type: string;
872
- } | undefined;
873
929
  content?: {
874
930
  tracker: string;
875
931
  window: {
@@ -882,7 +938,7 @@ declare function useRelay(): {
882
938
  } | undefined;
883
939
  tracker?: {
884
940
  tracker: string;
885
- source: "custom" | "x-search";
941
+ source: string;
886
942
  beneficiary: string;
887
943
  on?: "new-record" | "new-observation" | "data-changed" | undefined;
888
944
  where?: {
@@ -958,10 +1014,6 @@ declare function useRelay(): {
958
1014
  kind: "staff-required";
959
1015
  } | {
960
1016
  kind: "automatic";
961
- observation?: {
962
- source: string;
963
- type: string;
964
- } | undefined;
965
1017
  } | {
966
1018
  kind: "checking";
967
1019
  attempt: string;
@@ -1127,10 +1179,6 @@ declare function useRelay(): {
1127
1179
  connections: ("google" | "apple" | "x" | "discord")[];
1128
1180
  trigger: {
1129
1181
  kind: "automatic";
1130
- observation?: {
1131
- source: string;
1132
- type: string;
1133
- } | undefined;
1134
1182
  content?: {
1135
1183
  tracker: string;
1136
1184
  window: {
@@ -1143,7 +1191,7 @@ declare function useRelay(): {
1143
1191
  } | undefined;
1144
1192
  tracker?: {
1145
1193
  tracker: string;
1146
- source: "custom" | "x-search";
1194
+ source: string;
1147
1195
  beneficiary: string;
1148
1196
  on?: "new-record" | "new-observation" | "data-changed" | undefined;
1149
1197
  where?: {
@@ -1219,10 +1267,6 @@ declare function useRelay(): {
1219
1267
  kind: "staff-required";
1220
1268
  } | {
1221
1269
  kind: "automatic";
1222
- observation?: {
1223
- source: string;
1224
- type: string;
1225
- } | undefined;
1226
1270
  } | {
1227
1271
  kind: "checking";
1228
1272
  attempt: string;
@@ -1391,10 +1435,6 @@ declare function useRelay(): {
1391
1435
  connections: ("google" | "apple" | "x" | "discord")[];
1392
1436
  trigger: {
1393
1437
  kind: "automatic";
1394
- observation?: {
1395
- source: string;
1396
- type: string;
1397
- } | undefined;
1398
1438
  content?: {
1399
1439
  tracker: string;
1400
1440
  window: {
@@ -1407,7 +1447,7 @@ declare function useRelay(): {
1407
1447
  } | undefined;
1408
1448
  tracker?: {
1409
1449
  tracker: string;
1410
- source: "custom" | "x-search";
1450
+ source: string;
1411
1451
  beneficiary: string;
1412
1452
  on?: "new-record" | "new-observation" | "data-changed" | undefined;
1413
1453
  where?: {
@@ -1483,10 +1523,6 @@ declare function useRelay(): {
1483
1523
  kind: "staff-required";
1484
1524
  } | {
1485
1525
  kind: "automatic";
1486
- observation?: {
1487
- source: string;
1488
- type: string;
1489
- } | undefined;
1490
1526
  } | {
1491
1527
  kind: "checking";
1492
1528
  attempt: string;
@@ -1874,10 +1910,6 @@ declare function useRelay(): {
1874
1910
  connections: ("google" | "apple" | "x" | "discord")[];
1875
1911
  trigger: {
1876
1912
  kind: "automatic";
1877
- observation?: {
1878
- source: string;
1879
- type: string;
1880
- } | undefined;
1881
1913
  content?: {
1882
1914
  tracker: string;
1883
1915
  window: {
@@ -1890,7 +1922,7 @@ declare function useRelay(): {
1890
1922
  } | undefined;
1891
1923
  tracker?: {
1892
1924
  tracker: string;
1893
- source: "custom" | "x-search";
1925
+ source: string;
1894
1926
  beneficiary: string;
1895
1927
  on?: "new-record" | "new-observation" | "data-changed" | undefined;
1896
1928
  where?: {
@@ -1966,10 +1998,6 @@ declare function useRelay(): {
1966
1998
  kind: "staff-required";
1967
1999
  } | {
1968
2000
  kind: "automatic";
1969
- observation?: {
1970
- source: string;
1971
- type: string;
1972
- } | undefined;
1973
2001
  } | {
1974
2002
  kind: "checking";
1975
2003
  attempt: string;
@@ -2437,10 +2465,12 @@ declare function useRelay(): {
2437
2465
  };
2438
2466
 
2439
2467
  /**
2440
- * One participant client and one quest observer for the whole app.
2468
+ * One participant client, one quest observer and one query cache for the whole app.
2441
2469
  *
2442
- * `baseUrl` defaults to `/relay`, the participant proxy that `domino dev` and
2443
- * Domino hosting provide. Pass `client` to share a client created elsewhere.
2470
+ * `baseUrl` defaults to `/relay`, the participant proxy that `domino dev` and Domino hosting
2471
+ * provide. Pass `client` to share a client created elsewhere. A new `client` or `baseUrl` takes
2472
+ * effect at once: the old observer stops, reads in flight are cancelled, and the hooks start again
2473
+ * from an empty cache. Safe to render on the server; nothing is read until it mounts.
2444
2474
  */
2445
2475
  declare function RelayProvider({ client, baseUrl, children, }: {
2446
2476
  client?: RelayClient;
@@ -2448,90 +2478,139 @@ declare function RelayProvider({ client, baseUrl, children, }: {
2448
2478
  children: ReactNode;
2449
2479
  }): react.JSX.Element;
2450
2480
 
2481
+ /** What every read hook returns: `{ status, data, error, refresh }`. */
2482
+ type Query<T> = QuerySnapshot<T> & {
2483
+ /** Reads again now, replacing a read in flight. Never rejects: a failure lands in `error`. */
2484
+ refresh(): Promise<void>;
2485
+ };
2486
+ /** A read that needs a signed-in member: a `Query`, or `signed-out` while nobody is. */
2487
+ type MemberQuery<T> = MemberSnapshot<T> & {
2488
+ refresh(): Promise<void>;
2489
+ };
2490
+
2491
+ /** What a paged read adds to its query: the next page. */
2492
+ type PageLoader = {
2493
+ /** Appends the next page; does nothing while one loads, or when there is none. */
2494
+ loadMore(): Promise<void>;
2495
+ /** True while `loadMore()` reads. */
2496
+ loadingMore: boolean;
2497
+ /** Why the last `loadMore()` failed, until the next one starts. */
2498
+ loadMoreError: RelayError | null;
2499
+ };
2500
+ /** A paged read as one growing list: `data.items` and `data.nextCursor`, and `loadMore()`. */
2501
+ type PagedQuery<T> = Query<Page<T>> & PageLoader;
2502
+ /** A paged read that needs a signed-in member: a `PagedQuery`, or `signed-out` while nobody is. */
2503
+ type MemberPagedQuery<T> = MemberQuery<Page<T>> & PageLoader;
2504
+ /** A paged read: `read` receives `cursor` (absent for the first page) with `signal` and `session`. */
2505
+ type PageRead<T> = (context: QueryContext & {
2506
+ cursor?: string;
2507
+ }) => Promise<Page<T>>;
2451
2508
  /**
2452
- * All a participant sees of a reward Domino delivers elsewhere, such as a
2453
- * Discord role. Attempts, errors, and role IDs stay out of the page.
2509
+ * A cached read through the participant client, for calls no hook covers.
2510
+ *
2511
+ * Every component that asks for the same `key` shares one read and one value; `useInvalidate()`
2512
+ * marks it out of date after a change. `read` receives `{ signal, session }`: pass them as the
2513
+ * client method's last argument, so a superseded read is cancelled and Relay refuses one for
2514
+ * another session. The value belongs to the participant signed in when it was read, and is
2515
+ * dropped when they sign out or another member signs in.
2516
+ *
2517
+ * ```tsx
2518
+ * const boards = useRelayQuery(["leaderboards", "list"], (options) =>
2519
+ * relay.leaderboards.list(options)
2520
+ * )
2521
+ * ```
2454
2522
  */
2455
- type RoleDelivery = {
2456
- quest: string;
2457
- status: "pending" | "delivered" | "failed";
2458
- };
2459
- declare function sanitizeDeliveries(deliveries: RoleDelivery[]): RoleDelivery[];
2523
+ declare function useRelayQuery<T>(key: QueryKey, read: QueryRead<T>, options?: QueryOptions): Query<T>;
2460
2524
  /**
2461
- * Changes whenever the participant completes something, which is the only time
2462
- * a new delivery can appear. Empty until the first completion, so participants
2463
- * with nothing to deliver never ask.
2525
+ * `useRelayQuery` for reads that need a signed-in member. Nothing is read while nobody is signed
2526
+ * in, and `status` is then `signed-out`.
2464
2527
  */
2465
- declare function completionMark(quests: Pick<QuestView, "quest" | "completed" | "completionCount">[]): string;
2466
- declare function hasPending(deliveries: RoleDelivery[]): boolean;
2528
+ declare function useMemberQuery<T>(key: QueryKey, read: QueryRead<T>, options?: QueryOptions): MemberQuery<T>;
2467
2529
  /**
2468
- * The words for one quest, or null when it delivers nothing. This never
2469
- * changes whether the quest is completed: the role follows the completion.
2530
+ * A paged read (`{ cursor }` in, `{ items, nextCursor }` out) as one growing list, for pages a
2531
+ * signed-out visitor can read too. `refresh()` and an invalidation start again from the first
2532
+ * page; a page that arrives after that, or for another key or participant, is dropped rather than
2533
+ * appended.
2470
2534
  */
2471
- declare function roleDeliveryLabel(deliveries: RoleDelivery[], quest: string): "Role delivery pending" | "Role delivery failed" | "Role granted" | null;
2472
- /** Waits between reads: while a role is pending, then after each failure. */
2473
- declare const PENDING_POLL_MS = 5000;
2474
- declare const RETRY_DELAYS_MS: number[];
2475
- type Schedule = (run: () => void, ms: number) => () => void;
2535
+ declare function useRelayPages<T>(key: QueryKey, read: PageRead<T>, options?: QueryOptions): PagedQuery<T>;
2536
+ /** `useRelayPages` for pages that need a signed-in member: `signed-out` while nobody is. */
2537
+ declare function useMemberPages<T>(key: QueryKey, read: PageRead<T>, options?: QueryOptions): MemberPagedQuery<T>;
2476
2538
  /**
2477
- * Follows one participant's role deliveries until there is nothing to wait for.
2539
+ * Marks cached reads out of date after a change. Each key is a prefix: `[["leaderboards"]]`
2540
+ * reads every leaderboard on screen again and forgets the rest. With no keys, everything.
2478
2541
  *
2479
- * - A read that succeeds with nothing pending ends the watch: no empty polling.
2480
- * - A pending role is read again every few seconds.
2481
- * - A failed read, including the very first, is retried with a short backoff.
2482
- * After the last retry the watch rests until `wake()`, which the page calls
2483
- * when it becomes visible or focused again.
2484
- * - Failures never report anything, so a pending role stays pending on screen.
2542
+ * The SDK's own mutating hooks call it; call it after a change made with `useRelay()` directly.
2543
+ * `["experience"]` reads the quest experience again.
2485
2544
  */
2486
- declare function watchRoleDeliveries(options: {
2487
- read: () => Promise<RoleDelivery[]>;
2488
- onChange: (deliveries: RoleDelivery[]) => void;
2489
- schedule?: Schedule;
2490
- }): {
2491
- wake(): void;
2492
- dispose(): void;
2545
+ declare function useInvalidate(): (keys?: QueryKey[]) => Promise<void>;
2546
+ /**
2547
+ * Who is signed in: the session (`data`, null for a signed-out visitor) and its `member`.
2548
+ * Independent of the quest experience: a failing experience read signs nobody out.
2549
+ */
2550
+ declare function useSession(): Query<ParticipantSession | null> & {
2551
+ /** The signed-in member's ID, or null. */
2552
+ member: string | null;
2493
2553
  };
2494
2554
 
2495
- type Collection = {
2496
- id: string;
2497
- title: string;
2498
- quests: QuestView[];
2555
+ /**
2556
+ * The key prefixes the SDK's hooks cache under, for `useInvalidate()`: after a change made with
2557
+ * `useRelay()` directly, `invalidate([relayKeys.leaderboards])` reads every leaderboard on screen
2558
+ * again. Keys of your own `useRelayQuery` calls must not start with these.
2559
+ */
2560
+ declare const relayKeys: {
2561
+ /** `useExperience`: invalidating it reads the quest experience again. */
2562
+ readonly experience: readonly ["experience"];
2563
+ /** `useMe`. */
2564
+ readonly me: readonly ["me"];
2565
+ /** `useAccounts`. */
2566
+ readonly accounts: readonly ["accounts"];
2567
+ /** `useWallets`. */
2568
+ readonly wallets: readonly ["wallets"];
2569
+ /** `useMyProfile`, `useProfile`, `useProfileCompletions`, `useProfileReferral`. */
2570
+ readonly profiles: readonly ["profiles"];
2571
+ /** `useLeaderboard`, `useLeaderboardPlacement`. */
2572
+ readonly leaderboards: readonly ["leaderboards"];
2573
+ /** `useReferralSummary`. */
2574
+ readonly referrals: readonly ["referrals"];
2575
+ /** `usePointLedgers`. */
2576
+ readonly pointLedgers: readonly ["point-ledgers"];
2577
+ /** `useTieredRewards`. */
2578
+ readonly tieredRewards: readonly ["tiered-rewards"];
2579
+ /** `useSignInMethods`. */
2580
+ readonly signInMethods: readonly ["sign-in-methods"];
2581
+ /** `useBalances`, `usePointHistory`. */
2582
+ readonly balances: readonly ["balances"];
2499
2583
  };
2500
- type Experience = {
2501
- status: "loading";
2502
- } | {
2503
- status: "signed-out";
2504
- } | {
2505
- status: "error";
2506
- message: string;
2507
- } | {
2508
- status: "ready";
2509
- quests: QuestView[];
2510
- collections: Collection[];
2511
- /** Set when a refresh failed; the last good data stays on screen. */
2512
- stale: string | null;
2584
+
2585
+ /**
2586
+ * All a participant sees of a reward Domino delivers elsewhere, such as a
2587
+ * Discord role. Attempts, errors, and role IDs stay out of the page.
2588
+ */
2589
+ type RoleDelivery = {
2590
+ quest: string;
2591
+ status: "pending" | "delivered" | "failed";
2513
2592
  };
2593
+ type KnownRoleDeliveryStatus = "pending" | "failed" | "granted";
2594
+ /** Where one quest's role deliveries stand, as a code an app can localize; open for new codes. */
2595
+ type RoleDeliveryStatus = KnownRoleDeliveryStatus | (string & {});
2514
2596
  /**
2515
- * Everything to list, in display order: collections as Console arranged them,
2516
- * then any quest nobody has placed yet, sorted by `order`: authoring keys
2517
- * (the `key` in quests/*.quest.ts), or quest IDs for quests without one.
2518
- * Archived quests are left out.
2597
+ * Where one quest's roles stand, or null when it delivers nothing. This never
2598
+ * changes whether the quest is completed: the role follows the completion.
2599
+ * `failed` means Domino gave up; only the organizer can grant the role now.
2519
2600
  */
2520
- declare function questGroups(experience: Extract<Experience, {
2521
- status: "ready";
2522
- }>, order?: string[]): Collection[];
2601
+ declare function roleDeliveryStatus(deliveries: RoleDelivery[], quest: string): KnownRoleDeliveryStatus | null;
2602
+ /** `roleDeliveryStatus` in English, or null for null. */
2603
+ declare function roleDeliveryLabel(status: RoleDeliveryStatus | null): "Role delivery pending" | "Role delivery failed" | "Role granted" | null;
2523
2604
 
2524
- type ExperienceContext = {
2525
- experience: Experience;
2526
- /** The signed-in participant's member ID, or null. */
2527
- member: string | null;
2528
- /** Discord roles earned from quests, and whether each has arrived yet. */
2605
+ /**
2606
+ * The signed-in participant's quests and collections, polled for the whole app by one observer,
2607
+ * which pauses while the tab is hidden. `data` holds `quests` and `collections`; `signed-out`
2608
+ * means nobody is signed in. `deliveries` lists Discord roles earned from quests and whether each
2609
+ * has arrived yet.
2610
+ */
2611
+ declare function useExperience(): MemberQuery<ExperienceData> & {
2529
2612
  deliveries: RoleDelivery[];
2530
- /** Read again now instead of waiting for the next poll. */
2531
- refresh(): Promise<void>;
2532
2613
  };
2533
- /** The participant's quests and collections, polled for the whole app. */
2534
- declare function useExperience(): ExperienceContext;
2535
2614
 
2536
2615
  type QuestInput = {
2537
2616
  kind: "text" | "url";
@@ -2586,10 +2665,6 @@ declare function useQuest(observed: QuestView): {
2586
2665
  connections: ("google" | "apple" | "x" | "discord")[];
2587
2666
  trigger: {
2588
2667
  kind: "automatic";
2589
- observation?: {
2590
- source: string;
2591
- type: string;
2592
- } | undefined;
2593
2668
  content?: {
2594
2669
  tracker: string;
2595
2670
  window: {
@@ -2602,7 +2677,7 @@ declare function useQuest(observed: QuestView): {
2602
2677
  } | undefined;
2603
2678
  tracker?: {
2604
2679
  tracker: string;
2605
- source: "custom" | "x-search";
2680
+ source: string;
2606
2681
  beneficiary: string;
2607
2682
  on?: "new-record" | "new-observation" | "data-changed" | undefined;
2608
2683
  where?: {
@@ -2678,10 +2753,6 @@ declare function useQuest(observed: QuestView): {
2678
2753
  kind: "staff-required";
2679
2754
  } | {
2680
2755
  kind: "automatic";
2681
- observation?: {
2682
- source: string;
2683
- type: string;
2684
- } | undefined;
2685
2756
  } | {
2686
2757
  kind: "checking";
2687
2758
  attempt: string;
@@ -2821,7 +2892,7 @@ declare function useQuest(observed: QuestView): {
2821
2892
  /** Input recovered after the quest changed. Prefill the form with it. */
2822
2893
  draft: SubmissionDraft | null;
2823
2894
  notice: QuestNotice;
2824
- error: string | null;
2895
+ error: RelayError | null;
2825
2896
  busy: boolean;
2826
2897
  saved: SavedSubmission;
2827
2898
  /** The attempt the last "received" notice is about. */
@@ -2839,8 +2910,37 @@ declare function useQuest(observed: QuestView): {
2839
2910
  };
2840
2911
 
2841
2912
  type QuestTone = "open" | "done" | "waiting" | "attention" | "locked" | "off";
2913
+ /**
2914
+ * Why a quest shows the way it does, as a stable code an app can localize. `questStatusText` turns
2915
+ * it into English.
2916
+ *
2917
+ * - `completed`: nothing more to do.
2918
+ * - `paused`, `closed`: the operator paused or archived the quest.
2919
+ * - `streak`: a streak in progress; `view.streak` has the counts and when the next day starts.
2920
+ * - `streak-budget-exhausted`: the streak reached a milestone whose reward budget ran out.
2921
+ * - `automatic`: Domino counts this by itself whenever the participant qualifies.
2922
+ * - `in-progress`: an automatic quest Domino is running.
2923
+ * - `locked`: prerequisites first; `view.availability.missing` lists them.
2924
+ * - `checking`, `in-review`: a submission is being verified, or reviewed by a person.
2925
+ * - `retryable`: verification could not run; the participant can retry.
2926
+ * - `staff-required`: a staff member confirms it.
2927
+ * - `rejected`: the last submission was not accepted; the participant can try again.
2928
+ * - `ready`: the participant can act now.
2929
+ */
2930
+ type KnownQuestStatusCode = "completed" | "paused" | "closed" | "streak" | "streak-budget-exhausted" | "automatic" | "in-progress" | "locked" | "checking" | "in-review" | "retryable" | "staff-required" | "rejected" | "ready";
2931
+ /** A known code, or one a newer SDK adds: render unknown codes with a neutral fallback. */
2932
+ type QuestStatusCode = KnownQuestStatusCode | (string & {});
2842
2933
  type QuestStatus = {
2843
2934
  tone: QuestTone;
2935
+ code: QuestStatusCode;
2936
+ /**
2937
+ * Set when the participant's last submission was rejected: the evaluator's reason, or an empty
2938
+ * string when it gave none.
2939
+ */
2940
+ reason?: string;
2941
+ };
2942
+ /** `QuestStatus` in English: a short `label`, and a sentence of `detail` when there is more. */
2943
+ type QuestStatusText = QuestStatus & {
2844
2944
  label: string;
2845
2945
  detail?: string;
2846
2946
  };
@@ -2856,11 +2956,6 @@ declare function isFinished(view: QuestView): boolean;
2856
2956
  * status and nothing to press.
2857
2957
  */
2858
2958
  declare function isAutomatic(view: QuestView): boolean;
2859
- /**
2860
- * True for automatic quests driven by observed activity, possibly many times
2861
- * and without telling the participant. They read "Automatic", not "In progress".
2862
- */
2863
- declare function runsSilently(view: QuestView): boolean;
2864
2959
  /** "Completed 3 times" for repeatable quests, otherwise nothing. */
2865
2960
  declare function completionCountLabel(view: QuestView): string | null;
2866
2961
  /**
@@ -2868,10 +2963,18 @@ declare function completionCountLabel(view: QuestView): string | null;
2868
2963
  * open again, so show the normal submit action rather than a finished state.
2869
2964
  */
2870
2965
  declare function canRepeat(view: QuestView): boolean;
2871
- /** Hidden quests run in the background and are never listed. */
2872
- declare function visibleQuests<T extends Pick<QuestView, "visibility">>(quests: T[]): T[];
2873
- /** Turns a QuestView into words. Pure, so copy changes stay in one place. */
2874
- declare function questStatus(view: QuestView): QuestStatus;
2966
+ /**
2967
+ * What a QuestView means for the participant, as a tone and a code. Pure and free of copy: render
2968
+ * the code in your own words, or call `questStatusText` for English.
2969
+ */
2970
+ declare function questStatus(view: QuestView): QuestStatus & {
2971
+ code: KnownQuestStatusCode;
2972
+ };
2973
+ /**
2974
+ * `questStatus` in English, for apps that do not localize: a `label` such as "In review", and a
2975
+ * `detail` sentence when there is more to say.
2976
+ */
2977
+ declare function questStatusText(view: QuestView): QuestStatusText;
2875
2978
  /** True when the participant can submit something right now. */
2876
2979
  declare function canSubmit(view: QuestView): boolean;
2877
2980
  declare function progressOf(quests: QuestView[]): {
@@ -2882,25 +2985,24 @@ declare function progressOf(quests: QuestView[]): {
2882
2985
  type OAuthProvider = AuthProvider;
2883
2986
  /**
2884
2987
  * The sign-in methods participants can use here: the OAuth providers and email Domino configured
2885
- * for the platform, and the wallet sign-in this project enabled in Console.
2988
+ * for the platform, and the wallet sign-in this project enabled in Console, in `data`. When they
2989
+ * cannot be read, `data` offers email alone and `data.error` says why; the server still has the
2990
+ * final say.
2886
2991
  */
2887
- declare function useSignInMethods(): SignInMethods | null;
2992
+ declare function useSignInMethods(): Query<SignInMethods>;
2888
2993
  /**
2889
- * The signed-in member and the seed of their generated avatar, or null while signed out or
2890
- * loading: `<img src={defaultAvatar(me.avatarSeed)} />`. The seed is derived from the member and
2994
+ * The signed-in member and the seed of their generated avatar, in `data`:
2995
+ * `<img src={defaultAvatar(me.data.avatarSeed)} />`. The seed is derived from the member and
2891
2996
  * reveals nothing about them; linking wallets or accounts does not change it.
2892
2997
  */
2893
- declare function useMe(): {
2894
- member: string;
2895
- avatarSeed: string;
2896
- } | null;
2998
+ declare function useMe(): MemberQuery<MemberSummary>;
2897
2999
  /**
2898
3000
  * Email codes, OAuth consent and wallet signatures always happen in the participant's browser.
2899
3001
  * The actions keep their identity across renders, and never reject: a failure lands in `error`.
2900
3002
  */
2901
3003
  declare function useSignIn(): {
2902
3004
  busy: boolean;
2903
- error: string | null;
3005
+ error: RelayError | null;
2904
3006
  /** True once a code was sent and the form should ask for it. */
2905
3007
  codeSent: boolean;
2906
3008
  sendCode: (email: string) => Promise<void>;
@@ -2937,9 +3039,10 @@ declare function useSignInOptions(input?: {
2937
3039
  options: SignInOption[] | null;
2938
3040
  unavailable: SignInUnavailable[];
2939
3041
  /** Why the project's methods could not be read; the options are then email alone. */
2940
- methodsError: string | null;
3042
+ methodsError: RelayError | null;
2941
3043
  busy: boolean;
2942
- error: string | null;
3044
+ /** Why the last sign-in action failed. */
3045
+ error: RelayError | null;
2943
3046
  /** True once an email code was sent and the form should ask for it. */
2944
3047
  codeSent: boolean;
2945
3048
  /** Forgets the email code sent, to use another address. */
@@ -2947,53 +3050,119 @@ declare function useSignInOptions(input?: {
2947
3050
  };
2948
3051
 
2949
3052
  /** True when the account can be used to verify quests right now. */
2950
- declare function isConnected(accounts: AccountConnection[] | null, provider: AuthProvider): boolean;
3053
+ declare function isConnected(accounts: AccountConnection[] | null | undefined, provider: AuthProvider): boolean;
2951
3054
  /**
2952
- * The signed-in participant's connected accounts (X, Discord, Google, Apple).
3055
+ * The signed-in participant's connected accounts (X, Discord, Google, Apple) in `data`.
2953
3056
  *
2954
3057
  * Connecting leaves the page for the provider's consent screen and comes back
2955
3058
  * connected. The list is read again when the window regains focus and after
2956
- * any disconnect in this tab, so every component using it stays in step.
3059
+ * a disconnect, in every component that uses it. `connect` and `disconnect`
3060
+ * never reject: they resolve whether they succeeded, and a failure lands in
3061
+ * `actionError`.
2957
3062
  */
2958
3063
  declare function useAccounts(): {
2959
- /** Null until read, and while nobody is signed in. */
2960
- accounts: {
3064
+ connect: (provider: AuthProvider) => Promise<boolean>;
3065
+ disconnect: (provider: AuthProvider) => Promise<boolean>;
3066
+ busy: boolean;
3067
+ actionError: RelayError | null;
3068
+ isConnected: (provider: AuthProvider) => boolean;
3069
+ status: "loading";
3070
+ data: undefined;
3071
+ error: null;
3072
+ refresh(): Promise<void>;
3073
+ } | {
3074
+ connect: (provider: AuthProvider) => Promise<boolean>;
3075
+ disconnect: (provider: AuthProvider) => Promise<boolean>;
3076
+ busy: boolean;
3077
+ actionError: RelayError | null;
3078
+ isConnected: (provider: AuthProvider) => boolean;
3079
+ status: "error";
3080
+ data: undefined;
3081
+ error: RelayError;
3082
+ refresh(): Promise<void>;
3083
+ } | {
3084
+ connect: (provider: AuthProvider) => Promise<boolean>;
3085
+ disconnect: (provider: AuthProvider) => Promise<boolean>;
3086
+ busy: boolean;
3087
+ actionError: RelayError | null;
3088
+ isConnected: (provider: AuthProvider) => boolean;
3089
+ status: "signed-out";
3090
+ data: undefined;
3091
+ error: null;
3092
+ refresh(): Promise<void>;
3093
+ } | {
3094
+ connect: (provider: AuthProvider) => Promise<boolean>;
3095
+ disconnect: (provider: AuthProvider) => Promise<boolean>;
3096
+ busy: boolean;
3097
+ actionError: RelayError | null;
3098
+ isConnected: (provider: AuthProvider) => boolean;
3099
+ status: "ready";
3100
+ data: {
2961
3101
  provider: "google" | "apple" | "x" | "discord";
2962
3102
  configured: boolean;
2963
3103
  status: "disconnected" | "connected" | "reconnect";
2964
3104
  subject: string | null;
2965
3105
  revision: number;
2966
- }[] | null;
2967
- error: string | null;
2968
- busy: boolean;
2969
- refresh: () => Promise<void>;
2970
- isConnected: (provider: AuthProvider) => boolean;
2971
- connect: (provider: AuthProvider) => Promise<void>;
2972
- disconnect: (provider: AuthProvider) => Promise<void>;
3106
+ }[];
3107
+ error: RelayError | null;
3108
+ refresh(): Promise<void>;
2973
3109
  };
2974
3110
 
2975
3111
  /**
2976
- * The signed-in participant's linked Solana, EVM and Cosmos wallets.
3112
+ * The signed-in participant's linked Solana, EVM and Cosmos wallets, in `data`.
2977
3113
  *
2978
3114
  * Linking asks the wallet to sign a one-time message. The list is read again
2979
- * when the window regains focus and after any link or unlink in this tab, so
2980
- * every component using it stays in step.
3115
+ * when the window regains focus and after a link or unlink, in every component
3116
+ * that uses it. `link` and `unlink` never reject: they resolve whether they
3117
+ * succeeded, and a failure lands in `actionError`.
2981
3118
  */
2982
3119
  declare function useWallets(): {
2983
- /** Null until read, and while nobody is signed in. */
2984
- wallets: {
3120
+ /** Links the wallet to the member the list was read for; another session is refused. */
3121
+ link: (signer: WalletSigner) => Promise<boolean>;
3122
+ unlink: (wallet: Pick<LinkedWallet, "chain" | "address">) => Promise<boolean>;
3123
+ busy: boolean;
3124
+ actionError: RelayError | null;
3125
+ status: "loading";
3126
+ data: undefined;
3127
+ error: null;
3128
+ refresh(): Promise<void>;
3129
+ } | {
3130
+ /** Links the wallet to the member the list was read for; another session is refused. */
3131
+ link: (signer: WalletSigner) => Promise<boolean>;
3132
+ unlink: (wallet: Pick<LinkedWallet, "chain" | "address">) => Promise<boolean>;
3133
+ busy: boolean;
3134
+ actionError: RelayError | null;
3135
+ status: "error";
3136
+ data: undefined;
3137
+ error: RelayError;
3138
+ refresh(): Promise<void>;
3139
+ } | {
3140
+ /** Links the wallet to the member the list was read for; another session is refused. */
3141
+ link: (signer: WalletSigner) => Promise<boolean>;
3142
+ unlink: (wallet: Pick<LinkedWallet, "chain" | "address">) => Promise<boolean>;
3143
+ busy: boolean;
3144
+ actionError: RelayError | null;
3145
+ status: "signed-out";
3146
+ data: undefined;
3147
+ error: null;
3148
+ refresh(): Promise<void>;
3149
+ } | {
3150
+ /** Links the wallet to the member the list was read for; another session is refused. */
3151
+ link: (signer: WalletSigner) => Promise<boolean>;
3152
+ unlink: (wallet: Pick<LinkedWallet, "chain" | "address">) => Promise<boolean>;
3153
+ busy: boolean;
3154
+ actionError: RelayError | null;
3155
+ status: "ready";
3156
+ data: {
2985
3157
  chain: "solana" | "eip155" | "cosmos";
2986
3158
  network: `cosmos:${string}` | null;
2987
3159
  address: string;
2988
3160
  verifiedAt: number;
2989
3161
  revision: number;
2990
3162
  signIn: boolean;
2991
- }[] | null;
2992
- error: string | null;
2993
- busy: boolean;
2994
- refresh: () => Promise<void>;
2995
- link: (signer: WalletSigner) => Promise<void>;
2996
- unlink: (wallet: Pick<LinkedWallet, "chain" | "address">) => Promise<void>;
3163
+ }[];
3164
+ error: RelayError | null;
3165
+ refresh(): Promise<void>;
2997
3166
  };
2998
3167
  /**
2999
3168
  * The EVM, Solana and Cosmos wallets installed in this browser. Empty during
@@ -3021,19 +3190,54 @@ declare function useSignInWallets(networks: readonly WalletSignInNetwork[] | nul
3021
3190
  cosmos?: CosmosChains;
3022
3191
  }): SignInWallet[] | null;
3023
3192
 
3024
- /** Dispatched on `window` after the member changes their profile in this tab. */
3025
- declare const PROFILE_CHANGED = "relay-profile-changed";
3026
3193
  /**
3027
- * The signed-in member's own profile, and changes to it: the name, the avatar (an uploaded
3028
- * picture, an account's picture or the generated one) and whether others see it.
3194
+ * The signed-in member's own profile in `data`, and changes to it: the name, the avatar (an
3195
+ * uploaded picture, an account's picture or the generated one) and whether others see it.
3029
3196
  *
3030
- * Read again when the window regains focus and after any change in this tab, so every component
3031
- * using it stays in step. Each change is bound to the session it was read with: a session that
3032
- * changed in between is refused rather than applied to another member.
3197
+ * Read again when the window regains focus and after a change, in every component that uses it,
3198
+ * with the leaderboards that show the name. Each change is bound to the session the profile was
3199
+ * read with: a session that changed in between is refused rather than applied to another member.
3200
+ * `update` and `uploadAvatar` never reject: they resolve true once saved, and a failure lands in
3201
+ * `actionError`.
3033
3202
  */
3034
3203
  declare function useMyProfile(): {
3035
- /** Null until read, and while nobody is signed in. */
3036
- profile: {
3204
+ update: (input: ProfileUpdate) => Promise<boolean>;
3205
+ /** Uploads a JPEG, PNG or WebP of at most 5 MB as the avatar. */
3206
+ uploadAvatar: (file: Blob) => Promise<boolean>;
3207
+ busy: boolean;
3208
+ actionError: RelayError | null;
3209
+ status: "loading";
3210
+ data: undefined;
3211
+ error: null;
3212
+ refresh(): Promise<void>;
3213
+ } | {
3214
+ update: (input: ProfileUpdate) => Promise<boolean>;
3215
+ /** Uploads a JPEG, PNG or WebP of at most 5 MB as the avatar. */
3216
+ uploadAvatar: (file: Blob) => Promise<boolean>;
3217
+ busy: boolean;
3218
+ actionError: RelayError | null;
3219
+ status: "error";
3220
+ data: undefined;
3221
+ error: RelayError;
3222
+ refresh(): Promise<void>;
3223
+ } | {
3224
+ update: (input: ProfileUpdate) => Promise<boolean>;
3225
+ /** Uploads a JPEG, PNG or WebP of at most 5 MB as the avatar. */
3226
+ uploadAvatar: (file: Blob) => Promise<boolean>;
3227
+ busy: boolean;
3228
+ actionError: RelayError | null;
3229
+ status: "signed-out";
3230
+ data: undefined;
3231
+ error: null;
3232
+ refresh(): Promise<void>;
3233
+ } | {
3234
+ update: (input: ProfileUpdate) => Promise<boolean>;
3235
+ /** Uploads a JPEG, PNG or WebP of at most 5 MB as the avatar. */
3236
+ uploadAvatar: (file: Blob) => Promise<boolean>;
3237
+ busy: boolean;
3238
+ actionError: RelayError | null;
3239
+ status: "ready";
3240
+ data: {
3037
3241
  id: string;
3038
3242
  name: string;
3039
3243
  avatar: {
@@ -3060,198 +3264,43 @@ declare function useMyProfile(): {
3060
3264
  name: string | null;
3061
3265
  imageUrl: string | null;
3062
3266
  }[];
3063
- } | null;
3064
- error: string | null;
3065
- busy: boolean;
3066
- refresh: () => Promise<void>;
3067
- /** Resolves true once saved; a failure sets `error`. */
3068
- update: (input: ProfileUpdate) => Promise<boolean>;
3069
- /** Uploads a JPEG, PNG or WebP of at most 5 MB as the avatar; resolves true once saved. */
3070
- uploadAvatar: (file: Blob) => Promise<boolean>;
3071
- };
3072
- /**
3073
- * A read keyed by a profile. `missing` is the uniform 404: a profile (or board, or program) that
3074
- * is missing, hidden by its member, or not visible to this viewer, on purpose indistinguishable.
3075
- */
3076
- type ProfileReadState<T> = {
3077
- status: "loading";
3078
- } | {
3079
- status: "error";
3080
- message: string;
3081
- } | {
3082
- status: "missing";
3083
- } | {
3084
- status: "ready";
3085
- value: T;
3086
- stale: string | null;
3087
- };
3088
- type ProfileState = {
3089
- status: "loading";
3090
- } | {
3091
- status: "error";
3092
- message: string;
3093
- }
3094
- /** Missing, hidden by its member, or not visible to this viewer: indistinguishable on purpose. */
3095
- | {
3096
- status: "missing";
3097
- } | {
3098
- status: "ready";
3099
- profile: ProfileSummary;
3100
- stale: string | null;
3267
+ };
3268
+ error: RelayError | null;
3269
+ refresh(): Promise<void>;
3101
3270
  };
3102
3271
  /**
3103
- * A profile's name and avatar by its public ID (`relay.profiles.get`), read again every
3104
- * `intervalMs` (default 60 seconds) while the page is visible and when the participant signs in
3105
- * or out.
3272
+ * A profile's name and avatar by its public ID (`relay.profiles.get`), in `data`. Read again every
3273
+ * `intervalMs` (default 60 seconds) while the page is visible. A profile that is missing, hidden by
3274
+ * its member or not visible to this viewer is an `error` with `status` 404, indistinguishable on
3275
+ * purpose.
3106
3276
  */
3107
3277
  declare function useProfile(id: string, options?: {
3108
3278
  intervalMs?: number;
3109
- }): {
3110
- refresh: () => Promise<void>;
3111
- status: "loading";
3112
- } | {
3113
- refresh: () => Promise<void>;
3114
- status: "error";
3115
- message: string;
3116
- } | {
3117
- refresh: () => Promise<void>;
3118
- status: "missing";
3119
- } | {
3120
- refresh: () => Promise<void>;
3121
- status: "ready";
3122
- profile: ProfileSummary;
3123
- stale: string | null;
3124
- };
3279
+ }): Query<ProfileSummary>;
3125
3280
  /**
3126
- * A profile's current rank and score on one board (`relay.leaderboards.placement`), read again
3127
- * every `intervalMs` (default 30 seconds) while the page is visible. Signed-out visitors get an
3128
- * error for boards without public access.
3281
+ * A profile's current rank and score on one board (`relay.leaderboards.placement`), in `data`.
3282
+ * Read again every `intervalMs` (default 30 seconds) while the page is visible. Signed-out
3283
+ * visitors get an error for boards without public access.
3129
3284
  */
3130
3285
  declare function useLeaderboardPlacement(board: string, profile: string, options?: {
3131
3286
  intervalMs?: number;
3132
- }): {
3133
- refresh: () => Promise<void>;
3134
- status: "loading";
3135
- } | {
3136
- refresh: () => Promise<void>;
3137
- status: "error";
3138
- message: string;
3139
- } | {
3140
- refresh: () => Promise<void>;
3141
- status: "missing";
3142
- } | {
3143
- refresh: () => Promise<void>;
3144
- status: "ready";
3145
- value: {
3146
- leaderboard: string;
3147
- title: string;
3148
- profile: string;
3149
- rank: number | null;
3150
- score: number | null;
3151
- total: number;
3152
- key?: string | undefined;
3153
- };
3154
- stale: string | null;
3155
- };
3287
+ }): Query<LeaderboardPlacement>;
3156
3288
  /**
3157
- * A profile's progress in one referral program (`relay.profiles.referral`), read again every
3158
- * `intervalMs` (default 60 seconds) while the page is visible. Needs a signed-in participant.
3289
+ * A profile's progress in one referral program (`relay.profiles.referral`), in `data`. Read again
3290
+ * every `intervalMs` (default 60 seconds) while the page is visible. Needs a signed-in participant.
3159
3291
  */
3160
3292
  declare function useProfileReferral(profile: string, program: string, options?: {
3161
3293
  intervalMs?: number;
3162
- }): {
3163
- refresh: () => Promise<void>;
3164
- status: "loading";
3165
- } | {
3166
- refresh: () => Promise<void>;
3167
- status: "error";
3168
- message: string;
3169
- } | {
3170
- refresh: () => Promise<void>;
3171
- status: "missing";
3172
- } | {
3173
- refresh: () => Promise<void>;
3174
- status: "ready";
3175
- value: {
3176
- program: string;
3177
- title: string;
3178
- invited: number;
3179
- qualified: number;
3180
- milestones: {
3181
- id: string;
3182
- count: number;
3183
- reached: boolean;
3184
- }[];
3185
- key?: string | undefined;
3186
- };
3187
- stale: string | null;
3188
- };
3189
- type ProfileCompletionsState = {
3190
- status: "loading";
3191
- } | {
3192
- status: "error";
3193
- message: string;
3194
- } | {
3195
- status: "missing";
3196
- } | {
3197
- status: "ready";
3198
- items: ProfileCompletion[];
3199
- /** More completions to load with `loadMore()`. */
3200
- more: boolean;
3201
- /** Loading the next page. */
3202
- loading: boolean;
3203
- /** Why the last `loadMore()` failed, until the next succeeds. */
3204
- error: string | null;
3205
- };
3294
+ }): MemberQuery<ProfileReferral>;
3206
3295
  /**
3207
3296
  * A profile's completions of quests that show on profiles (`relay.profiles.completions`), most
3208
- * recently completed first, `limit` quests at a time. `loadMore()` appends the next page;
3209
- * `refresh()` starts again from the first, as signing in or out and a profile change in this tab
3210
- * do.
3297
+ * recently completed first, `limit` quests at a time: `data.items`, and `data.nextCursor` while
3298
+ * there are more. `loadMore()` appends the next page; `refresh()` starts again from the first, as a
3299
+ * quest submission or a profile change does.
3211
3300
  */
3212
3301
  declare function useProfileCompletions(profile: string, options?: {
3213
3302
  limit?: number;
3214
- }): {
3215
- refresh: () => Promise<void>;
3216
- /**
3217
- * Appends the next page; does nothing while one loads, while the first page reloads, or when
3218
- * there is none.
3219
- */
3220
- loadMore: () => Promise<void>;
3221
- status: "loading";
3222
- } | {
3223
- refresh: () => Promise<void>;
3224
- /**
3225
- * Appends the next page; does nothing while one loads, while the first page reloads, or when
3226
- * there is none.
3227
- */
3228
- loadMore: () => Promise<void>;
3229
- status: "error";
3230
- message: string;
3231
- } | {
3232
- refresh: () => Promise<void>;
3233
- /**
3234
- * Appends the next page; does nothing while one loads, while the first page reloads, or when
3235
- * there is none.
3236
- */
3237
- loadMore: () => Promise<void>;
3238
- status: "missing";
3239
- } | {
3240
- refresh: () => Promise<void>;
3241
- /**
3242
- * Appends the next page; does nothing while one loads, while the first page reloads, or when
3243
- * there is none.
3244
- */
3245
- loadMore: () => Promise<void>;
3246
- status: "ready";
3247
- items: ProfileCompletion[];
3248
- /** More completions to load with `loadMore()`. */
3249
- more: boolean;
3250
- /** Loading the next page. */
3251
- loading: boolean;
3252
- /** Why the last `loadMore()` failed, until the next succeeds. */
3253
- error: string | null;
3254
- };
3303
+ }): PagedQuery<ProfileCompletion>;
3255
3304
 
3256
3305
  type ReferralSummary = z.infer<typeof referralSummarySchema>;
3257
3306
  /**
@@ -3271,14 +3320,18 @@ type Invitation = {
3271
3320
  } | {
3272
3321
  status: "accepted";
3273
3322
  code: string;
3274
- } | {
3323
+ }
3324
+ /** Domino refused the code, or it is malformed: `error` says why. Never retry it. */
3325
+ | {
3275
3326
  status: "rejected";
3276
3327
  code: string;
3277
- reason: string;
3278
- } | {
3328
+ error: RelayError;
3329
+ }
3330
+ /** Offline, a server error or a cut-off answer: `error` says why. Retry with `accept(code)`. */
3331
+ | {
3279
3332
  status: "failed";
3280
3333
  code: string;
3281
- reason: string;
3334
+ error: RelayError;
3282
3335
  };
3283
3336
  /**
3284
3337
  * Accepts the invitation the participant arrived with, as soon as they are
@@ -3286,334 +3339,67 @@ type Invitation = {
3286
3339
  *
3287
3340
  * Domino rejects self-referrals, cycles, codes from another project, and
3288
3341
  * invitations accepted after the participant's first completion. Show the
3289
- * reason; `accept(code)` also serves a manual "Have a code?" input.
3342
+ * reason; `accept(code)` also serves a manual "Have a code?" input. Once
3343
+ * accepted, the referral summary and referral progress are read again.
3290
3344
  */
3291
3345
  declare function useInvitation(param?: string): {
3292
3346
  invitation: Invitation;
3293
3347
  accept: (code: string) => Promise<void>;
3294
3348
  };
3295
3349
  /**
3296
- * The signed-in participant's invitation code, share link, and progress:
3297
- * attributed friends, qualified friends per program, milestones, and points.
3350
+ * The signed-in participant's invitation code and progress in `data`: attributed friends,
3351
+ * qualified friends per program, milestones, and points. `link` is the share link, null until the
3352
+ * summary is read. Read again after an invitation is accepted and after a quest submission.
3298
3353
  */
3299
3354
  declare function useReferralSummary(options?: {
3300
3355
  path?: string;
3301
3356
  param?: string;
3302
- }): {
3303
- summary: {
3304
- code: string;
3305
- attributed: number;
3306
- programs: {
3307
- definition: {
3308
- id: string;
3309
- title: string;
3310
- enabled: boolean;
3311
- qualification: {
3312
- kind: "signup";
3313
- } | {
3314
- kind: "quest";
3315
- quest: string;
3316
- } | {
3317
- kind: "external";
3318
- };
3319
- rewards: {
3320
- inviter: ({
3321
- kind: "tier";
3322
- reward: {
3323
- id: string;
3324
- title: string;
3325
- tiers: {
3326
- id: string;
3327
- title: string;
3328
- }[];
3329
- inventory: {
3330
- kind: "untracked";
3331
- };
3332
- fulfillment: {
3333
- kind: "staff-handover";
3334
- };
3335
- };
3336
- tier: string;
3337
- } | {
3338
- kind: "points";
3339
- balance: string;
3340
- amount: number;
3341
- })[];
3342
- invitee: ({
3343
- kind: "tier";
3344
- reward: {
3345
- id: string;
3346
- title: string;
3347
- tiers: {
3348
- id: string;
3349
- title: string;
3350
- }[];
3351
- inventory: {
3352
- kind: "untracked";
3353
- };
3354
- fulfillment: {
3355
- kind: "staff-handover";
3356
- };
3357
- };
3358
- tier: string;
3359
- } | {
3360
- kind: "points";
3361
- balance: string;
3362
- amount: number;
3363
- })[];
3364
- };
3365
- milestones: {
3366
- id: string;
3367
- count: number;
3368
- rewards: ({
3369
- kind: "tier";
3370
- reward: {
3371
- id: string;
3372
- title: string;
3373
- tiers: {
3374
- id: string;
3375
- title: string;
3376
- }[];
3377
- inventory: {
3378
- kind: "untracked";
3379
- };
3380
- fulfillment: {
3381
- kind: "staff-handover";
3382
- };
3383
- };
3384
- tier: string;
3385
- } | {
3386
- kind: "points";
3387
- balance: string;
3388
- amount: number;
3389
- })[];
3390
- }[];
3391
- bonuses: {
3392
- id: string;
3393
- balance: string;
3394
- basisPoints: number;
3395
- external: boolean;
3396
- quests?: string[] | undefined;
3397
- durationDays?: number | undefined;
3398
- maxPoints?: number | undefined;
3399
- }[];
3400
- key?: string | undefined;
3401
- inviterGroup?: string | undefined;
3402
- };
3403
- qualified: number;
3404
- milestones: {
3405
- id: string;
3406
- count: number;
3407
- reached: boolean;
3408
- }[];
3409
- }[];
3410
- accounts: {
3411
- id: string;
3412
- program: string;
3413
- kind: "fixed" | "milestone" | "bonus";
3414
- balance: string;
3415
- earned: number;
3416
- settled: number;
3417
- outstanding: number;
3418
- }[];
3419
- } | null;
3357
+ }): MemberQuery<ReferralSummary> & {
3420
3358
  link: string | null;
3421
- error: string | null;
3422
- refresh: () => Promise<void>;
3423
3359
  };
3424
3360
 
3425
- type LeaderboardState = {
3426
- status: "loading";
3427
- } | {
3428
- status: "error";
3429
- message: string;
3430
- } | {
3431
- status: "ready";
3432
- page: LeaderboardPage;
3433
- stale: string | null;
3434
- };
3435
3361
  /**
3436
- * One leaderboard's top rows plus the participant's own row and neighbours.
3362
+ * One leaderboard's top rows plus the participant's own row and neighbours, in `data`.
3437
3363
  *
3438
- * Read again every `intervalMs` while the page is visible, and when the
3439
- * participant signs in or out. Boards default to participants-only access:
3440
- * signed-out visitors see an error unless the board is published as public.
3364
+ * Read again every `intervalMs` while the page is visible, after a quest submission, and for each
3365
+ * participant who signs in. Boards default to participants-only access: signed-out visitors get
3366
+ * an error unless the board is published as public.
3441
3367
  */
3442
3368
  declare function useLeaderboard(board: string, options?: {
3443
3369
  limit?: number;
3444
3370
  intervalMs?: number;
3445
- }): {
3446
- refresh: () => Promise<void>;
3447
- status: "loading";
3448
- } | {
3449
- refresh: () => Promise<void>;
3450
- status: "error";
3451
- message: string;
3452
- } | {
3453
- refresh: () => Promise<void>;
3454
- status: "ready";
3455
- page: LeaderboardPage;
3456
- stale: string | null;
3457
- };
3371
+ }): Query<LeaderboardPage>;
3458
3372
 
3459
- type BalancesState = {
3460
- status: "loading";
3461
- } | {
3462
- status: "signed-out";
3463
- } | {
3464
- status: "error";
3465
- message: string;
3466
- } | {
3467
- status: "ready";
3468
- items: PointBalance[];
3469
- stale: string | null;
3470
- };
3471
- type PointHistoryState = {
3472
- status: "loading";
3473
- } | {
3474
- status: "signed-out";
3475
- }
3476
- /** No ledger has this key or ID in the project (`point-ledger-not-found`). */
3477
- | {
3478
- status: "missing";
3479
- }
3480
- /** The member has not joined this project, so it has no points here (`not-joined`). */
3481
- | {
3482
- status: "not-joined";
3483
- } | {
3484
- status: "error";
3485
- message: string;
3486
- } | {
3487
- status: "ready";
3488
- items: PointEntry[];
3489
- /** Whether `loadMore()` has another page to read. */
3490
- more: boolean;
3491
- /** A next page is loading. */
3492
- loading: boolean;
3493
- /** Why the last next page failed; the items read so far stay. */
3494
- error: string | null;
3495
- };
3496
3373
  /**
3497
- * The signed-in participant's balance in each of the project's point ledgers: `balance` counts
3498
- * every entry, `available` leaves out points reservations hold. Read again when the participant
3499
- * signs in or out and after a quest completes. Find a ledger by the key your app declared:
3500
- * `items.find((b) => b.ledger.key === "points")`.
3374
+ * The signed-in participant's balance in each of the project's point ledgers, as `data.items`:
3375
+ * `balance` counts every entry, `available` leaves out points reservations hold. Read again
3376
+ * whenever one of the participant's quests completes. Find a ledger by the key your app declared:
3377
+ * `data.items.find((b) => b.ledger.key === "points")`.
3501
3378
  */
3502
- declare function useBalances(): {
3503
- refresh: () => Promise<void>;
3504
- status: "loading";
3505
- } | {
3506
- refresh: () => Promise<void>;
3507
- status: "signed-out";
3508
- } | {
3509
- refresh: () => Promise<void>;
3510
- status: "error";
3511
- message: string;
3512
- } | {
3513
- refresh: () => Promise<void>;
3514
- status: "ready";
3515
- items: PointBalance[];
3516
- stale: string | null;
3517
- };
3379
+ declare function useBalances(): MemberQuery<PointBalances>;
3518
3380
  /**
3519
- * The signed-in participant's own entries in one ledger, by key or ID, most recently recorded first, a page at a
3520
- * time. Starts over when the participant signs in or out and after a quest completes.
3381
+ * The signed-in participant's own entries in one ledger, by key or ID, most recently recorded
3382
+ * first, a page at a time: `data.items`, and `loadMore()` while `data.nextCursor` is set. Starts
3383
+ * over whenever one of the participant's quests completes. An unknown ledger is an `error` with
3384
+ * code `point-ledger-not-found`; a member who has not joined the project, `not-joined`.
3521
3385
  */
3522
- declare function usePointHistory(ledger: string): {
3523
- refresh: () => Promise<void>;
3524
- /** Appends the next page; does nothing while a page loads or when there is none. */
3525
- loadMore: () => Promise<void>;
3526
- status: "loading";
3527
- } | {
3528
- refresh: () => Promise<void>;
3529
- /** Appends the next page; does nothing while a page loads or when there is none. */
3530
- loadMore: () => Promise<void>;
3531
- status: "signed-out";
3532
- } | {
3533
- refresh: () => Promise<void>;
3534
- /** Appends the next page; does nothing while a page loads or when there is none. */
3535
- loadMore: () => Promise<void>;
3536
- status: "missing";
3537
- } | {
3538
- refresh: () => Promise<void>;
3539
- /** Appends the next page; does nothing while a page loads or when there is none. */
3540
- loadMore: () => Promise<void>;
3541
- status: "not-joined";
3542
- } | {
3543
- refresh: () => Promise<void>;
3544
- /** Appends the next page; does nothing while a page loads or when there is none. */
3545
- loadMore: () => Promise<void>;
3546
- status: "error";
3547
- message: string;
3548
- } | {
3549
- refresh: () => Promise<void>;
3550
- /** Appends the next page; does nothing while a page loads or when there is none. */
3551
- loadMore: () => Promise<void>;
3552
- status: "ready";
3553
- items: PointEntry[];
3554
- /** Whether `loadMore()` has another page to read. */
3555
- more: boolean;
3556
- /** A next page is loading. */
3557
- loading: boolean;
3558
- /** Why the last next page failed; the items read so far stay. */
3559
- error: string | null;
3560
- };
3386
+ declare function usePointHistory(ledger: string): MemberPagedQuery<PointEntry>;
3561
3387
 
3562
- type ReferencesState<T> = {
3563
- status: "loading";
3564
- } | {
3565
- status: "error";
3566
- message: string;
3567
- } | {
3568
- status: "ready";
3569
- items: T[];
3570
- };
3571
- type PointLedgersState = ReferencesState<PointLedgerReference>;
3572
- type TieredRewardsState = ReferencesState<TieredRewardReference>;
3573
3388
  /**
3574
- * The project's point ledgers with their keys and names.
3389
+ * The project's point ledgers with their keys and names, in `data`.
3575
3390
  *
3576
3391
  * Reads such as referral summaries and history name ledgers by ID. Find a
3577
3392
  * ledger by the key your app declared, then compare IDs:
3578
- * `items.find((l) => l.key === "credits")?.id`.
3393
+ * `data.find((l) => l.key === "credits")?.id`.
3579
3394
  */
3580
- declare function usePointLedgers(): {
3581
- refresh: () => Promise<void>;
3582
- status: "loading";
3583
- } | {
3584
- refresh: () => Promise<void>;
3585
- status: "error";
3586
- message: string;
3587
- } | {
3588
- refresh: () => Promise<void>;
3589
- status: "ready";
3590
- items: {
3591
- id: string;
3592
- key: string | null;
3593
- name: string;
3594
- }[];
3595
- };
3395
+ declare function usePointLedgers(): Query<PointLedgerReference[]>;
3596
3396
  /**
3597
- * The project's tiered rewards with their keys.
3397
+ * The project's tiered rewards with their keys, in `data`.
3598
3398
  *
3599
3399
  * Entitlements and reward definitions name tiered rewards by ID. Find a reward
3600
3400
  * by the key you gave defineTieredReward, then compare IDs:
3601
- * `items.find((r) => r.key === "tote")?.id`.
3401
+ * `data.find((r) => r.key === "tote")?.id`.
3602
3402
  */
3603
- declare function useTieredRewards(): {
3604
- refresh: () => Promise<void>;
3605
- status: "loading";
3606
- } | {
3607
- refresh: () => Promise<void>;
3608
- status: "error";
3609
- message: string;
3610
- } | {
3611
- refresh: () => Promise<void>;
3612
- status: "ready";
3613
- items: {
3614
- id: string;
3615
- key: string | null;
3616
- }[];
3617
- };
3403
+ declare function useTieredRewards(): Query<TieredRewardReference[]>;
3618
3404
 
3619
- export { type BalancesState, type Collection, CosmosChains, DiscoveredWallet, type Experience, type Invitation, type LeaderboardState, type OAuthProvider, PENDING_POLL_MS, PROFILE_CHANGED, type PointHistoryState, type PointLedgersState, type ProfileCompletionsState, type ProfileReadState, type ProfileState, type QuestInput, type QuestNotice, type QuestStatus, type QuestTone, RETRY_DELAYS_MS, type ReferencesState, type ReferralSummary, type RelayClient, RelayProvider, type RoleDelivery, type SavedSubmission, SignInMethods, SignInOption, SignInUnavailable, SignInWallet, type TieredRewardsState, canRepeat, canSubmit, captureReferralCode, completionCountLabel, completionMark, hasPending, isAutomatic, isConnected, isFinished, progressOf, questGroups, questStatus, roleDeliveryLabel, runsSilently, sanitizeDeliveries, useAccounts, useBalances, useExperience, useInvitation, useLeaderboard, useLeaderboardPlacement, useMe, useMyProfile, usePointHistory, usePointLedgers, useProfile, useProfileCompletions, useProfileReferral, useQuest, useReferralSummary, useRelay, useSignIn, useSignInMethods, useSignInOptions, useSignInWallets, useSignOut, useTieredRewards, useWalletDiscovery, useWallets, visibleQuests, watchRoleDeliveries };
3405
+ export { type Collection, CosmosChains, DiscoveredWallet, type ExperienceData, type Invitation, type KnownQuestStatusCode, type KnownRoleDeliveryStatus, type MemberPagedQuery, type MemberQuery, type MemberSnapshot, type OAuthProvider, Page, type PageLoader, type PageRead, type PagedQuery, type Query, type QueryContext, type QueryKey, type QueryOptions, type QueryRead, type QuerySnapshot, type QuestInput, type QuestNotice, type QuestStatus, type QuestStatusCode, type QuestStatusText, type QuestTone, type ReferralSummary, type RelayClient, RelayProvider, type RoleDelivery, type RoleDeliveryStatus, type SavedSubmission, SignInMethods, SignInOption, SignInUnavailable, SignInWallet, canRepeat, canSubmit, captureReferralCode, completionCountLabel, isAutomatic, isConnected, isFinished, progressOf, questGroups, questStatus, questStatusText, relayKeys, roleDeliveryLabel, roleDeliveryStatus, useAccounts, useBalances, useExperience, useInvalidate, useInvitation, useLeaderboard, useLeaderboardPlacement, useMe, useMemberPages, useMemberQuery, useMyProfile, usePointHistory, usePointLedgers, useProfile, useProfileCompletions, useProfileReferral, useQuest, useReferralSummary, useRelay, useRelayPages, useRelayQuery, useSession, useSignIn, useSignInMethods, useSignInOptions, useSignInWallets, useSignOut, useTieredRewards, useWalletDiscovery, useWallets };