@domino-sdk/relay 1.61.0 → 1.62.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.
Files changed (80) hide show
  1. package/CHANGELOG.md +156 -8
  2. package/README.md +3 -2
  3. package/dist/{schema-CZ6Rj0ZF.d.ts → accounts-CCqCKCVx.d.ts} +3127 -1764
  4. package/dist/action-9Xl0StBg.d.ts +33 -0
  5. package/dist/{integrations-B3y36OaB.d.ts → analytics-BwAKLi7O.d.ts} +347 -57
  6. package/dist/{attempt-captures-DYnJVkfg.d.ts → attempt-captures-Cm-5G0PC.d.ts} +1 -1
  7. package/dist/authoring.d.ts +130 -42
  8. package/dist/authoring.js +112 -37
  9. package/dist/{automatic-outcomes-If88LR9f.d.ts → automatic-outcomes-pAsOyIVp.d.ts} +5 -1
  10. package/dist/avatar.d.ts +1 -1
  11. package/dist/browser.d.ts +15 -11
  12. package/dist/browser.js +15 -11
  13. package/dist/{announcements-CHx3-HBw.d.ts → capability-error-DFMwSuSf.d.ts} +161 -4
  14. package/dist/{pages-K7-KjSXw.d.ts → catalog--ZIlskAo.d.ts} +39 -561
  15. package/dist/{chunk-DYXDZFKA.js → chunk-3KLC4AMA.js} +37 -11
  16. package/dist/{chunk-XBV2CK2H.js → chunk-3PYYGFSB.js} +14 -2
  17. package/dist/{chunk-RHBS5FJL.js → chunk-5TEYFX6X.js} +2 -2
  18. package/dist/chunk-6WCIWXUV.js +31 -0
  19. package/dist/chunk-CICDIBOF.js +373 -0
  20. package/dist/{chunk-MHPKEVEW.js → chunk-CZFXGGEK.js} +5 -0
  21. package/dist/chunk-D26YNACF.js +320 -0
  22. package/dist/{chunk-5I54XBK2.js → chunk-E6IKQHTX.js} +2 -2
  23. package/dist/chunk-FWLQ5XCS.js +21 -0
  24. package/dist/{chunk-SKGTNTLF.js → chunk-GKIFABGN.js} +150 -61
  25. package/dist/chunk-GTDW453U.js +522 -0
  26. package/dist/{chunk-VURRFVNP.js → chunk-HTZHBTIL.js} +69 -27
  27. package/dist/chunk-K2KS3JLY.js +188 -0
  28. package/dist/{chunk-ZDJLPN4L.js → chunk-MK425RXJ.js} +3143 -3209
  29. package/dist/{chunk-L77O64I4.js → chunk-QYPMPWVX.js} +1 -1
  30. package/dist/chunk-UAKYDVT3.js +565 -0
  31. package/dist/{chunk-DHXDBAJF.js → chunk-UPQPUJOU.js} +36 -123
  32. package/dist/{chunk-24I4R732.js → chunk-UW5232GG.js} +34 -19
  33. package/dist/chunk-WSBYWVTZ.js +717 -0
  34. package/dist/{chunk-PPB7HALD.js → chunk-XJFYAXET.js} +9 -8
  35. package/dist/{chunk-7A4J67ZL.js → chunk-XNKSSE32.js} +1 -1
  36. package/dist/{chunk-QEAS4ZGS.js → chunk-Y322OTCH.js} +1 -1
  37. package/dist/{client-C51w7fMb.d.ts → client-B-hbHX30.d.ts} +109 -40
  38. package/dist/{deployments-kWoHFaqj.d.ts → deployments-M4OHn7G3.d.ts} +407 -18
  39. package/dist/identity-integration-3mX6b5iH.d.ts +1241 -0
  40. package/dist/index.d.ts +13 -11
  41. package/dist/index.js +14 -11
  42. package/dist/internal.d.ts +14 -12
  43. package/dist/internal.js +26 -14
  44. package/dist/{offers-BoV4ryUc.d.ts → offers-DhAtss4_.d.ts} +21 -4
  45. package/dist/pages-B8q8Y_dQ.d.ts +715 -0
  46. package/dist/{participant-activity-DHL8QYks.d.ts → participant-activity-D-Drp24G.d.ts} +16 -3
  47. package/dist/portal-proxy.d.ts +59 -2
  48. package/dist/portal-proxy.js +109 -23
  49. package/dist/react/index.d.ts +206 -26
  50. package/dist/react/index.js +153 -25
  51. package/dist/{action-DBzILFx9.d.ts → request-error-D_dUPLwo.d.ts} +1 -31
  52. package/dist/runtime/authoring.js +1 -1
  53. package/dist/runtime/chunks/Y4OAVXFR.js +1 -0
  54. package/dist/runtime/manifest.json +3 -3
  55. package/dist/runtime/schema.js +1 -1
  56. package/dist/schema.d.ts +154 -30
  57. package/dist/schema.js +126 -75
  58. package/dist/server/index.d.ts +14 -12
  59. package/dist/server/index.js +2137 -1633
  60. package/dist/sign-in-providers-BkjcDsFF.d.ts +39 -0
  61. package/dist/submission-input-DHGKhXfp.d.ts +342 -0
  62. package/dist/{telegram-browser-BUceZIGZ.d.ts → telegram-browser-BCE7zdOb.d.ts} +46 -6
  63. package/dist/telegram.d.ts +64 -0
  64. package/dist/telegram.js +23 -0
  65. package/dist/testing.d.ts +112 -15
  66. package/dist/testing.js +321 -16
  67. package/dist/tracker-data-Hb1uQnk5.d.ts +1855 -0
  68. package/dist/{transport-BrX9pIGN.d.ts → transport-DkhX2Cbs.d.ts} +5764 -2217
  69. package/dist/{wallet-CIqFoxdm.d.ts → wallet-DTO7SXj7.d.ts} +20 -7
  70. package/package.json +5 -1
  71. package/dist/accounts-DoabriBX.d.ts +0 -306
  72. package/dist/capability-error-DufwG3G0.d.ts +0 -159
  73. package/dist/chunk-3Y3L2F54.js +0 -982
  74. package/dist/chunk-EN3M5Z4J.js +0 -13
  75. package/dist/chunk-QNUHWK7R.js +0 -602
  76. package/dist/chunk-SE4PFJPT.js +0 -247
  77. package/dist/groups-CqcA2Ycs.d.ts +0 -2827
  78. package/dist/runtime/chunks/5QVFB4TU.js +0 -1
  79. package/dist/submission-input-C54Q8yAD.d.ts +0 -146
  80. package/dist/x-D1PGi8Sq.d.ts +0 -331
package/dist/testing.d.ts CHANGED
@@ -1,11 +1,18 @@
1
- import { ar as memberIdentitySchema, as as pointBalanceSchema, at as trackerQuerySchema, au as trackerPageSchema, av as trackerSummarySchema, aw as TrackerContentQuery, ax as trackerContentSchema, ay as photoResultSchema, a7 as TextReviewInput, R as Reward, a9 as Decision, az as TrackerAttribution, U as Tracker, V as TrackerData, ae as ContentBody, a5 as TrackerTriggerContext, ak as TrackerPage } from './schema-CZ6Rj0ZF.js';
1
+ import { d3 as pointBalanceSchema, bD as discordMemberSchema, bG as discordMessageSchema, cZ as photoResultSchema, a8 as TextReviewInput, H as Reward, ac as Decision, P as PointBalance, y as PointLedgerReference, u as PointEntry, o as LeaderboardPage } from './accounts-CCqCKCVx.js';
2
2
  import * as z from 'zod';
3
- import { m as discordMemberSchema, n as discordMessageSchema, t as telegramChatMemberSchema } from './accounts-DoabriBX.js';
4
- import { b as CapabilityFailure, m as memberCompletionSchema } from './capability-error-DufwG3G0.js';
5
- export { C as CapabilityError } from './capability-error-DufwG3G0.js';
6
- import { x as xConnectionSchema, a as xProfileSchema, b as xPostSchema, c as ImageReference, i as imageInspectionSchema, d as imageComparisonSchema, e as imageSightingsSchema } from './x-D1PGi8Sq.js';
7
- import { aj as profileReferralSchema, ak as linkedWalletSchema, al as walletBalanceSchema, am as walletNftsSchema, K as WalletCallArgument, an as walletCallSchema, ao as walletDelegationsSchema, T as WalletEventCondition, ap as walletEventsSchema, aq as walletTransactionsSchema } from './wallet-CIqFoxdm.js';
8
- export { A as AttemptCapture, C as CapturedFact } from './attempt-captures-DYnJVkfg.js';
3
+ import { k as CapabilityFailure, _ as memberCompletionSchema } from './capability-error-DFMwSuSf.js';
4
+ export { C as CapabilityError } from './capability-error-DFMwSuSf.js';
5
+ import { z as xConnectionSchema, C as xProfileSchema, B as xPostSchema, f as ImageReference, s as imageInspectionSchema, o as imageComparisonSchema, v as imageSightingsSchema } from './analytics-BwAKLi7O.js';
6
+ import { aI as memberIdentitySchema, bc as trackerQuerySchema, b7 as trackerPageSchema, bl as trackerSummarySchema, a8 as TrackerContentQuery, aU as trackerContentSchema, a4 as TrackerAttribution, l as Tracker, m as TrackerData, C as ContentBody, v as TrackerTriggerContext, H as TrackerPage } from './tracker-data-Hb1uQnk5.js';
7
+ import { aO as profileReferralSchema, aE as linkedWalletSchema, aY as walletBalanceSchema, b2 as walletNftsSchema, M as WalletCallArgument, aZ as walletCallSchema, a$ as walletDelegationsSchema, U as WalletEventCondition, b0 as walletEventsSchema, b7 as walletTransactionsSchema } from './wallet-DTO7SXj7.js';
8
+ import { C as telegramConnectionSchema, y as telegramChatMemberSchema } from './submission-input-DHGKhXfp.js';
9
+ import { EvaluationMember } from './authoring.js';
10
+ export { A as AttemptCapture, C as CapturedFact } from './attempt-captures-Cm-5G0PC.js';
11
+ import { b as ParticipantSession, R as RelayEvent } from './participant-activity-D-Drp24G.js';
12
+ import { B as Badge } from './offers-DhAtss4_.js';
13
+ import { b as QuestView, q as questExperienceSchema } from './catalog--ZIlskAo.js';
14
+ import './streaks-0YuipsUQ.js';
15
+ import './units-CwgsPB_r.js';
9
16
 
10
17
  /** One project time-zone change: from `effectiveAt` on, days are counted in `timeZone`. */
11
18
  type CalendarSegment = {
@@ -257,7 +264,7 @@ type WalletDelegationsQueryRequest = {
257
264
  };
258
265
  /**
259
266
  * An event query as Relay receives it: `afterActivation` false, and `where` and `address` null,
260
- * when the quest leaves them out.
267
+ * when the quest leaves them out; `newest` only when the quest sets it.
261
268
  */
262
269
  type WalletEventsQueryRequest = {
263
270
  event: string;
@@ -266,6 +273,7 @@ type WalletEventsQueryRequest = {
266
273
  afterActivation: boolean;
267
274
  where: Record<string, WalletEventCondition> | null;
268
275
  address: string | null;
276
+ newest?: number;
269
277
  };
270
278
  /**
271
279
  * A transaction query as Relay receives it: `afterActivation` false, and `mint` and `address`
@@ -301,7 +309,7 @@ type TrackerFacts = {
301
309
  * Reads of the member's own accounts default to a member who has linked nothing:
302
310
  * `member.identities()`, `wallet.list()`, `wallet.balance()`, `wallet.nfts()`, `wallet.call()`,
303
311
  * `wallet.delegations()`, `x.connection()`, `x.profile()`,
304
- * `x.follows()`, `x.reposted()`, `discord.member()` and `telegram.member()` resolve to null. Once the test links an
312
+ * `x.follows()`, `x.reposted()`, `discord.member()`, `telegram.connection()` and `telegram.member()` resolve to null. Once the test links an
305
313
  * account (`wallet.list` or `x.connection` set to a value), supply every read of it the quest
306
314
  * makes. Every other read (the member's balances, completions, groups, campaigns and referrals, an
307
315
  * X post, a Discord message, trackers, images and AI review) needs an answer: a call without one
@@ -373,8 +381,9 @@ type TestFacts = {
373
381
  ], Result<typeof walletDelegationsSchema> | null>;
374
382
  /**
375
383
  * `ctx.wallet.events({ event, account, since })`. Like `balance`, it reads null while the
376
- * member has no linked EVM wallet. The kit does not apply `where` or `since`: answer with the
377
- * logs Relay would return. A run's `calls` does not list event reads.
384
+ * member has no linked EVM wallet. The kit does not apply `where`, `since` or `newest`: answer
385
+ * with the logs Relay would return, and `complete: false` for a `newest` read that left older
386
+ * events out. A run's `calls` does not list event reads.
378
387
  */
379
388
  events?: FakeAnswer<[
380
389
  query: WalletEventsQueryRequest
@@ -414,11 +423,14 @@ type TestFacts = {
414
423
  ], Result<typeof discordMessageSchema> | null>;
415
424
  };
416
425
  /**
417
- * `ctx.telegram.member({ chat })`, which the testing kit supplies to every quest as Relay does.
418
- * Throw `new CapabilityError("integration-required", …)` to act out a chat the project has not
419
- * connected. A run's `calls` does not list Telegram calls: its request type is closed in SDK 1.x.
426
+ * `ctx.telegram.connection()` and `ctx.telegram.member({ chat })`, which the testing kit supplies
427
+ * to every quest as Relay does. `connection` is answered once per run, as Relay pins the account.
428
+ * Throw `new CapabilityError("integration-required", …)` from `member` to act out a chat the
429
+ * project has not connected. A run's `calls` does not list Telegram calls: its request type is
430
+ * closed in SDK 1.x.
420
431
  */
421
432
  telegram?: {
433
+ connection?: FakeAnswer<[], Result<typeof telegramConnectionSchema> | null>;
422
434
  member?: FakeAnswer<[
423
435
  input: {
424
436
  chat: string;
@@ -658,6 +670,16 @@ declare function createTestContext<Q extends TestableQuest>(quest: Q, options?:
658
670
  */
659
671
  declare function runQuest<Q extends TestableQuest>(quest: Q, options?: RunQuestOptions<Q>): Promise<DiagnosedRun>;
660
672
 
673
+ /**
674
+ * An `EvaluationMember` for a test that builds its own evaluation context, such as one that calls
675
+ * `quest.evaluate` with readers of its own. Pass the methods the code under test reads; the rest
676
+ * answer for a member with no linked accounts, completions, groups or enrollments, and `balance`
677
+ * and `referrals` fail as Relay does for an unknown ledger or program. A minor release can give
678
+ * `EvaluationMember` a method; this helper gives it a default, so a test that uses it keeps
679
+ * compiling. `createTestContext` and `runQuest` create the member themselves.
680
+ */
681
+ declare function fakeEvaluationMember(member?: Partial<EvaluationMember>): EvaluationMember;
682
+
661
683
  type Kind = Decision["kind"];
662
684
  type DecisionOf<K extends Kind> = Extract<Decision, {
663
685
  kind: K;
@@ -803,4 +825,79 @@ type AttemptReplay = {
803
825
  */
804
826
  declare function replayAttempt<Q extends TestableQuest>(quest: Q, capture: unknown, options?: ReplayOptions): Promise<AttemptReplay>;
805
827
 
806
- export { type AttemptReplay, type CalendarSegment, type CapabilityCall, type CapabilityOutcome, type CapabilityRequest, Decision, DecisionAssertionError, EvaluatorError, type FakeAnswer, type QuestDiagnostic, type QuestHostContext, type QuestInputOf, type QuestRun, type ReplayDivergence, type ReplayOptions, type RunQuestOptions, type TestContext, type TestFacts, type TestRelease, TestSetupError, type TestableQuest, type TrackerFacts, type TrackerObservationInput, type TrackerReadRequest, type TrackerRecordInput, type WalletBalanceRequest, createTestContext, defaultMember, defaultNow, defaultSeed, expectAccept, expectDefer, expectProgress, expectReject, expectReview, maxCapabilityCalls, replayAttempt, runQuest, trackerObservation, trackerPage };
828
+ type Experience = z.output<typeof questExperienceSchema>;
829
+ /**
830
+ * What `createFakeRelay` answers with. Every seed is optional; an omitted one answers empty.
831
+ *
832
+ * @experimental A minor release can change it, like `createFakeRelay`.
833
+ */
834
+ type FakeRelayOptions = {
835
+ /** The signed-in member's ID, or null (the default) for a signed-out visitor. */
836
+ member?: string | null;
837
+ /** The session's project scope. Defaults to `org_test`, `project_test` and `test`. */
838
+ scope?: Pick<ParticipantSession, "organization" | "project" | "environment">;
839
+ /** The member's quests, as `GET /v1/me/experience` lists them. */
840
+ quests?: QuestView[];
841
+ /** The experience's collections. */
842
+ collections?: Experience["collections"];
843
+ /** The experience's entitlements. */
844
+ entitlements?: Experience["entitlements"];
845
+ /**
846
+ * The member's balance in each ledger (`GET /v1/me/points`). `GET /v1/point-ledgers` lists these
847
+ * ledgers unless `ledgers` is given.
848
+ */
849
+ balances?: PointBalance[];
850
+ /** The project's ledgers (`GET /v1/point-ledgers`). */
851
+ ledgers?: PointLedgerReference[];
852
+ /** Each ledger's history (`GET /v1/me/points/:ledger/history`), keyed by ledger key or ID. */
853
+ pointHistory?: Record<string, PointEntry[]>;
854
+ /** The member's activity feed (`GET /v1/me/activity`), oldest event first. */
855
+ activity?: RelayEvent[];
856
+ /** The project's active badge catalog (`GET /v1/badges/catalog` and `GET /v1/badges`). */
857
+ badges?: Badge[];
858
+ /** Each board's standings (`GET /v1/leaderboards/:board`), keyed by the board's key or ID. */
859
+ leaderboards?: Record<string, LeaderboardPage>;
860
+ /**
861
+ * Answers for routes the fake does not cover, or replacements for ones it does, keyed
862
+ * `"METHOD /v1/path"`. A `:name` path segment matches any one segment, as in
863
+ * `"POST /v1/me/submissions/:submission/submit"`.
864
+ */
865
+ routes?: Record<string, (request: Request) => Response | Promise<Response>>;
866
+ };
867
+ /**
868
+ * One request the fake answered: its method and `/v1/...` path, without the query.
869
+ *
870
+ * @experimental A minor release can change it, like `createFakeRelay`.
871
+ */
872
+ type FakeRelayRequest = {
873
+ method: string;
874
+ path: string;
875
+ };
876
+ /**
877
+ * The fake `createFakeRelay` returns.
878
+ *
879
+ * @experimental A minor release can change it, like `createFakeRelay`.
880
+ */
881
+ type FakeRelay = {
882
+ /** Answers one request. Use it from a Playwright `page.route` handler or an MSW handler. */
883
+ handle(request: Request): Promise<Response>;
884
+ /** A `fetch` that answers from the fake, for `createRelayClient({ fetch })`. */
885
+ fetch: typeof fetch;
886
+ /** Every request answered so far, in order. */
887
+ readonly requests: FakeRelayRequest[];
888
+ };
889
+ /**
890
+ * An in-memory participant API for UI tests: it answers what `RelayProvider` and the hooks read
891
+ * from the seeds, as Relay would, under `/v1/...` and the hosted proxy's `/relay/v1/...`.
892
+ *
893
+ * Every answer is checked against the schema the SDK reads it with, so a seed Relay could never
894
+ * send throws a `TestSetupError` naming the route. A participant route the fake does not cover
895
+ * answers 501 with code `not-faked`, so a test cannot pass on an empty answer; `routes` answers
896
+ * it instead. Any other path answers 404 with code `route-not-found`, as the proxy does.
897
+ *
898
+ * @experimental Outside the 1.x compatibility promise: a minor release can change its options,
899
+ * the routes it fakes and its answers, as the participant API it imitates grows.
900
+ */
901
+ declare function createFakeRelay(options?: FakeRelayOptions): FakeRelay;
902
+
903
+ export { type AttemptReplay, type CalendarSegment, type CapabilityCall, type CapabilityOutcome, type CapabilityRequest, Decision, DecisionAssertionError, EvaluatorError, type FakeAnswer, type FakeRelay, type FakeRelayOptions, type FakeRelayRequest, type QuestDiagnostic, type QuestHostContext, type QuestInputOf, type QuestRun, type ReplayDivergence, type ReplayOptions, type RunQuestOptions, type TestContext, type TestFacts, type TestRelease, TestSetupError, type TestableQuest, type TrackerFacts, type TrackerObservationInput, type TrackerReadRequest, type TrackerRecordInput, type WalletBalanceRequest, createFakeRelay, createTestContext, defaultMember, defaultNow, defaultSeed, expectAccept, expectDefer, expectProgress, expectReject, expectReview, fakeEvaluationMember, maxCapabilityCalls, replayAttempt, runQuest, trackerObservation, trackerPage };
package/dist/testing.js CHANGED
@@ -1,23 +1,40 @@
1
1
  import {
2
2
  attemptCaptureSchema
3
- } from "./chunk-7A4J67ZL.js";
3
+ } from "./chunk-XNKSSE32.js";
4
4
  import {
5
5
  CapabilityError
6
6
  } from "./chunk-4OC7REYX.js";
7
7
  import {
8
8
  parseImageResult
9
- } from "./chunk-5I54XBK2.js";
9
+ } from "./chunk-E6IKQHTX.js";
10
10
  import {
11
11
  imageRequestSchema,
12
12
  xConnectionSchema,
13
13
  xPostSchema,
14
14
  xProfileSchema,
15
15
  xRequestSchema
16
- } from "./chunk-L77O64I4.js";
16
+ } from "./chunk-QYPMPWVX.js";
17
17
  import {
18
+ isParticipantRoute
19
+ } from "./chunk-CZFXGGEK.js";
20
+ import {
21
+ participantActivitySchema,
22
+ questExperienceSchema
23
+ } from "./chunk-D26YNACF.js";
24
+ import {
25
+ badgeCatalogSchema,
26
+ badgeSchema,
27
+ leaderboardPageSchema,
18
28
  pointBalanceSchema,
29
+ pointBalancesSchema,
30
+ pointHistoryPageSchema,
31
+ pointLedgerReferenceSchema,
19
32
  profileReferralSchema
20
- } from "./chunk-SE4PFJPT.js";
33
+ } from "./chunk-WSBYWVTZ.js";
34
+ import {
35
+ exchangeResultSchema,
36
+ participantSessionSchema
37
+ } from "./chunk-XJFYAXET.js";
21
38
  import {
22
39
  contentPerformanceSnapshotSchema,
23
40
  cosmosAddressSchema,
@@ -32,8 +49,10 @@ import {
32
49
  isContractEvent,
33
50
  isNftCollection,
34
51
  isSolanaProgram,
52
+ knownResponse,
35
53
  linkedWalletSchema,
36
54
  maxMemberCompletions,
55
+ maxWalletEventLogs,
37
56
  memberCompletionSchema,
38
57
  memberIdentitySchema,
39
58
  photoResultSchema,
@@ -42,6 +61,8 @@ import {
42
61
  solanaAddressSchema,
43
62
  solanaMainnet,
44
63
  telegramChatMemberSchema,
64
+ telegramConnectionInputSchema,
65
+ telegramConnectionSchema,
45
66
  telegramMemberInputSchema,
46
67
  textEvidenceSchema,
47
68
  textResultSchema,
@@ -61,7 +82,10 @@ import {
61
82
  walletEventsSchema,
62
83
  walletNftsSchema,
63
84
  walletTransactionsSchema
64
- } from "./chunk-ZDJLPN4L.js";
85
+ } from "./chunk-MK425RXJ.js";
86
+ import {
87
+ RelayErrorCode
88
+ } from "./chunk-K2KS3JLY.js";
65
89
 
66
90
  // src/testing/run.ts
67
91
  import * as z3 from "zod";
@@ -202,7 +226,7 @@ var capabilityRequestSchemas = {
202
226
  };
203
227
  var telegramRequestSchema = z.strictObject({
204
228
  kind: z.literal("telegram"),
205
- input: telegramMemberInputSchema
229
+ input: z.union([telegramMemberInputSchema, telegramConnectionInputSchema])
206
230
  });
207
231
  var walletNftsRequestSchema = z.strictObject({
208
232
  kind: z.literal("wallet-nfts"),
@@ -221,7 +245,8 @@ var walletEventsRequestSchema = z.strictObject({
221
245
  since: z.number().int().nonnegative().safe(),
222
246
  afterActivation: z.boolean(),
223
247
  where: z.record(z.string().min(1).max(100), walletEventConditionSchema).refine((where) => Object.keys(where).length <= 10).nullable(),
224
- address: z.string().min(1).max(100).nullable()
248
+ address: z.string().min(1).max(100).nullable(),
249
+ newest: z.number().int().min(1).max(maxWalletEventLogs).optional()
225
250
  });
226
251
  var walletTransactionsRequestSchema = z.strictObject({
227
252
  kind: z.literal("wallet-transactions"),
@@ -342,7 +367,7 @@ function parseCapabilityResult(request, value) {
342
367
  case "member-identities":
343
368
  return z.array(memberIdentitySchema).min(1).nullable().parse(value);
344
369
  case "telegram":
345
- return telegramChatMemberSchema.nullable().parse(value);
370
+ return ("chat" in request.input ? telegramChatMemberSchema : telegramConnectionSchema).nullable().parse(value);
346
371
  case "wallet-nfts":
347
372
  return walletNftsSchema.nullable().parse(value);
348
373
  case "wallet-call":
@@ -414,6 +439,7 @@ var readerFactories = {
414
439
  message: (input) => call({ kind: "discord", input })
415
440
  }),
416
441
  telegram: (call) => ({
442
+ connection: () => call({ kind: "telegram", input: { operation: "connection" } }),
417
443
  member: (input) => call({ kind: "telegram", input })
418
444
  }),
419
445
  x: (call) => ({
@@ -493,7 +519,9 @@ var readerFactories = {
493
519
  since: query.since,
494
520
  afterActivation: query.afterActivation ?? false,
495
521
  where: query.where ?? null,
496
- address: query.address ?? null
522
+ address: query.address ?? null,
523
+ // Only when set, so a call without it sends the request it always did.
524
+ ...query.newest === void 0 ? {} : { newest: query.newest }
497
525
  }),
498
526
  transactions: (query) => call({
499
527
  kind: "wallet-transactions",
@@ -641,21 +669,21 @@ function install(now, seed, restores) {
641
669
  const crypto = globalThis.crypto;
642
670
  if (crypto) {
643
671
  restores.push(
644
- replace(crypto, "getRandomValues", (array3) => {
645
- if (!integerArrays.some((type) => array3 instanceof type)) {
672
+ replace(crypto, "getRandomValues", (array4) => {
673
+ if (!integerArrays.some((type) => array4 instanceof type)) {
646
674
  throw new DOMException(
647
675
  "getRandomValues takes an integer typed array",
648
676
  "TypeMismatchError"
649
677
  );
650
678
  }
651
- if (array3.byteLength > 65536) {
679
+ if (array4.byteLength > 65536) {
652
680
  throw new DOMException(
653
681
  "getRandomValues takes at most 65536 bytes",
654
682
  "QuotaExceededError"
655
683
  );
656
684
  }
657
- fill(new Uint8Array(array3.buffer, array3.byteOffset, array3.byteLength));
658
- return array3;
685
+ fill(new Uint8Array(array4.buffer, array4.byteOffset, array4.byteLength));
686
+ return array4;
659
687
  })
660
688
  );
661
689
  restores.push(
@@ -796,10 +824,14 @@ function fakeFor(facts, request) {
796
824
  };
797
825
  }
798
826
  case "telegram":
799
- return {
827
+ return "chat" in request.input ? {
800
828
  name: "telegram.member",
801
829
  answer: facts.telegram?.member,
802
830
  args: [request.input]
831
+ } : {
832
+ name: "telegram.connection",
833
+ answer: facts.telegram?.connection,
834
+ args: []
803
835
  };
804
836
  case "tracker-records":
805
837
  case "tracker-summary": {
@@ -1082,6 +1114,9 @@ function factsAnswerer(facts) {
1082
1114
  return "channelId" in request.input || fake.answer !== void 0 ? ask(fake) : { value: null };
1083
1115
  // Without an answer, the member has not linked Telegram.
1084
1116
  case "telegram":
1117
+ if (!("chat" in request.input)) {
1118
+ return pin(request);
1119
+ }
1085
1120
  return fake.answer !== void 0 ? ask(fake) : { value: null };
1086
1121
  case "image": {
1087
1122
  if (imageReferences(request.input).some(
@@ -1518,6 +1553,33 @@ async function runQuest(quest, options = {}) {
1518
1553
  );
1519
1554
  }
1520
1555
 
1556
+ // src/testing/member.ts
1557
+ var keyOf = (resource) => typeof resource === "string" ? resource : resource.key;
1558
+ function fakeEvaluationMember(member = {}) {
1559
+ return {
1560
+ id: defaultMember,
1561
+ identities: async () => null,
1562
+ balance: async (ledger) => {
1563
+ throw new CapabilityError(
1564
+ "author-error",
1565
+ `The fake member has no balance in point ledger ${keyOf(ledger)}; pass balance to fakeEvaluationMember`,
1566
+ { retryable: false }
1567
+ );
1568
+ },
1569
+ completions: async () => [],
1570
+ groups: async () => [],
1571
+ enrolled: async () => false,
1572
+ referrals: async (program) => {
1573
+ throw new CapabilityError(
1574
+ "author-error",
1575
+ `The fake member has no progress in referral program ${keyOf(program)}; pass referrals to fakeEvaluationMember`,
1576
+ { retryable: false }
1577
+ );
1578
+ },
1579
+ ...member
1580
+ };
1581
+ }
1582
+
1521
1583
  // src/testing/assertions.ts
1522
1584
  var DecisionAssertionError = class extends Error {
1523
1585
  decision;
@@ -1685,7 +1747,7 @@ function captureAnswerer(facts, fallback, diverged) {
1685
1747
  if (unlistedRequest(request)) {
1686
1748
  if (!answerFallback) {
1687
1749
  const [reader, fact2] = {
1688
- telegram: ["ctx.telegram.member()", "facts.telegram"],
1750
+ telegram: request.kind === "telegram" && !("chat" in request.input) ? ["ctx.telegram.connection()", "facts.telegram.connection"] : ["ctx.telegram.member()", "facts.telegram"],
1689
1751
  "wallet-nfts": ["ctx.wallet.nfts()", "facts.wallet.nfts"],
1690
1752
  "wallet-call": ["ctx.wallet.call()", "facts.wallet.call"],
1691
1753
  "wallet-delegations": [
@@ -1782,11 +1844,253 @@ async function replayAttempt(quest, capture, options = {}) {
1782
1844
  calls: host.calls
1783
1845
  };
1784
1846
  }
1847
+
1848
+ // src/testing/participant-api.ts
1849
+ import * as z5 from "zod";
1850
+ var defaultScope = {
1851
+ organization: "org_test",
1852
+ project: "project_test",
1853
+ environment: "test"
1854
+ };
1855
+ var sessionLifetimeMs = 24 * 60 * 60 * 1e3;
1856
+ var refuse = (status, code, error) => Response.json({ error, code }, { status });
1857
+ function createFakeRelay(options = {}) {
1858
+ let member = options.member ?? null;
1859
+ const scope = options.scope ?? defaultScope;
1860
+ const requests = [];
1861
+ const activity = options.activity ?? [];
1862
+ const balances = options.balances ?? [];
1863
+ const badges = options.badges ?? [];
1864
+ const ledgers = options.ledgers ?? balances.map((balance) => balance.ledger).filter(
1865
+ (ledger, index, all) => all.findIndex((other) => other.id === ledger.id) === index
1866
+ );
1867
+ const overrides = Object.entries(options.routes ?? {}).map(
1868
+ ([key, answer2]) => {
1869
+ const [method, path, ...rest] = key.trim().split(/\s+/);
1870
+ if (!method || !path?.startsWith("/v1/") || rest.length > 0) {
1871
+ throw new TestSetupError(
1872
+ `routes["${key}"]: name a route as "METHOD /v1/path"`
1873
+ );
1874
+ }
1875
+ const source = path.split("/").map(
1876
+ (segment) => segment.startsWith(":") ? "[^/]+" : segment.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
1877
+ ).join("/");
1878
+ return {
1879
+ method: method.toUpperCase(),
1880
+ pattern: new RegExp(`^${source}$`),
1881
+ answer: answer2
1882
+ };
1883
+ }
1884
+ );
1885
+ const session = (current) => ({
1886
+ ...scope,
1887
+ id: `session_${current}`,
1888
+ principal: { kind: "member", member: current },
1889
+ expiresAt: Date.now() + sessionLifetimeMs
1890
+ });
1891
+ const answer = (route, schema, value) => {
1892
+ const wire = JSON.parse(JSON.stringify(value));
1893
+ const parsed = knownResponse(schema).safeParse(wire);
1894
+ if (!parsed.success) {
1895
+ const issue = parsed.error.issues[0];
1896
+ const at = issue?.path.length ? ` at ${issue.path.join(".")}` : "";
1897
+ throw new TestSetupError(
1898
+ `createFakeRelay: the answer to ${route} is not one Relay sends${at}: ${issue?.message ?? parsed.error.message}`
1899
+ );
1900
+ }
1901
+ return Response.json(wire);
1902
+ };
1903
+ const experience = (current) => answer("GET /v1/me/experience", questExperienceSchema, {
1904
+ member: current,
1905
+ quests: options.quests ?? [],
1906
+ collections: options.collections ?? [],
1907
+ entitlements: options.entitlements ?? []
1908
+ });
1909
+ const activityPage = (current, query) => {
1910
+ const cursor = query.get("cursor");
1911
+ const limit = Number(query.get("limit") ?? 50);
1912
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
1913
+ return refuse(400, "invalid-request", "limit must be 1 to 100");
1914
+ }
1915
+ const start = query.get("start") === "latest" ? activity.length : cursor === null ? 0 : Number(cursor.replace(/^fake-/, ""));
1916
+ if (!Number.isInteger(start) || start < 0 || start > activity.length) {
1917
+ return refuse(400, "invalid-request", "Invalid activity cursor");
1918
+ }
1919
+ const items = activity.slice(start, start + limit);
1920
+ return answer("GET /v1/me/activity", participantActivitySchema, {
1921
+ member: current,
1922
+ items,
1923
+ nextCursor: `fake-${start + items.length}`
1924
+ });
1925
+ };
1926
+ const leaderboard = (board) => {
1927
+ const page = options.leaderboards?.[board] ?? Object.values(options.leaderboards ?? {}).find(
1928
+ (seed) => seed.leaderboard === board
1929
+ );
1930
+ return page ? answer("GET /v1/leaderboards/:board", leaderboardPageSchema, page) : refuse(404, RelayErrorCode.NotFound, "Leaderboard not found");
1931
+ };
1932
+ const pointHistory = (ledger) => {
1933
+ const known = ledgers.find(
1934
+ (reference) => reference.id === ledger || reference.key === ledger
1935
+ );
1936
+ const entries = options.pointHistory?.[ledger] ?? (known && (options.pointHistory?.[known.id] ?? (known.key ? options.pointHistory?.[known.key] : void 0)));
1937
+ if (!known && !entries) {
1938
+ return refuse(404, RelayErrorCode.NotFound, "Point ledger not found");
1939
+ }
1940
+ return answer("GET /v1/me/points/:ledger/history", pointHistoryPageSchema, {
1941
+ items: entries ?? [],
1942
+ nextCursor: null
1943
+ });
1944
+ };
1945
+ const exchange = async (request) => {
1946
+ const credential = request.headers.get("Authorization") ?? "";
1947
+ if (!/^Bearer di_\S+$/.test(credential)) {
1948
+ return Response.json(
1949
+ {
1950
+ error: "Use a valid identity integration server key from Console Settings. CLI and management credentials cannot issue participant sessions."
1951
+ },
1952
+ { status: 401 }
1953
+ );
1954
+ }
1955
+ const body = await request.json().catch(() => null);
1956
+ const subject = typeof body === "object" && body && "subject" in body ? body.subject : void 0;
1957
+ if (typeof subject !== "string" || subject.length === 0) {
1958
+ return refuse(400, "invalid-request", "subject is required");
1959
+ }
1960
+ if (member === null && !identifier.safeParse(subject).success) {
1961
+ throw new TestSetupError(
1962
+ `createFakeRelay: POST /v1/auth/exchange signs in subject "${subject}", which is not a member ID; seed options.member`
1963
+ );
1964
+ }
1965
+ member ??= subject;
1966
+ return answer("POST /v1/auth/exchange", exchangeResultSchema, {
1967
+ token: `fake_${member}`,
1968
+ session: session(member)
1969
+ });
1970
+ };
1971
+ const builtIn = (request, method, path, query) => {
1972
+ if (method === "GET" && path === "/v1/auth/session") {
1973
+ return member === null ? Response.json(null) : answer(
1974
+ "GET /v1/auth/session",
1975
+ participantSessionSchema,
1976
+ session(member)
1977
+ );
1978
+ }
1979
+ if (method === "POST" && path === "/v1/auth/logout") {
1980
+ member = null;
1981
+ return answer("POST /v1/auth/logout", z5.object({ ok: z5.boolean() }), {
1982
+ ok: true
1983
+ });
1984
+ }
1985
+ if (method === "POST" && path === "/v1/auth/exchange") {
1986
+ return exchange(request);
1987
+ }
1988
+ if (method === "GET" && path === "/v1/point-ledgers") {
1989
+ return answer(
1990
+ "GET /v1/point-ledgers",
1991
+ z5.array(pointLedgerReferenceSchema),
1992
+ ledgers
1993
+ );
1994
+ }
1995
+ if (method === "GET" && path === "/v1/badges/catalog") {
1996
+ return answer("GET /v1/badges/catalog", badgeCatalogSchema, {
1997
+ items: badges
1998
+ });
1999
+ }
2000
+ if (method === "GET" && path === "/v1/badges") {
2001
+ return answer("GET /v1/badges", z5.array(badgeSchema), badges);
2002
+ }
2003
+ const board = method === "GET" && /^\/v1\/leaderboards\/([\w-]+)$/.exec(path);
2004
+ if (board) {
2005
+ return leaderboard(board[1]);
2006
+ }
2007
+ if (method !== "GET" || !path.startsWith("/v1/me/")) {
2008
+ return void 0;
2009
+ }
2010
+ const history = /^\/v1\/me\/points\/([\w-]+)\/history$/.exec(path);
2011
+ const covered = path === "/v1/me/experience" || path === "/v1/me/points" || path === "/v1/me/activity" || history;
2012
+ if (!covered) {
2013
+ return void 0;
2014
+ }
2015
+ if (member === null) {
2016
+ return refuse(401, RelayErrorCode.NotSignedIn, "Sign in to continue");
2017
+ }
2018
+ if (history) {
2019
+ return pointHistory(history[1]);
2020
+ }
2021
+ switch (path) {
2022
+ case "/v1/me/experience":
2023
+ return experience(member);
2024
+ case "/v1/me/points":
2025
+ return answer("GET /v1/me/points", pointBalancesSchema, {
2026
+ items: balances
2027
+ });
2028
+ default:
2029
+ return activityPage(member, query);
2030
+ }
2031
+ };
2032
+ const handle = async (request) => {
2033
+ const url = new URL(request.url);
2034
+ const path = url.pathname.startsWith("/relay/v1/") ? url.pathname.slice("/relay".length) : url.pathname;
2035
+ const method = request.method.toUpperCase();
2036
+ requests.push({ method, path });
2037
+ const override = overrides.find(
2038
+ (route) => route.method === method && route.pattern.test(path)
2039
+ );
2040
+ if (override) {
2041
+ return override.answer(request);
2042
+ }
2043
+ const response = builtIn(request, method, path, url.searchParams);
2044
+ if (response) {
2045
+ return response;
2046
+ }
2047
+ if (isParticipantRoute(path, method)) {
2048
+ return refuse(
2049
+ 501,
2050
+ "not-faked",
2051
+ `createFakeRelay does not answer ${method} ${path}: answer it with options.routes["${method} ${path}"]`
2052
+ );
2053
+ }
2054
+ return refuse(404, "route-not-found", "Route not found");
2055
+ };
2056
+ experience(member ?? "member_test");
2057
+ answer("GET /v1/me/points", pointBalancesSchema, { items: balances });
2058
+ answer("GET /v1/point-ledgers", z5.array(pointLedgerReferenceSchema), ledgers);
2059
+ answer("GET /v1/badges/catalog", badgeCatalogSchema, { items: badges });
2060
+ answer("GET /v1/me/activity", participantActivitySchema, {
2061
+ member: member ?? "member_test",
2062
+ items: activity,
2063
+ nextCursor: "fake-0"
2064
+ });
2065
+ for (const ledger of Object.keys(options.pointHistory ?? {})) {
2066
+ pointHistory(ledger);
2067
+ }
2068
+ for (const board of Object.keys(options.leaderboards ?? {})) {
2069
+ leaderboard(board);
2070
+ }
2071
+ if (member !== null) {
2072
+ answer("GET /v1/auth/session", participantSessionSchema, session(member));
2073
+ }
2074
+ return {
2075
+ handle,
2076
+ fetch: (input, init) => handle(
2077
+ new Request(
2078
+ input instanceof Request ? input : new URL(
2079
+ String(input),
2080
+ globalThis.location?.href ?? "http://localhost"
2081
+ ),
2082
+ init
2083
+ )
2084
+ ),
2085
+ requests
2086
+ };
2087
+ }
1785
2088
  export {
1786
2089
  CapabilityError,
1787
2090
  DecisionAssertionError,
1788
2091
  EvaluatorError,
1789
2092
  TestSetupError,
2093
+ createFakeRelay,
1790
2094
  createTestContext,
1791
2095
  defaultMember,
1792
2096
  defaultNow,
@@ -1796,6 +2100,7 @@ export {
1796
2100
  expectProgress,
1797
2101
  expectReject,
1798
2102
  expectReview,
2103
+ fakeEvaluationMember,
1799
2104
  maxCapabilityCalls,
1800
2105
  replayAttempt,
1801
2106
  runQuest,