@domino-sdk/relay 1.0.0-next.1 → 1.0.0-next.12

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 (56) hide show
  1. package/README.md +13 -1
  2. package/build.mjs +18 -1
  3. package/dist/authoring.d.ts +461 -51
  4. package/dist/authoring.js +317 -65
  5. package/dist/avatar.d.ts +29 -0
  6. package/dist/avatar.js +21 -0
  7. package/dist/browser.d.ts +325 -2266
  8. package/dist/browser.js +6 -6
  9. package/dist/catalog-qrYne8qV.d.ts +4161 -0
  10. package/dist/chunk-22J4K7ED.js +1426 -0
  11. package/dist/chunk-4LBUTHD6.js +171 -0
  12. package/dist/chunk-5L7XZA4Y.js +1012 -0
  13. package/dist/chunk-6A2V4L3T.js +30 -0
  14. package/dist/{chunk-OQ4ZNOC7.js → chunk-7Q4BGGT6.js} +26 -5
  15. package/dist/{chunk-BGN3CQC6.js → chunk-EWFB35JW.js} +145 -32
  16. package/dist/chunk-LY7SXUNW.js +301 -0
  17. package/dist/chunk-NZA2TLBF.js +68 -0
  18. package/dist/chunk-UW5CQHPQ.js +100 -0
  19. package/dist/chunk-V7UKZLHA.js +35 -0
  20. package/dist/chunk-YYD4QUBO.js +2348 -0
  21. package/dist/client-CW72TaAs.d.ts +2704 -0
  22. package/dist/collection-BGneJ6Bk.d.ts +114 -0
  23. package/dist/deployments-Bc0dPSXN.d.ts +4119 -0
  24. package/dist/identity-integration-CBR8l3ju.d.ts +37 -0
  25. package/dist/images-D0Txfte6.d.ts +234 -0
  26. package/dist/index.d.ts +13 -13133
  27. package/dist/index.js +23 -690
  28. package/dist/integrations-EjQzIApL.d.ts +323 -0
  29. package/dist/internal.d.ts +90 -0
  30. package/dist/internal.js +52 -0
  31. package/dist/observations-bFY4yv2K.d.ts +90 -0
  32. package/dist/pages-KYK1L60W.d.ts +152 -0
  33. package/dist/portal-proxy.js +9 -9
  34. package/dist/profiles-BtOXMGcC.d.ts +297 -0
  35. package/dist/react/index.d.ts +403 -2230
  36. package/dist/react/index.js +165 -23
  37. package/dist/referrals-CvSmxvjm.d.ts +2592 -0
  38. package/dist/request-error-VnTiMXc3.d.ts +52 -0
  39. package/dist/schema-ZfjL-d1N.d.ts +3532 -0
  40. package/dist/schema.d.ts +36 -3375
  41. package/dist/schema.js +547 -3
  42. package/dist/server/index.d.ts +19411 -0
  43. package/dist/server/index.js +1860 -0
  44. package/dist/streaks-CwGReWGt.d.ts +73 -0
  45. package/dist/submission-input-CccwAl5Z.d.ts +83 -0
  46. package/dist/trackers-BsUh5Ky0.d.ts +1333 -0
  47. package/dist/units-CwgsPB_r.d.ts +9 -0
  48. package/dist/vite.d.ts +1 -1
  49. package/dist/vite.js +1 -0
  50. package/dist/wallet-BtiG0Xhr.d.ts +231 -0
  51. package/package.json +22 -5
  52. package/dist/chunk-IZYXEXUO.js +0 -1847
  53. package/dist/chunk-QEN4GHL7.js +0 -624
  54. package/dist/chunk-UI4N4XRZ.js +0 -373
  55. package/dist/chunk-UPWID5KS.js +0 -1833
  56. package/dist/submission-input-JDwq3EVK.d.ts +0 -4986
package/README.md CHANGED
@@ -6,6 +6,18 @@ Author quests and connect an application to Domino Relay.
6
6
  pnpm add @domino-sdk/relay
7
7
  ```
8
8
 
9
- Use `@domino-sdk/relay/authoring` for quest definitions, `/browser` for browser integrations, `/portal-proxy` for server proxies, and `/build` for bundling authored modules.
9
+ | Import | For |
10
+ | -------------------------------- | ------------------------------------------------------------------------------------------ |
11
+ | `@domino-sdk/relay` | The participant client (`createRelayClient`), `RelayError`, and the types it returns |
12
+ | `@domino-sdk/relay/browser` | The browser client with durable submission recovery, wallet signers and sign-in options |
13
+ | `@domino-sdk/relay/react` | React hooks over the browser client |
14
+ | `@domino-sdk/relay/avatar` | Generated default avatars (`avatarSrc`, `defaultAvatar`) |
15
+ | `@domino-sdk/relay/authoring` | Quest, tracker, leaderboard, referral and engagement definitions, and evaluator types |
16
+ | `@domino-sdk/relay/schema` | Zod schemas of Relay's API bodies, deployments, tracker records and observations |
17
+ | `@domino-sdk/relay/build` | Bundling authored modules; needs `esbuild` installed |
18
+ | `@domino-sdk/relay/portal-proxy` | Server proxies and hosted app Workers |
19
+ | `@domino-sdk/relay/vite` | The Vite plugin that keeps server code out of the browser build |
20
+
21
+ `@domino-sdk/relay/internal` is shared by Relay's own services and Console. It is not a supported API and can change in any release.
10
22
 
11
23
  Requires Node.js 24 for tooling. Proprietary software; `UNLICENSED`.
package/build.mjs CHANGED
@@ -1,5 +1,21 @@
1
1
  import { resolve, dirname } from "node:path"
2
- import { build } from "esbuild"
2
+
3
+ // esbuild is an optional peer dependency: only quest builds need it, so apps that use the runtime
4
+ // client do not install its native binary. `@domino-sdk/relay-cli` depends on it.
5
+ async function esbuild() {
6
+ try {
7
+ return await import("esbuild")
8
+ } catch (error) {
9
+ if (error?.code === "ERR_MODULE_NOT_FOUND") {
10
+ throw new Error(
11
+ "@domino-sdk/relay/build needs esbuild. Install it next to @domino-sdk/relay, or build with the domino CLI (@domino-sdk/relay-cli).",
12
+ { cause: error }
13
+ )
14
+ }
15
+
16
+ throw error
17
+ }
18
+ }
3
19
 
4
20
  /** Bundle an authored quest without evaluating developer code in the build process. */
5
21
  export async function buildQuest(entry, options = {}) {
@@ -10,6 +26,7 @@ export async function buildQuest(entry, options = {}) {
10
26
  throw new Error("Invalid quest export name")
11
27
  }
12
28
 
29
+ const { build } = await esbuild()
13
30
  const result = await build({
14
31
  ...(options.exportName
15
32
  ? {
@@ -1,7 +1,312 @@
1
- import { A as AdmittedContent, O as Observation, T as TrackerTriggerContext, a as TrackerReader, X as XReader, I as ImageReader, D as DiscordMember, b as DiscordMessage, W as WalletConnection, S as SolanaBalance, L as LinkedWallet, c as WalletBalanceQuery, d as WalletBalance, e as TrackerDefinition, f as TrackerTriggerMode, g as TrackerWhere, o as observationTriggerSchema, h as TrackerTriggerPolicy, B as Budget, i as AuthoringIdentity, C as ContentMetric, j as IntegrationProvider, k as StreakPolicy, l as TextEvidence, U as UrlEvidence, m as TextOptions, n as UrlOptions } from './submission-input-JDwq3EVK.js';
2
- export { E as EvmChainId, p as content, q as customSource, r as defineEngagementReward, s as defineLeaderboard, t as defineReferral, u as defineTracker, v as discord, w as formatUnits, x as isEvmChainId, y as parseUnits, z as perContent, F as pointLedgers, G as settings, H as streak, J as trackerTriggerContextSchema, K as x } from './submission-input-JDwq3EVK.js';
3
1
  import { z } from 'zod';
4
- import { TextReviewInput, TextResult, PhotoResult, rewardSchema, triggerSchema, QuestPresentation, QuestProfile, QuizEvidence, Decision, TieredRewardDefinition } from './schema.js';
2
+ export { z } from 'zod';
3
+ import { e as ContentPolicy, C as ContentMetric, E as EngagementDefinition, P as leaderboardAuthoringSchema, a7 as referralAuthoringSchema, A as AdmittedContent } from './referrals-CvSmxvjm.js';
4
+ import { Q as QuizContent, a as StreakPolicy } from './streaks-CwGReWGt.js';
5
+ import { b as ResourceSource, S as Setting, g as XReader, I as IntegerSettings, a as IntegrationProvider } from './integrations-EjQzIApL.js';
6
+ import { j as TrackerDefinition, I as IdentityKind, J as customSourceSchema, F as WithRecordData, t as TrackerSource, a1 as trackerDefinitionSchema, a as Tracker, h as TrackerData, au as xSearchSourceSchema, X as XTweet, v as TrackerTriggerContext, x as TrackerTriggerPolicy, q as TrackerReader, W as Where, w as TrackerTriggerMode, E as WhereSetting } from './trackers-BsUh5Ky0.js';
7
+ export { T as TrackerDataSchema, m as TrackerPage, aw as trackerDataSchema, ak as trackerTriggerContextSchema } from './trackers-BsUh5Ky0.js';
8
+ import { e as ImageReader } from './images-D0Txfte6.js';
9
+ import { O as Observation, D as DiscordMessage, h as observationTriggerSchema, B as Budget } from './observations-bFY4yv2K.js';
10
+ import { D as DiscordMember, T as TextEvidence, U as UrlEvidence, b as TextOptions, c as UrlOptions } from './submission-input-CccwAl5Z.js';
11
+ import { n as TextReviewInput, m as TextResult, P as PhotoResult, ag as rewardSchema, au as triggerSchema, Q as QuestPresentation, a as QuestProfile, b as QuizEvidence, D as Decision, o as TieredRewardDefinition } from './schema-ZfjL-d1N.js';
12
+ import { k as WalletConnection, S as SolanaBalance, L as LinkedWallet, j as WalletBalanceQuery, i as WalletBalance } from './wallet-BtiG0Xhr.js';
13
+ export { E as EvmChainId, r as isEvmChainId } from './wallet-BtiG0Xhr.js';
14
+ export { f as formatUnits, p as parseUnits } from './units-CwgsPB_r.js';
15
+
16
+ /** Legacy `id` names an authoring key, never a server resource ID. */
17
+ type AuthoringIdentity = {
18
+ key: string;
19
+ id?: string;
20
+ } | {
21
+ key?: never; /** @deprecated Use key. */
22
+ id: string;
23
+ };
24
+
25
+ /** Discover posts automatically; historical release remains an explicit policy. */
26
+ declare function content(tracker: TrackerDefinition, policy?: Partial<Pick<ContentPolicy, "enrollment" | "posts" | "historical">>): {
27
+ kind: "automatic";
28
+ content: {
29
+ tracker: string;
30
+ window: {
31
+ start: number;
32
+ end?: number | undefined;
33
+ };
34
+ enrollment: "required" | "none";
35
+ posts: "after-enrollment" | "since-start";
36
+ historical: "staff" | "automatic" | "claim";
37
+ };
38
+ };
39
+ declare function perContent(): {
40
+ kind: "keyed";
41
+ perContent: true;
42
+ key: (ctx: {
43
+ content?: AdmittedContent | null;
44
+ }) => string;
45
+ };
46
+ /** One independent cumulative formula; management changes capture a new scoring boundary. */
47
+ declare function defineEngagementReward<const M extends readonly [ContentMetric, ...ContentMetric[]]>(input: {
48
+ key: string;
49
+ qualifiedBy: string | {
50
+ key: string;
51
+ };
52
+ balance: string;
53
+ metrics: M;
54
+ baseline: "historical";
55
+ maxAgeMs?: number;
56
+ budget?: EngagementDefinition["budget"];
57
+ target: (input: {
58
+ metrics: Record<M[number], number>;
59
+ }) => number;
60
+ }): {
61
+ key: string;
62
+ describe: () => {
63
+ key: string;
64
+ qualifiedBy: string;
65
+ balance: string;
66
+ metrics: ("likes" | "replies" | "reposts" | "quotes" | "views" | "bookmarks")[];
67
+ baseline: "historical";
68
+ maxAgeMs: number;
69
+ budget: {
70
+ projectDaily?: number | undefined;
71
+ memberDaily?: number | undefined;
72
+ projectLifetime?: number | undefined;
73
+ };
74
+ };
75
+ score(raw: unknown): {
76
+ target: number;
77
+ baseline: number;
78
+ };
79
+ };
80
+ /**
81
+ * One milestone per quest, after `days` consecutive community days with an accepted attempt.
82
+ * Multiple accepted attempts occupy one day. The day comes from the attempt's trusted occurrence
83
+ * time: a tracker record's (or observation's) `occurredAt` for `automatic({ tracker })`, and the
84
+ * submission's admission time for `manual()`, so a manual streak counts the days a member
85
+ * submitted. Not available on content quests or prerequisite-only `automatic()`.
86
+ */
87
+ declare const streak: (options: Omit<StreakPolicy, "kind">) => StreakPolicy;
88
+ declare function defineReferral(input: Omit<z.input<typeof referralAuthoringSchema>, "key" | "id"> & AuthoringIdentity): {
89
+ id: string;
90
+ title: string;
91
+ rewards: {
92
+ inviter: ({
93
+ kind: "tier";
94
+ reward: {
95
+ id: string;
96
+ title: string;
97
+ tiers: {
98
+ id: string;
99
+ title: string;
100
+ }[];
101
+ inventory: {
102
+ kind: "untracked";
103
+ };
104
+ fulfillment: {
105
+ kind: "staff-handover";
106
+ };
107
+ };
108
+ tier: string;
109
+ } | {
110
+ kind: "points";
111
+ balance: string;
112
+ amount: number;
113
+ })[];
114
+ invitee: ({
115
+ kind: "tier";
116
+ reward: {
117
+ id: string;
118
+ title: string;
119
+ tiers: {
120
+ id: string;
121
+ title: string;
122
+ }[];
123
+ inventory: {
124
+ kind: "untracked";
125
+ };
126
+ fulfillment: {
127
+ kind: "staff-handover";
128
+ };
129
+ };
130
+ tier: string;
131
+ } | {
132
+ kind: "points";
133
+ balance: string;
134
+ amount: number;
135
+ })[];
136
+ };
137
+ qualification: {
138
+ kind: "signup";
139
+ } | {
140
+ kind: "quest";
141
+ quest: string;
142
+ } | {
143
+ kind: "external";
144
+ };
145
+ enabled: boolean;
146
+ inviterGroup?: string | undefined;
147
+ milestones: {
148
+ id: string;
149
+ count: number;
150
+ rewards: ({
151
+ kind: "tier";
152
+ reward: {
153
+ id: string;
154
+ title: string;
155
+ tiers: {
156
+ id: string;
157
+ title: string;
158
+ }[];
159
+ inventory: {
160
+ kind: "untracked";
161
+ };
162
+ fulfillment: {
163
+ kind: "staff-handover";
164
+ };
165
+ };
166
+ tier: string;
167
+ } | {
168
+ kind: "points";
169
+ balance: string;
170
+ amount: number;
171
+ })[];
172
+ }[];
173
+ bonuses: {
174
+ id: string;
175
+ balance: string;
176
+ basisPoints: number;
177
+ external: boolean;
178
+ quests?: string[] | undefined;
179
+ durationDays?: number | undefined;
180
+ maxPoints?: number | undefined;
181
+ }[];
182
+ key: string;
183
+ };
184
+ declare function defineLeaderboard(input: Omit<z.input<typeof leaderboardAuthoringSchema>, "key" | "id"> & AuthoringIdentity): {
185
+ id: string;
186
+ balance: string;
187
+ title: string;
188
+ group?: string | undefined;
189
+ score: {
190
+ kind: "balance";
191
+ } | {
192
+ window: {
193
+ kind: "fixed";
194
+ start: number;
195
+ end: number;
196
+ } | {
197
+ kind: "all-time";
198
+ } | {
199
+ kind: "rolling";
200
+ days: number;
201
+ } | {
202
+ kind: "calendar";
203
+ period: "day" | "week" | "month";
204
+ timeZone: string;
205
+ };
206
+ kind: "earned";
207
+ quests?: string[] | undefined;
208
+ } | {
209
+ kind: "net";
210
+ window: {
211
+ kind: "fixed";
212
+ start: number;
213
+ end: number;
214
+ } | {
215
+ kind: "all-time";
216
+ } | {
217
+ kind: "rolling";
218
+ days: number;
219
+ } | {
220
+ kind: "calendar";
221
+ period: "day" | "week" | "month";
222
+ timeZone: string;
223
+ };
224
+ };
225
+ access: "participants" | "public";
226
+ key: string;
227
+ };
228
+ declare const x: {
229
+ /** Posts matching an X search. Each record's `data` is the provider's post payload. */
230
+ search: (input: {
231
+ query: string;
232
+ }) => z.output<typeof xSearchSourceSchema> & WithRecordData<XTweet>;
233
+ };
234
+ /**
235
+ * Records your own code sends with `trackers.ingest`. `data` is a zod object schema that every
236
+ * record's `data` must match: Relay stores its JSON Schema form with the tracker and rejects
237
+ * records that do not match it, and quests read each record's data with its type. With
238
+ * `identities`, each record's author, and each named account in `accounts`, is a verified identity
239
+ * of one of those kinds, which Relay attributes to the member who verified it, so quests can
240
+ * trigger from the tracker. A record states each account's `kind` unless the source names exactly
241
+ * one.
242
+ */
243
+ declare function customSource<S extends z.ZodObject>(namespace: string, options: {
244
+ identities?: IdentityKind[];
245
+ accounts?: string[];
246
+ data: S;
247
+ }): z.output<typeof customSourceSchema> & WithRecordData<z.input<S>>;
248
+ declare function defineTracker<S extends TrackerSource>(input: Omit<z.input<typeof trackerDefinitionSchema>, "source"> & {
249
+ source: S;
250
+ }): Tracker<TrackerData<S>>;
251
+ type ChannelTypes = Extract<ResourceSource, {
252
+ resource: "channel";
253
+ }>["types"];
254
+ declare const discord: {
255
+ roles(options?: {
256
+ assignable?: boolean;
257
+ }): ResourceSource;
258
+ channels(options?: {
259
+ types?: ChannelTypes;
260
+ }): ResourceSource;
261
+ };
262
+ declare const pointLedgers: () => ResourceSource;
263
+ type Label = {
264
+ label: string;
265
+ description?: string;
266
+ };
267
+ declare const settings: {
268
+ pointLedger(options: Label & {
269
+ default: string;
270
+ }): Setting<"point-ledger", z.ZodDefault<z.ZodString>>;
271
+ datetime(options: Label & {
272
+ default: string;
273
+ }): Setting<"datetime", z.ZodDefault<z.ZodPipe<z.ZodISODateTime, z.ZodTransform<string, string>>>>;
274
+ resource(options: Label & {
275
+ source: ResourceSource;
276
+ default?: string;
277
+ }): Setting<"resource", z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<"">, z.ZodString]>>>;
278
+ quiz(options: Label & {
279
+ default: QuizContent;
280
+ }): Setting<"quiz", z.ZodDefault<z.ZodArray<z.ZodObject<{
281
+ id: z.ZodString;
282
+ prompt: z.ZodString;
283
+ choices: z.ZodArray<z.ZodObject<{
284
+ id: z.ZodString;
285
+ label: z.ZodString;
286
+ }, z.core.$strict>>;
287
+ correct: z.ZodString;
288
+ }, z.core.$strict>>>>;
289
+ text(options: Label & {
290
+ default: string;
291
+ minLength?: number;
292
+ maxLength?: number;
293
+ multiline?: boolean;
294
+ }): Setting<"text", z.ZodDefault<z.ZodString>>;
295
+ integer(options: Label & {
296
+ default: number;
297
+ min: number;
298
+ max: number;
299
+ }): Setting<"integer", z.ZodDefault<z.ZodNumber>>;
300
+ boolean(options: Label & {
301
+ default: boolean;
302
+ }): Setting<"boolean", z.ZodDefault<z.ZodBoolean>>;
303
+ choice<const T extends readonly [string, ...string[]]>(options: Label & {
304
+ options: T;
305
+ default: T[number];
306
+ }): Setting<"choice", z.ZodDefault<z.ZodEnum<{ [k_1 in T[number]]: k_1; } extends infer T_1 ? { [k in keyof T_1]: T_1[k]; } : never>>>;
307
+ /** A quest's settings: every field is built with one of the builders above. */
308
+ object<T extends Record<string, Setting>>(shape: T): z.ZodObject<{ -readonly [P in keyof T]: T[P]; }, z.core.$strict>;
309
+ };
5
310
 
6
311
  type Reward = z.infer<typeof rewardSchema>;
7
312
  declare const points: (balance: string, amount: number) => {
@@ -59,8 +364,9 @@ declare function manual(options: {
59
364
  actor: "staff";
60
365
  };
61
366
  /** A tracker trigger: which observations of which tracker run the quest, and for whom. */
62
- type TrackerTriggerInput = {
63
- tracker: TrackerDefinition;
367
+ type TrackerTriggerInput<T extends Tracker = Tracker, W extends Where<TrackerData<T>> | undefined = Where<TrackerData<T>> | undefined> = {
368
+ /** The tracker handle `defineTracker` returns. */
369
+ tracker: T;
64
370
  /**
65
371
  * Which observations fire. `"new-record"`: each record's first observation (the default for X
66
372
  * trackers, where it means each newly collected post). `"new-observation"`: each observation
@@ -70,9 +376,11 @@ type TrackerTriggerInput = {
70
376
  on?: TrackerTriggerMode;
71
377
  /**
72
378
  * Admit only observations whose `data` matches, such as `{ path: "amount", gte: 1000 }` or
73
- * `{ path: "amount", gte: { setting: "threshold" } }`. Others record no outcome.
379
+ * `{ path: "amount", gte: { setting: "threshold" } }`, or a list of at most 10 such conditions
380
+ * that must all hold. Paths and values are checked against the tracker's data type, and a
381
+ * `setting` must name one of the quest's integer settings. Others record no outcome.
74
382
  */
75
- where?: TrackerWhere;
383
+ where?: W;
76
384
  /**
77
385
  * The record account whose member the quest pays: `"author"` (the default), `"inReplyTo"` on
78
386
  * an X tracker, or a named account a custom source declares in `accounts`. Skipped when that
@@ -80,6 +388,24 @@ type TrackerTriggerInput = {
80
388
  */
81
389
  beneficiary?: string;
82
390
  };
391
+ /** Type-only markers a tracker trigger carries into `defineQuest`. They never exist at runtime. */
392
+ declare const triggerData: unique symbol;
393
+ declare const triggerSettings: unique symbol;
394
+ /**
395
+ * A tracker trigger whose records carry `data` of type `D` and whose filter compares with the
396
+ * settings `S`. `defineQuest` types `ctx.tracker` from `D` and checks `S` against its integer
397
+ * settings.
398
+ */
399
+ type TrackerTrigger<D = unknown, S extends string = never> = {
400
+ kind: "automatic";
401
+ tracker: TrackerTriggerPolicy;
402
+ readonly [triggerData]?: D;
403
+ readonly [triggerSettings]?: S;
404
+ };
405
+ type TriggerData<G> = G extends TrackerTrigger<infer D, string> ? unknown extends D ? unknown : D : unknown;
406
+ type TriggerSettings<G> = G extends {
407
+ readonly [triggerSettings]?: infer S;
408
+ } ? unknown extends S ? never : Exclude<S, undefined> : never;
83
409
  /**
84
410
  * An automatic quest. With no argument it runs once its prerequisites are complete; with an
85
411
  * observation it runs for each matching observation; with a tracker it runs for each observation
@@ -92,9 +418,33 @@ declare function automatic(observation: z.infer<typeof observationTriggerSchema>
92
418
  kind: "automatic";
93
419
  observation: z.infer<typeof observationTriggerSchema>;
94
420
  };
95
- declare function automatic(input: TrackerTriggerInput): {
96
- kind: "automatic";
97
- tracker: TrackerTriggerPolicy;
421
+ declare function automatic<T extends Tracker, const W extends Where<TrackerData<T>> | undefined = undefined>(input: TrackerTriggerInput<T, W>): TrackerTrigger<TrackerData<T>, WhereSetting<W>>;
422
+ /**
423
+ * What a trigger `G` supplies under key `K`: `Present` when the trigger has it, null when it cannot,
424
+ * and `Present | null` when `G` is not known.
425
+ */
426
+ type Supplied<G, K extends string, Present, Base> = unknown extends G ? Base | null : G extends unknown ? K extends keyof G ? [G[K]] extends [object] ? Present : Base | null : null : never;
427
+ /** `ctx.tracker` for a trigger: non-null and typed for a tracker trigger, null for others. */
428
+ type ContextTracker<G> = Supplied<G, "tracker", TrackerTriggerContext<TriggerData<G>>, TrackerTriggerContext>;
429
+ /** `ctx.content` for a trigger: the admitted post for a content trigger, null for others. */
430
+ type ContextContent<G> = Supplied<G, "content", AdmittedContent, AdmittedContent>;
431
+ /** `ctx.observation` for a trigger: the observation for an observation trigger, null for others. */
432
+ type ContextObservation<G> = Supplied<G, "observation", Observation, Observation>;
433
+ /**
434
+ * What a `keyed()` completion key reads. TypeScript cannot carry the trigger into a completion
435
+ * built inline, so `tracker` stays nullable here; the evaluator's `ctx.tracker` is typed.
436
+ */
437
+ type KeyedContext = {
438
+ member: {
439
+ id: string;
440
+ };
441
+ content?: AdmittedContent | null;
442
+ observation: Observation | null;
443
+ /** The pinned tracker observation of a tracker-triggered quest; null for other triggers. */
444
+ tracker: TrackerTriggerContext | null;
445
+ eligibleAt: number;
446
+ /** The attempt's evaluation time; see {@link EvaluationContext.now}. */
447
+ now: number;
98
448
  };
99
449
  declare const budget: (definition: Budget) => {
100
450
  id: string;
@@ -105,31 +455,12 @@ declare const budget: (definition: Budget) => {
105
455
  balance: string;
106
456
  };
107
457
  };
108
- declare const keyed: (key: (ctx: {
109
- member: {
110
- id: string;
111
- };
112
- content?: AdmittedContent | null;
113
- observation: Observation | null;
114
- /** The pinned tracker observation of a tracker-triggered quest. */
115
- tracker?: TrackerTriggerContext | null;
116
- eligibleAt: number;
117
- }) => string) => {
458
+ declare const keyed: (key: (ctx: KeyedContext) => string) => {
118
459
  kind: "keyed";
119
- key: (ctx: {
120
- member: {
121
- id: string;
122
- };
123
- content?: AdmittedContent | null;
124
- observation: Observation | null;
125
- /** The pinned tracker observation of a tracker-triggered quest. */
126
- tracker?: TrackerTriggerContext | null;
127
- eligibleAt: number;
128
- }) => string;
460
+ key: (ctx: KeyedContext) => string;
129
461
  };
130
462
  declare const completed: (quest: AuthoringIdentity | string) => {
131
463
  quest: string;
132
- scope: "ever";
133
464
  };
134
465
  declare const untracked: () => {
135
466
  kind: "untracked";
@@ -174,34 +505,82 @@ type Outcome = {
174
505
  kind: "reject";
175
506
  reason: string;
176
507
  };
508
+ /**
509
+ * A performance evaluator's answer. It gates the rewards content evaluation already selected, so
510
+ * it carries none: a `rewards` value fails the evaluation.
511
+ */
512
+ type PerformanceOutcome = {
513
+ kind: "accept";
514
+ rewards?: never;
515
+ } | {
516
+ kind: "review";
517
+ reason: string;
518
+ rewards?: never;
519
+ } | {
520
+ kind: "reject";
521
+ reason: string;
522
+ };
523
+ declare function accept(): {
524
+ kind: "accept";
525
+ };
526
+ declare function accept(options: {
527
+ rewards?: Reward[];
528
+ }): Outcome;
529
+ declare function review(options: {
530
+ reason: string;
531
+ rewards?: undefined;
532
+ }): {
533
+ kind: "review";
534
+ reason: string;
535
+ };
536
+ declare function review(options: {
537
+ reason: string;
538
+ rewards?: Reward[];
539
+ }): Outcome;
177
540
  declare const decision: {
178
- accept: (options?: {
179
- rewards?: Reward[];
180
- }) => Outcome;
541
+ accept: typeof accept;
181
542
  reject: (options: {
182
543
  reason: string;
183
- }) => Outcome;
184
- review: (options: {
544
+ }) => {
545
+ kind: "reject";
185
546
  reason: string;
186
- rewards?: Reward[];
187
- }) => Outcome;
547
+ };
548
+ review: typeof review;
188
549
  };
550
+ /**
551
+ * What an evaluator reads. `G` is the quest's trigger: `tracker`, `content` and `observation` are
552
+ * non-null for the trigger that supplies them and null otherwise. `G` defaults to `any`, which
553
+ * leaves them nullable and lets a shared evaluator, such as `evaluators.photo()`, fit every quest.
554
+ */
189
555
  type EvaluationContext<T, I = {
190
556
  photo: string;
191
- }> = {
557
+ }, G = any> = {
192
558
  settings: T;
193
559
  member: {
194
560
  id: string;
195
561
  };
196
562
  input: I;
197
- content: AdmittedContent | null;
198
- observation: Observation | null;
563
+ /** The admitted post of a content quest. */
564
+ content: ContextContent<G>;
565
+ /** The observation of an observation-triggered quest. */
566
+ observation: ContextObservation<G>;
199
567
  /**
200
568
  * The tracker observation that admitted this attempt, and the member and verified account it
201
- * was attributed to. Null unless the quest has a tracker trigger.
569
+ * was attributed to. Present for a tracker trigger, with `record.data` of the tracker's type.
570
+ */
571
+ tracker: ContextTracker<G>;
572
+ /**
573
+ * When the activity happened, in epoch milliseconds: the observation's or tracker record's
574
+ * occurrence time, else when the attempt was admitted. Budgets and daily keys use it.
202
575
  */
203
- tracker: TrackerTriggerContext | null;
204
576
  eligibleAt: number;
577
+ /**
578
+ * The attempt's evaluation time, in epoch milliseconds: when its first evaluation started.
579
+ * Every retry and replay of the attempt receives the same value, and the sandbox freezes
580
+ * `Date.now()` and `new Date()` at it. Use `eligibleAt` for when the activity
581
+ * happened.
582
+ */
583
+ now: number;
205
584
  trackers: TrackerReader;
206
585
  x: XReader;
207
586
  images: ImageReader;
@@ -236,7 +615,7 @@ type EvaluationContext<T, I = {
236
615
  };
237
616
  type Evaluator<T, I = {
238
617
  photo: string;
239
- }> = (ctx: EvaluationContext<T, I>) => Promise<Outcome>;
618
+ }, G = any> = (ctx: EvaluationContext<T, I, G>) => Promise<Outcome>;
240
619
  declare const evaluators: {
241
620
  quiz(answerKey: QuizEvidence["answers"], options?: {
242
621
  minimumCorrect?: number;
@@ -260,7 +639,16 @@ type QuestInput<T extends Trigger> = T extends {
260
639
  } ? TextEvidence : T extends {
261
640
  input: "url";
262
641
  } ? UrlEvidence : Record<string, never>;
263
- declare function defineQuest<T extends Record<string, z.ZodType>, const G extends Trigger, const M extends readonly [ContentMetric, ...ContentMetric[]] = readonly [
642
+ /**
643
+ * Rejects a trigger whose filter compares with settings that are not integer settings of the
644
+ * quest, naming them.
645
+ */
646
+ type IntegerSettingBounds<G, T> = [
647
+ Exclude<TriggerSettings<G>, IntegerSettings<T>>
648
+ ] extends [never] ? unknown : {
649
+ readonly "The tracker filter compares with settings that are not integer settings of this quest": Exclude<TriggerSettings<G>, IntegerSettings<T>>;
650
+ };
651
+ declare function defineQuest<T extends Record<string, Setting>, const G extends Trigger, const M extends readonly [ContentMetric, ...ContentMetric[]] = readonly [
264
652
  ContentMetric,
265
653
  ...ContentMetric[]
266
654
  ]>(definition: AuthoringIdentity & {
@@ -271,7 +659,7 @@ declare function defineQuest<T extends Record<string, z.ZodType>, const G extend
271
659
  presentation?: (ctx: {
272
660
  settings: z.output<z.ZodObject<T>>;
273
661
  }) => QuestPresentation;
274
- trigger: G;
662
+ trigger: G & NoInfer<IntegerSettingBounds<G, T>>;
275
663
  integrations?: IntegrationProvider[];
276
664
  requires?: ReturnType<typeof completed>[];
277
665
  completion: ReturnType<typeof once> | ReturnType<typeof keyed> | StreakPolicy;
@@ -282,6 +670,12 @@ declare function defineQuest<T extends Record<string, z.ZodType>, const G extend
282
670
  */
283
671
  profile?: QuestProfile;
284
672
  budgets?: Budget[];
673
+ /**
674
+ * Whether staff can review this quest's attempts. Human review is on unless this returns
675
+ * `{ enabled: false }`. With it off, `decision.review(...)` becomes a reject that keeps the
676
+ * review reason, and the attempt records `requestedReview: true`; Console shows "Would have
677
+ * gone to review; human review is off".
678
+ */
285
679
  review?: (ctx: {
286
680
  settings: z.output<z.ZodObject<T>>;
287
681
  }) => {
@@ -290,7 +684,7 @@ declare function defineQuest<T extends Record<string, z.ZodType>, const G extend
290
684
  rewards: Reward[] | ((ctx: {
291
685
  settings: z.output<z.ZodObject<T>>;
292
686
  }) => Reward[]);
293
- evaluate?: Evaluator<z.output<z.ZodObject<T>>, QuestInput<G>>;
687
+ evaluate?: Evaluator<z.output<z.ZodObject<T>>, QuestInput<G>, NoInfer<G>>;
294
688
  performance?: G extends {
295
689
  content: unknown;
296
690
  } ? {
@@ -304,7 +698,7 @@ declare function defineQuest<T extends Record<string, z.ZodType>, const G extend
304
698
  content: AdmittedContent;
305
699
  metrics: Record<M[number], number>;
306
700
  observedAt: number;
307
- }) => Outcome | Promise<Outcome>;
701
+ }) => PerformanceOutcome | Promise<PerformanceOutcome>;
308
702
  } : never;
309
703
  }): {
310
704
  key: string;
@@ -477,7 +871,24 @@ declare function defineQuest<T extends Record<string, z.ZodType>, const G extend
477
871
  } | {
478
872
  path: string;
479
873
  equals: string | number | boolean;
480
- } | undefined;
874
+ } | ({
875
+ path: string;
876
+ gt?: number | {
877
+ setting: string;
878
+ } | undefined;
879
+ gte?: number | {
880
+ setting: string;
881
+ } | undefined;
882
+ lt?: number | {
883
+ setting: string;
884
+ } | undefined;
885
+ lte?: number | {
886
+ setting: string;
887
+ } | undefined;
888
+ } | {
889
+ path: string;
890
+ equals: string | number | boolean;
891
+ })[] | undefined;
481
892
  } | undefined;
482
893
  } | {
483
894
  kind: "manual";
@@ -503,7 +914,6 @@ declare function defineQuest<T extends Record<string, z.ZodType>, const G extend
503
914
  };
504
915
  requires: {
505
916
  quest: string;
506
- scope: "ever";
507
917
  }[];
508
918
  completion: {
509
919
  kind: "streak";
@@ -542,4 +952,4 @@ declare function defineQuest<T extends Record<string, z.ZodType>, const G extend
542
952
  * Console instances receive independent quest identities and pinned versions. */
543
953
  declare const defineQuestType: typeof defineQuest;
544
954
 
545
- export { type EvaluationContext, TrackerTriggerContext, type TrackerTriggerInput, WalletBalance, WalletBalanceQuery, automatic, budget, completed, decision, defineQuest, defineQuestType, defineTieredReward, discordRole, evaluators, evidence, keyed, manual, once, points, staffHandover, untracked };
955
+ export { type EvaluationContext, ImageReader, type KeyedContext, type PerformanceOutcome, Setting, Tracker, TrackerData, TrackerReader, type TrackerTrigger, TrackerTriggerContext, type TrackerTriggerInput, WalletBalance, WalletBalanceQuery, Where, XReader, automatic, budget, completed, content, customSource, decision, defineEngagementReward, defineLeaderboard, defineQuest, defineQuestType, defineReferral, defineTieredReward, defineTracker, discord, discordRole, evaluators, evidence, keyed, manual, once, perContent, pointLedgers, points, settings, staffHandover, streak, untracked, x };