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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +13 -1
  2. package/build.mjs +18 -1
  3. package/dist/activity-B8k9Sa_a.d.ts +9112 -0
  4. package/dist/authoring.d.ts +1355 -182
  5. package/dist/authoring.js +658 -225
  6. package/dist/automatic-outcomes-B1xrETxm.d.ts +758 -0
  7. package/dist/avatar.d.ts +29 -0
  8. package/dist/avatar.js +21 -0
  9. package/dist/browser-J7tUpXxy.d.ts +4154 -0
  10. package/dist/browser.d.ts +11 -4580
  11. package/dist/browser.js +8 -6
  12. package/dist/capability-error-DNMwKmlL.d.ts +458 -0
  13. package/dist/chunk-36HTX2BM.js +1223 -0
  14. package/dist/chunk-6QJ3LADK.js +112 -0
  15. package/dist/chunk-BGGKNIRP.js +171 -0
  16. package/dist/chunk-EYH5RDIE.js +496 -0
  17. package/dist/chunk-GQKYAQM6.js +84 -0
  18. package/dist/chunk-GUP7YRR4.js +42 -0
  19. package/dist/chunk-GZ6FJAXN.js +1659 -0
  20. package/dist/chunk-JON5SESL.js +2986 -0
  21. package/dist/chunk-MC6V6VMG.js +49 -0
  22. package/dist/chunk-NPGENI7K.js +37 -0
  23. package/dist/chunk-V7UKZLHA.js +35 -0
  24. package/dist/chunk-VFHHLN6A.js +284 -0
  25. package/dist/chunk-WJSSJ7AF.js +151 -0
  26. package/dist/{chunk-VH4OQ5VX.js → chunk-XFMSUPNS.js} +26 -5
  27. package/dist/{chunk-FDWRBMB3.js → chunk-ZVIRG24J.js} +150 -33
  28. package/dist/client-XiuFnug_.d.ts +4262 -0
  29. package/dist/groups-Ck-OxzjD.d.ts +2449 -0
  30. package/dist/identity-integration-GxAHdKR2.d.ts +58 -0
  31. package/dist/images-Dv-P5VBw.d.ts +234 -0
  32. package/dist/index.d.ts +15 -13115
  33. package/dist/index.js +30 -689
  34. package/dist/internal.d.ts +159 -0
  35. package/dist/internal.js +79 -0
  36. package/dist/offers-Cy-e4o5x.d.ts +431 -0
  37. package/dist/outcomes-kVB-Juzs.d.ts +4902 -0
  38. package/dist/pages-BBYD21IE.d.ts +359 -0
  39. package/dist/participant-rewards-3duLb8YJ.d.ts +668 -0
  40. package/dist/portal-proxy.js +26 -9
  41. package/dist/profiles-D2aFcOxu.d.ts +297 -0
  42. package/dist/react/index.d.ts +3378 -3318
  43. package/dist/react/index.js +1635 -783
  44. package/dist/request-error-DWduJCGV.d.ts +62 -0
  45. package/dist/schema-DfJzH2ci.d.ts +5644 -0
  46. package/dist/schema.d.ts +15 -3388
  47. package/dist/schema.js +790 -3
  48. package/dist/server/index.d.ts +81498 -0
  49. package/dist/server/index.js +2520 -0
  50. package/dist/streaks-CWyq8Px5.d.ts +71 -0
  51. package/dist/submission-input-BZkm6ohU.d.ts +290 -0
  52. package/dist/units-CwgsPB_r.d.ts +9 -0
  53. package/dist/vite.d.ts +1 -1
  54. package/dist/vite.js +1 -0
  55. package/dist/wallet-YssWxGg8.d.ts +228 -0
  56. package/package.json +24 -5
  57. package/dist/chunk-4KNTMNXA.js +0 -1847
  58. package/dist/chunk-B76BAJ25.js +0 -624
  59. package/dist/chunk-NKGJEYZM.js +0 -1833
  60. package/dist/chunk-ONDBQSMU.js +0 -373
  61. package/dist/submission-input-JDwq3EVK.d.ts +0 -4986
package/dist/authoring.js CHANGED
@@ -1,55 +1,352 @@
1
1
  import {
2
- authoringKey,
3
- defineEngagementReward,
4
- defineLeaderboard,
5
- defineReferral,
6
2
  formatUnits,
7
3
  parseUnits
8
- } from "./chunk-B76BAJ25.js";
4
+ } from "./chunk-V7UKZLHA.js";
5
+ import {
6
+ CapabilityError,
7
+ capabilityErrorCodes
8
+ } from "./chunk-MC6V6VMG.js";
9
+ import {
10
+ sourceDataSchema,
11
+ sourceRoles,
12
+ validateQuestShape
13
+ } from "./chunk-GQKYAQM6.js";
9
14
  import {
10
- isEvmChainId
11
- } from "./chunk-ONDBQSMU.js";
15
+ cosmosDenomSchema,
16
+ cosmosNetworkSchema,
17
+ defineOffer,
18
+ engagementDefinitionSchema,
19
+ engagementScoreInputSchema,
20
+ engagementScoreSchema,
21
+ isEvmChainId,
22
+ leaderboardAuthoringSchema,
23
+ pointLedgerDeclarationSchema,
24
+ referralAuthoringSchema
25
+ } from "./chunk-36HTX2BM.js";
26
+ import "./chunk-NPGENI7K.js";
12
27
  import {
13
- admittedContentSchema,
28
+ authoringKey,
29
+ balanceAtLeast,
14
30
  budgetSchema,
15
- content,
31
+ completed,
32
+ contentMetricNameSchema,
16
33
  contentPerformancePolicySchema,
17
34
  contentPerformanceSnapshotSchema,
18
- customSource,
35
+ counterGoalSchema,
36
+ counterPolicySchema,
37
+ counterWindowSchema,
38
+ customSourceSchema,
19
39
  decisionSchema,
20
40
  defaultTriggerMode,
21
- defineTracker,
22
- discord,
23
- integrationRewardSchema,
41
+ defineCampaign,
42
+ defineGroup,
43
+ discordMessagesSourceSchema,
44
+ discordReactionsSourceSchema,
45
+ discordRewardSchema,
46
+ evaluatorIntegrationProviderSchema,
47
+ evaluatorProtocol,
48
+ identifier,
49
+ inGroup,
50
+ ledgerKey,
51
+ ledgerSettingSchema,
24
52
  parseSettings,
25
- perContent,
26
- pointLedgers,
27
53
  pointsRewardSchema,
28
54
  questDescriptionSchema,
29
55
  quizAnswersSchema,
56
+ quizContentSchema,
57
+ resourceHandle,
58
+ resourceSourceSchema,
30
59
  rewardBundleSchema,
60
+ schemaCheckedReader,
31
61
  settingFieldSchema,
32
- settings,
62
+ settingInstant,
63
+ settingResourceId,
64
+ sourceContent,
33
65
  sourceIdentities,
34
- streak,
66
+ streakPolicySchema,
35
67
  textOptionsSchema,
36
68
  tierRewardSchema,
37
69
  tieredRewardDefinitionSchema,
70
+ trackerDataIssue,
71
+ trackerDataSchema,
72
+ trackerDefinitionSchema,
38
73
  trackerTriggerContextSchema,
39
74
  trackerTriggerPolicySchema,
40
75
  urlOptionsSchema,
41
- x,
42
- xAccountRoles
43
- } from "./chunk-NKGJEYZM.js";
76
+ whereSchemaIssue,
77
+ whereSettings,
78
+ xSearchSourceSchema
79
+ } from "./chunk-JON5SESL.js";
44
80
 
45
- // src/authoring.ts
81
+ // src/builders.ts
46
82
  import { z } from "zod";
47
- var points = (balance, amount) => pointsRewardSchema.parse({ kind: "points", balance, amount });
48
- var discordRole = (roleId) => integrationRewardSchema.parse({
49
- kind: "integration",
83
+ function defineLedger(input) {
84
+ return resourceHandle("ledger", pointLedgerDeclarationSchema.parse(input));
85
+ }
86
+ function defineEngagementReward(input) {
87
+ const { target, ledger, ...config } = input;
88
+ const definition = engagementDefinitionSchema.parse({
89
+ ...config,
90
+ qualifiedBy: input.qualifiedBy.key,
91
+ balance: ledgerKey(ledger)
92
+ });
93
+ const selected = z.record(
94
+ z.enum(input.metrics),
95
+ engagementScoreSchema.shape.target
96
+ );
97
+ return {
98
+ key: definition.key,
99
+ describe: () => definition,
100
+ score(raw) {
101
+ const sample = engagementScoreInputSchema.parse(raw);
102
+ return engagementScoreSchema.parse({
103
+ target: target({ metrics: selected.parse(sample.metrics) }),
104
+ baseline: target({ metrics: selected.parse(sample.baseline) })
105
+ });
106
+ }
107
+ };
108
+ }
109
+ var streak = (options) => {
110
+ if ("timeZone" in options) {
111
+ throw new Error(
112
+ "streak() no longer takes a timeZone. Set the project timezone with calendar.timeZone in relay.json or in Console settings."
113
+ );
114
+ }
115
+ return streakPolicySchema.strict().parse({ kind: "streak", ...options });
116
+ };
117
+ var count = (count2) => counterGoalSchema.brand().parse({ kind: "count", count: count2 });
118
+ var calendar = (period, options = {}) => counterWindowSchema.brand().parse({ kind: "calendar", period, ...options });
119
+ var rolling = ({ days }) => counterWindowSchema.brand().parse({ kind: "rolling", durationMs: days * 864e5 });
120
+ var questCompleted = (quest) => ({
121
+ kind: "quest",
122
+ quest
123
+ });
124
+ function defineReferral(input) {
125
+ const { qualification, inviterGroup, milestones, bonuses, ...properties } = input;
126
+ const definition = referralAuthoringSchema.parse({
127
+ ...properties,
128
+ qualification: qualification.kind === "quest" ? { kind: "quest", quest: qualification.quest.key } : qualification,
129
+ ...inviterGroup ? { inviterGroup: identifier.parse(inviterGroup.key) } : {},
130
+ milestones: milestones?.map(({ key, ...rule }) => ({
131
+ ...rule,
132
+ id: authoringKey({ key, ...rule })
133
+ })),
134
+ bonuses: bonuses?.map(({ key, ledger, quests, ...rule }) => ({
135
+ ...rule,
136
+ id: authoringKey({ key, ...rule }),
137
+ balance: ledgerKey(ledger),
138
+ quests: quests?.map((quest) => quest.key)
139
+ }))
140
+ });
141
+ return resourceHandle("referral", definition);
142
+ }
143
+ function earned(options = {}) {
144
+ return { kind: "earned", ...options };
145
+ }
146
+ function defineLeaderboard(input) {
147
+ const { ledger, group, score, ...properties } = input;
148
+ const definition = leaderboardAuthoringSchema.parse({
149
+ ...properties,
150
+ balance: ledgerKey(ledger),
151
+ ...group ? { group: identifier.parse(group.key) } : {},
152
+ score: score?.kind === "earned" ? { ...score, quests: score.quests?.map((quest) => quest.key) } : score
153
+ });
154
+ return resourceHandle("leaderboard", definition);
155
+ }
156
+ var x = {
157
+ provider: "x",
158
+ /**
159
+ * Posts matching an X search. Each record's `data` is the provider's post payload. Its content
160
+ * is the post's normalized body (`XContentBody`), measured by the six X counters, so `content()`
161
+ * quests can use it.
162
+ */
163
+ search: (input) => xSearchSourceSchema.parse({ kind: "x-search", ...input })
164
+ };
165
+ function customSource(namespace, options) {
166
+ const { data, content: content2, ...rest } = options ?? {};
167
+ if (content2 !== void 0) {
168
+ if (data !== void 0) {
169
+ throw new Error(
170
+ `customSource("${namespace}") takes data or content, not both: a content source's records carry { body, metrics }`
171
+ );
172
+ }
173
+ if (!isZodSchema(content2.body)) {
174
+ throw new Error(
175
+ `customSource("${namespace}") needs a content body schema: customSource(namespace, { content: { body: z.object({ ... }), metrics: ["likes"] } })`
176
+ );
177
+ }
178
+ const metrics = [...content2.metrics ?? []];
179
+ for (const metric of metrics) {
180
+ contentMetricNameSchema.parse(metric);
181
+ }
182
+ return customSourceSchema.parse({
183
+ kind: "custom",
184
+ namespace,
185
+ ...rest,
186
+ data: trackerDataSchema(
187
+ z.object({
188
+ body: content2.body,
189
+ metrics: z.strictObject(
190
+ Object.fromEntries(
191
+ metrics.map((metric) => [
192
+ metric,
193
+ z.number().int().safe().nonnegative().optional()
194
+ ])
195
+ )
196
+ ).optional()
197
+ })
198
+ ),
199
+ content: { metrics }
200
+ });
201
+ }
202
+ if (!isZodSchema(data)) {
203
+ throw new Error(
204
+ `customSource("${namespace}") needs a data schema: customSource(namespace, { data: z.object({ ... }) }) describes each record's data`
205
+ );
206
+ }
207
+ return customSourceSchema.parse({
208
+ kind: "custom",
209
+ namespace,
210
+ ...rest,
211
+ data: trackerDataSchema(data)
212
+ });
213
+ }
214
+ function isZodSchema(value) {
215
+ return typeof value === "object" && value !== null && "_zod" in value;
216
+ }
217
+ function defineTracker(input) {
218
+ return resourceHandle(
219
+ "tracker",
220
+ trackerDefinitionSchema.parse(input)
221
+ );
222
+ }
223
+ function messages(options = {}) {
224
+ return discordMessagesSourceSchema.parse({
225
+ kind: "discord-messages",
226
+ ...options.text ? { text: true } : {}
227
+ });
228
+ }
229
+ function reactions() {
230
+ return discordReactionsSourceSchema.parse({ kind: "discord-reactions" });
231
+ }
232
+ var discord = {
50
233
  provider: "discord",
51
- action: "grant-role",
52
- roleId
234
+ grantRole: (roleId) => discordRewardSchema.parse({
235
+ kind: "integration",
236
+ provider: "discord",
237
+ action: "grant-role",
238
+ roleId
239
+ }),
240
+ messages,
241
+ reactions,
242
+ roles(options = {}) {
243
+ return resourceSourceSchema.parse({
244
+ provider: "discord",
245
+ resource: "role",
246
+ assignable: options.assignable ?? false
247
+ });
248
+ },
249
+ channels(options = {}) {
250
+ return resourceSourceSchema.parse({
251
+ provider: "discord",
252
+ resource: "channel",
253
+ types: options.types ?? ["text", "announcement"]
254
+ });
255
+ }
256
+ };
257
+ var pointLedgers = () => ({
258
+ provider: "domino",
259
+ resource: "point-ledger"
260
+ });
261
+ function setting(_kind, schema) {
262
+ return schema;
263
+ }
264
+ var settings = {
265
+ pointLedger(options) {
266
+ const field = {
267
+ ...options,
268
+ kind: "point-ledger",
269
+ default: options.default ? identifier.parse(options.default.key) : ""
270
+ };
271
+ return setting(
272
+ "point-ledger",
273
+ ledgerSettingSchema.prefault(field.default).meta(field)
274
+ );
275
+ },
276
+ datetime(options) {
277
+ const field = {
278
+ ...options,
279
+ kind: "datetime",
280
+ default: settingInstant.parse(options.default)
281
+ };
282
+ return setting(
283
+ "datetime",
284
+ settingInstant.default(field.default).meta(field)
285
+ );
286
+ },
287
+ resource(options) {
288
+ const field = {
289
+ ...options,
290
+ source: resourceSourceSchema.parse(options.source),
291
+ kind: "resource",
292
+ default: settingResourceId.parse(options.default ?? "")
293
+ };
294
+ return setting(
295
+ "resource",
296
+ settingResourceId.default(field.default).meta(field)
297
+ );
298
+ },
299
+ quiz(options) {
300
+ const value = quizContentSchema.parse(options.default);
301
+ return setting(
302
+ "quiz",
303
+ quizContentSchema.default(value).meta({ ...options, default: value, kind: "quiz" })
304
+ );
305
+ },
306
+ text(options) {
307
+ const field = {
308
+ ...options,
309
+ kind: "text",
310
+ minLength: options.minLength ?? 1,
311
+ maxLength: options.maxLength ?? 2e3,
312
+ multiline: options.multiline ?? false
313
+ };
314
+ return setting(
315
+ "text",
316
+ z.string().min(field.minLength).max(field.maxLength).default(field.default).meta(field)
317
+ );
318
+ },
319
+ integer(options) {
320
+ return setting(
321
+ "integer",
322
+ z.number().int().min(options.min).max(options.max).default(options.default).meta({ ...options, kind: "integer" })
323
+ );
324
+ },
325
+ boolean(options) {
326
+ return setting(
327
+ "boolean",
328
+ z.boolean().default(options.default).meta({ ...options, kind: "boolean" })
329
+ );
330
+ },
331
+ choice(options) {
332
+ return setting(
333
+ "choice",
334
+ z.enum(options.options).default(options.default).meta({ ...options, kind: "choice" })
335
+ );
336
+ },
337
+ /** A quest's settings: every field is built with one of the builders above. */
338
+ object(shape) {
339
+ return z.object(shape).strict();
340
+ }
341
+ };
342
+
343
+ // src/authoring.ts
344
+ import { z as z2 } from "zod";
345
+ import { z as z3 } from "zod";
346
+ var points = (ledger, amount) => pointsRewardSchema.parse({
347
+ kind: "points",
348
+ balance: ledgerKey(ledger),
349
+ amount
53
350
  });
54
351
  var evidence = {
55
352
  text: (options = {}) => ({
@@ -78,60 +375,129 @@ function manual(options) {
78
375
  ...options?.actor ? { actor: options.actor } : {}
79
376
  };
80
377
  }
378
+ var triggerDataSchemas = /* @__PURE__ */ new WeakMap();
379
+ var triggerContentMetrics = /* @__PURE__ */ new WeakMap();
380
+ function trackerTrigger(input, constructor) {
381
+ const {
382
+ tracker,
383
+ beneficiary = "author",
384
+ on,
385
+ where,
386
+ enrollment,
387
+ since,
388
+ historical,
389
+ content: content2
390
+ } = input;
391
+ if (!tracker?.source) {
392
+ throw new Error(
393
+ constructor === "automatic" ? "automatic() takes no argument, or { tracker } with a tracker handle from defineTracker" : "content() takes { tracker } with a tracker handle from defineTracker"
394
+ );
395
+ }
396
+ const { source } = tracker;
397
+ if (!sourceIdentities(source)) {
398
+ throw new Error(
399
+ 'Tracker triggers need attributable records: declare customSource(namespace, { identities: ["wallet"] }) or other identity kinds'
400
+ );
401
+ }
402
+ if (content2 && !sourceContent(source)) {
403
+ throw new Error(
404
+ `content() needs a tracker whose source has content: x.search(), or customSource(namespace, { content: { body, metrics } }). Tracker "${tracker.key}" has none; use automatic({ tracker }) instead.`
405
+ );
406
+ }
407
+ if (since === "enrollment" && enrollment !== "required") {
408
+ throw new Error(
409
+ `${constructor}() counts from enrollment only on a quest that requires it: add enrollment: "required"`
410
+ );
411
+ }
412
+ const roles = sourceRoles(source);
413
+ if (!roles.includes(beneficiary)) {
414
+ throw new Error(
415
+ source.kind === "custom" ? `Tracker "${tracker.key}" has no "${beneficiary}" account: declare it in customSource(namespace, { accounts: ["${beneficiary}"] })` : `Tracker "${tracker.key}" pays ${roles.map((role) => `"${role}"`).join(" or ")}, not "${beneficiary}"`
416
+ );
417
+ }
418
+ const policy = trackerTriggerPolicySchema.parse({
419
+ tracker: tracker.key,
420
+ source: source.kind,
421
+ // A content item is decided by its first observation; later observations run its stage.
422
+ on: content2 ? "new-record" : on ?? defaultTriggerMode(source.kind),
423
+ ...where ? { where } : {},
424
+ beneficiary,
425
+ // Defaults are left out too: "none", "activation", and "staff" ("claim" on content).
426
+ ...enrollment === "required" ? { enrollment } : {},
427
+ ...since && since !== "activation" ? { since } : {},
428
+ ...historical && historical !== (content2 ? "claim" : "staff") ? { historical } : {},
429
+ ...content2 ? { content: content2 } : {}
430
+ });
431
+ const schema = sourceDataSchema(source);
432
+ const issue = schema ? whereSchemaIssue(schema, policy.where) : null;
433
+ if (issue) {
434
+ throw new Error(issue);
435
+ }
436
+ const trigger = { kind: "automatic", tracker: policy };
437
+ if (schema) {
438
+ triggerDataSchemas.set(trigger, schema);
439
+ }
440
+ const declared = sourceContent(source);
441
+ if (declared) {
442
+ triggerContentMetrics.set(trigger, declared.metrics);
443
+ }
444
+ return trigger;
445
+ }
81
446
  function automatic(input) {
82
447
  if (!input) {
83
448
  return { kind: "automatic" };
84
449
  }
85
- if ("tracker" in input) {
86
- const { tracker, beneficiary = "author", on, where } = input;
87
- const { source } = tracker;
88
- if (source.kind === "x-search" && !xAccountRoles.includes(beneficiary)) {
89
- throw new Error(
90
- `X trackers pay "author" or "inReplyTo", not "${beneficiary}"`
91
- );
92
- }
93
- if (!sourceIdentities(source)) {
94
- throw new Error(
95
- 'Tracker triggers need attributable records: declare customSource(namespace, { identities: ["wallet"] }) or other identity kinds'
96
- );
97
- }
98
- if (source.kind === "custom" && beneficiary !== "author" && !source.accounts?.includes(beneficiary)) {
99
- throw new Error(
100
- `Tracker "${tracker.key}" has no "${beneficiary}" account: declare it in customSource(namespace, { accounts: ["${beneficiary}"] })`
101
- );
102
- }
103
- return {
104
- kind: "automatic",
105
- tracker: trackerTriggerPolicySchema.parse({
106
- tracker: tracker.key,
107
- source: source.kind,
108
- // The source's default mode is left out, so default quests keep the policy shape
109
- // earlier SDKs and servers read.
110
- ...on && on !== defaultTriggerMode(source.kind) ? { on } : {},
111
- ...where ? { where } : {},
112
- beneficiary
113
- })
114
- };
450
+ return trackerTrigger(input, "automatic");
451
+ }
452
+ function content(input) {
453
+ if (input && typeof input === "object" && "source" in input && "key" in input && !("tracker" in input)) {
454
+ throw new Error(
455
+ "content() takes { tracker }: write content({ tracker: posts }) instead of content(posts)"
456
+ );
115
457
  }
116
- return { kind: "automatic", observation: input };
458
+ return trackerTrigger({ ...input, content: true }, "content");
117
459
  }
118
- var budget = (definition) => budgetSchema.parse(definition);
119
- var keyed = (key) => ({ kind: "keyed", key });
120
- var completed = (quest) => ({
121
- quest: typeof quest === "string" ? quest : authoringKey(quest)
460
+ var budget = (definition) => {
461
+ const { key: _, cost, ...properties } = definition;
462
+ return budgetSchema.parse({
463
+ ...properties,
464
+ id: authoringKey(definition),
465
+ cost: typeof cost === "number" ? cost : { balance: ledgerKey(cost.ledger) }
466
+ });
467
+ };
468
+ var keyed = (key) => ({
469
+ kind: "keyed",
470
+ key
122
471
  });
472
+ var progress = (definition) => {
473
+ const { distinct, ...policy } = definition;
474
+ return {
475
+ ...counterPolicySchema.parse({
476
+ kind: "progress",
477
+ window: { kind: "lifetime" },
478
+ ...policy
479
+ }),
480
+ distinct
481
+ };
482
+ };
123
483
  var untracked = () => ({ kind: "untracked" });
124
484
  var staffHandover = () => ({ kind: "staff-handover" });
485
+ var automaticFulfillment = () => ({ kind: "automatic" });
125
486
  function defineTieredReward(input) {
126
- const { key: _, id: __, ...properties } = input;
487
+ const { key: _, tiers, ...properties } = input;
127
488
  const key = authoringKey(input);
128
- const reward = tieredRewardDefinitionSchema.parse({ id: key, ...properties });
129
- return {
489
+ const reward = tieredRewardDefinitionSchema.parse({
490
+ id: key,
491
+ ...properties,
492
+ tiers: tiers.map(({ key: key2, ...tier }) => ({
493
+ ...tier,
494
+ id: authoringKey({ key: key2, ...tier })
495
+ }))
496
+ });
497
+ return resourceHandle("tiered-reward", {
130
498
  key: reward.id,
131
- /** @deprecated Authoring key alias. Use key. */
132
- id: reward.id,
133
499
  tier: (tier) => tierRewardSchema.parse({ kind: "tier", reward, tier })
134
- };
500
+ });
135
501
  }
136
502
  var once = () => ({ kind: "once" });
137
503
  function accept(options = {}) {
@@ -142,6 +508,11 @@ function review(options) {
142
508
  }
143
509
  var decision = {
144
510
  accept,
511
+ defer: (options) => ({ kind: "defer", ...options }),
512
+ progress: (options) => ({
513
+ kind: "progress",
514
+ ...options
515
+ }),
145
516
  reject: (options) => ({
146
517
  kind: "reject",
147
518
  ...options
@@ -152,7 +523,7 @@ var evaluators = {
152
523
  quiz(answerKey, options = {}) {
153
524
  const expected = quizAnswersSchema.parse(answerKey);
154
525
  const questions = Object.keys(expected);
155
- const minimum = z.number().int().min(1).max(questions.length).parse(options.minimumCorrect ?? questions.length);
526
+ const minimum = z3.number().int().min(1).max(questions.length).parse(options.minimumCorrect ?? questions.length);
156
527
  return async ({ input }) => {
157
528
  const complete = Object.keys(input.answers).length === questions.length && questions.every((question) => Object.hasOwn(input.answers, question));
158
529
  const correct = questions.filter(
@@ -175,206 +546,268 @@ var evaluators = {
175
546
  };
176
547
  }
177
548
  };
178
- function defineQuest(definition) {
179
- const key = authoringKey(definition);
180
- const contentTrigger = definition.trigger.kind === "automatic" && definition.trigger.content !== void 0;
181
- if (definition.performance && !contentTrigger) {
182
- throw new Error("Performance requires a content trigger");
183
- }
184
- const trackerTrigger = definition.trigger.kind === "automatic" && definition.trigger.tracker !== void 0;
185
- if (trackerTrigger && definition.completion.kind === "streak") {
186
- throw new Error("Tracker triggers complete once() or keyed()");
187
- }
188
- const contentCompletion = "perContent" in definition.completion;
189
- if (contentTrigger !== contentCompletion) {
190
- throw new Error(
191
- "Content triggers require perContent completion, and perContent requires a content trigger"
192
- );
193
- }
194
- if (definition.completion.kind === "streak" && (definition.trigger.kind !== "automatic" || !definition.trigger.observation)) {
195
- throw new Error("Streaks require an automatic observation trigger");
196
- }
197
- const bareAutomatic = definition.trigger.kind === "automatic" && !definition.trigger.observation && !definition.trigger.content && !definition.trigger.tracker;
198
- if (bareAutomatic && !definition.requires?.length) {
199
- throw new Error(
200
- "A bare automatic() quest needs at least one requires entry, such as requires: [completed(quest)]. Give it an observation, content or tracker trigger, or a prerequisite."
201
- );
202
- }
203
- if (bareAutomatic && definition.completion.kind === "keyed") {
204
- throw new Error(
205
- "A bare automatic() quest cannot use keyed() completion. Give it an observation, content or tracker trigger, or use once()."
206
- );
207
- }
208
- if (definition.trigger.kind === "manual" && definition.trigger.input !== "none" && !definition.evaluate) {
209
- throw new Error("Evidence quests require an evaluator");
210
- }
211
- const fields = Object.fromEntries(
212
- Object.entries(definition.settings.shape).map(([key2, schema]) => [
213
- key2,
214
- settingFieldSchema.parse(schema.meta())
215
- ])
216
- );
217
- const where = definition.trigger.kind === "automatic" ? definition.trigger.tracker?.where : void 0;
218
- for (const bound of where && !("equals" in where) ? [where.gt, where.gte, where.lt, where.lte] : []) {
219
- if (typeof bound === "object" && fields[bound.setting]?.kind !== "integer") {
549
+ function questBuilder(template) {
550
+ return function defineQuest2(definition) {
551
+ const key = authoringKey(definition);
552
+ const integrations = [
553
+ ...new Set(
554
+ (definition.integrations ?? []).map(
555
+ (module) => evaluatorIntegrationProviderSchema.parse(module.provider)
556
+ )
557
+ )
558
+ ];
559
+ const contentTrigger = definition.trigger.kind === "automatic" && definition.trigger.tracker?.content === true;
560
+ const trackerTrigger2 = definition.trigger.kind === "automatic" && definition.trigger.tracker !== void 0;
561
+ const completion = definition.completion;
562
+ if (contentTrigger && completion !== void 0) {
220
563
  throw new Error(
221
- `The tracker filter compares with setting "${bound.setting}", which is not an integer setting`
564
+ "A content quest completes once per content item: remove its completion"
222
565
  );
223
566
  }
224
- }
225
- function describe(input) {
226
- const settings2 = definition.settings.parse(input);
227
- const values = parseSettings(fields, settings2);
228
- const rewards = rewardBundleSchema.parse(
229
- typeof definition.rewards === "function" ? definition.rewards({ settings: settings2 }) : definition.rewards
230
- );
231
- return questDescriptionSchema.parse({
232
- quest: key,
233
- runtimeVersion: 2,
234
- title: typeof definition.title === "function" ? definition.title({ settings: settings2 }) : definition.title,
235
- presentation: definition.presentation?.({ settings: settings2 }) ?? {},
236
- fields,
237
- values,
238
- rewards,
239
- humanReview: definition.review?.({ settings: settings2 })?.enabled ?? true,
240
- trigger: definition.trigger,
241
- ...definition.performance ? {
242
- performance: contentPerformancePolicySchema.parse({
567
+ if (!contentTrigger && completion === void 0) {
568
+ throw new Error(
569
+ "A quest needs a completion, such as once(), keyed(...), streak(...) or progress(...)"
570
+ );
571
+ }
572
+ validateQuestShape(
573
+ {
574
+ trigger: definition.trigger,
575
+ completion: completion ?? { kind: "keyed" },
576
+ requires: definition.requires ?? [],
577
+ performance: definition.performance ? contentPerformancePolicySchema.parse({
243
578
  metrics: definition.performance.metrics,
244
579
  maxAgeMs: definition.performance.maxAgeMs
245
- })
246
- } : {},
247
- integrations: [
248
- .../* @__PURE__ */ new Set([
249
- ...definition.integrations ?? [],
250
- ...rewards.flatMap(
251
- (reward) => reward.kind === "integration" ? [reward.provider] : []
252
- ),
253
- ...Object.values(fields).flatMap(
254
- (field) => field.kind === "resource" && field.source.provider !== "domino" ? [field.source.provider] : []
255
- )
256
- ])
257
- ],
258
- completion: definition.completion.kind === "streak" ? definition.completion : { kind: definition.completion.kind },
259
- visibility: definition.visibility ?? "visible",
260
- // Only when set: a release without the key is hidden and stays readable by older clients.
261
- ...definition.profile ? { profile: definition.profile } : {},
262
- budgets: definition.budgets ?? [],
263
- requires: definition.requires ?? []
264
- });
265
- }
266
- return {
267
- key,
268
- /** @deprecated Authoring key alias. Use key. */
269
- id: key,
270
- describe,
271
- async evaluate(ctx) {
272
- const settings2 = definition.settings.parse(ctx.settings);
273
- const tracker = trackerTrigger ? trackerTriggerContextSchema.parse(ctx.tracker) : null;
274
- if (ctx.performance) {
275
- if (!definition.performance) {
276
- throw new Error("Quest has no performance evaluator");
580
+ }) : void 0
581
+ },
582
+ { template }
583
+ );
584
+ if (definition.performance) {
585
+ const declared = triggerContentMetrics.get(definition.trigger) ?? [];
586
+ const unknown = definition.performance.metrics.filter(
587
+ (metric) => !declared.includes(metric)
588
+ );
589
+ if (unknown.length) {
590
+ throw new Error(
591
+ `The tracker's source does not measure ${unknown.join(", ")}`
592
+ );
593
+ }
594
+ }
595
+ if (definition.trigger.kind === "manual" && definition.trigger.input !== "none" && !definition.evaluate) {
596
+ throw new Error("Evidence quests require an evaluator");
597
+ }
598
+ const fields = Object.fromEntries(
599
+ Object.entries(definition.settings.shape).map(([key2, schema]) => {
600
+ const field = settingFieldSchema.safeParse(schema.meta());
601
+ if (!field.success) {
602
+ throw new Error(
603
+ `Setting "${key2}" is not a quest setting: build it with a settings builder, such as settings.integer({ label, default, min, max })`
604
+ );
277
605
  }
278
- const sample = contentPerformanceSnapshotSchema.parse(ctx.performance);
279
- const metrics = z.record(
280
- z.enum(definition.performance.metrics),
281
- z.number().int().safe().nonnegative()
282
- ).parse(sample.metrics);
283
- const result2 = await definition.performance.evaluate({
606
+ return [key2, field.data];
607
+ })
608
+ );
609
+ for (const setting2 of whereSettings(
610
+ definition.trigger.kind === "automatic" ? definition.trigger.tracker?.where : void 0
611
+ )) {
612
+ if (fields[setting2]?.kind !== "integer") {
613
+ throw new Error(
614
+ `The tracker filter compares with setting "${setting2}", which is not an integer setting`
615
+ );
616
+ }
617
+ }
618
+ function describe(input) {
619
+ const settings2 = definition.settings.parse(input);
620
+ const values = parseSettings(fields, settings2);
621
+ const rewards = rewardBundleSchema.parse(
622
+ typeof definition.rewards === "function" ? definition.rewards({ settings: settings2 }) : definition.rewards
623
+ );
624
+ const required = [
625
+ ...rewards.flatMap(
626
+ (reward) => reward.kind === "integration" ? [reward.provider] : []
627
+ ),
628
+ ...Object.values(fields).flatMap(
629
+ (field) => field.kind === "resource" && field.source.provider !== "domino" ? [field.source.provider] : []
630
+ )
631
+ ];
632
+ for (const provider of required) {
633
+ if (!integrations.includes(provider)) {
634
+ throw new Error(`Declare the ${provider} integration on this quest`);
635
+ }
636
+ }
637
+ return questDescriptionSchema.parse({
638
+ quest: key,
639
+ evaluatorProtocol,
640
+ title: typeof definition.title === "function" ? definition.title({ settings: settings2 }) : definition.title,
641
+ presentation: definition.presentation?.({ settings: settings2 }) ?? {},
642
+ fields,
643
+ values,
644
+ rewards,
645
+ humanReview: definition.review?.({ settings: settings2 })?.enabled ?? true,
646
+ trigger: definition.trigger,
647
+ ...definition.performance ? {
648
+ performance: contentPerformancePolicySchema.parse({
649
+ metrics: definition.performance.metrics,
650
+ maxAgeMs: definition.performance.maxAgeMs
651
+ })
652
+ } : {},
653
+ integrations,
654
+ // A content quest's completion is keyed by its content item, with a key Relay owns.
655
+ completion: !completion ? { kind: "keyed" } : completion.kind === "progress" ? {
656
+ kind: completion.kind,
657
+ goal: completion.goal,
658
+ window: completion.window,
659
+ repeat: completion.repeat
660
+ } : completion.kind === "streak" ? completion : { kind: completion.kind },
661
+ visibility: definition.visibility ?? "visible",
662
+ profile: definition.profile ?? { visible: false },
663
+ budgets: definition.budgets ?? [],
664
+ requires: definition.requires ?? [],
665
+ ...definition.campaign ? { campaign: definition.campaign } : {},
666
+ ...definition.availability ? { availability: definition.availability } : {}
667
+ });
668
+ }
669
+ return resourceHandle("quest", {
670
+ protocol: evaluatorProtocol,
671
+ key,
672
+ describe,
673
+ async evaluate(ctx) {
674
+ const settings2 = definition.settings.parse(ctx.settings);
675
+ const pinned = trackerTrigger2 ? trackerTriggerContextSchema.parse(ctx.tracker) : null;
676
+ const schema = triggerDataSchemas.get(definition.trigger);
677
+ const dataIssue = pinned && schema ? trackerDataIssue(schema, pinned.record.data) : null;
678
+ if (dataIssue) {
679
+ return decisionSchema.parse({
680
+ kind: "reject",
681
+ reason: `The record's data does not match the data schema this quest was built with: ${dataIssue.path} ${dataIssue.reason}`
682
+ });
683
+ }
684
+ const tracker = pinned;
685
+ if (ctx.performance) {
686
+ if (!definition.performance) {
687
+ throw new Error("Quest has no performance evaluator");
688
+ }
689
+ const sample = contentPerformanceSnapshotSchema.parse(ctx.performance);
690
+ const metrics = z3.record(
691
+ z3.enum(definition.performance.metrics),
692
+ z3.number().int().safe().nonnegative()
693
+ ).parse(sample.metrics);
694
+ const result2 = await definition.performance.evaluate({
695
+ settings: settings2,
696
+ member: ctx.member,
697
+ tracker,
698
+ metrics,
699
+ observedAt: sample.observedAt
700
+ });
701
+ if (!result2) {
702
+ throw new Error("Performance evaluator returned no decision");
703
+ }
704
+ if (result2.rewards !== void 0) {
705
+ throw new Error(
706
+ "A performance evaluator cannot select rewards. It gates the rewards content evaluation selected: return accept, review or reject with a reason only."
707
+ );
708
+ }
709
+ return decisionSchema.parse(
710
+ result2.kind === "accept" ? { kind: "accept", rewards: sample.rewards } : result2.kind === "review" && ctx.humanReview ? {
711
+ kind: "review",
712
+ reason: result2.reason,
713
+ rewards: sample.rewards
714
+ } : {
715
+ kind: "reject",
716
+ reason: result2.reason,
717
+ ...result2.kind === "review" ? { requestedReview: true } : {}
718
+ }
719
+ );
720
+ }
721
+ const result = definition.evaluate ? await definition.evaluate({
284
722
  settings: settings2,
285
723
  member: ctx.member,
286
- content: admittedContentSchema.parse(ctx.content),
287
- metrics,
288
- observedAt: sample.observedAt
289
- });
290
- if (!result2) {
291
- throw new Error("Performance evaluator returned no decision");
724
+ input: ctx.input,
725
+ tracker,
726
+ eligibleAt: ctx.eligibleAt,
727
+ calendar: ctx.calendar,
728
+ now: ctx.now,
729
+ review: ctx.review,
730
+ trackers: schemaCheckedReader(ctx.trackers),
731
+ wallet: ctx.wallet,
732
+ images: ctx.images,
733
+ // The declared module providers select these keys; unrelated host readers are omitted.
734
+ ...Object.fromEntries(
735
+ integrations.map((provider) => [provider, ctx[provider]])
736
+ )
737
+ }) : decision.accept();
738
+ if (!result) {
739
+ throw new Error("Evaluator returned no decision");
292
740
  }
293
- if (result2.rewards !== void 0) {
294
- throw new Error(
295
- "A performance evaluator cannot select rewards. It gates the rewards content evaluation selected: return accept, review or reject with a reason only."
296
- );
741
+ if (result.kind === "review" && !ctx.humanReview) {
742
+ return decisionSchema.parse({
743
+ kind: "reject",
744
+ reason: result.reason,
745
+ requestedReview: true
746
+ });
297
747
  }
298
748
  return decisionSchema.parse(
299
- result2.kind === "accept" ? {
300
- kind: "accept",
301
- rewards: sample.rewards,
302
- completionKey: admittedContentSchema.parse(ctx.content).completionKey
303
- } : result2.kind === "review" && ctx.humanReview ? {
304
- kind: "review",
305
- reason: result2.reason,
306
- rewards: sample.rewards
307
- } : { kind: "reject", reason: result2.reason }
749
+ result.kind === "accept" || result.kind === "review" ? {
750
+ ...result,
751
+ rewards: result.rewards ?? ctx.rewards,
752
+ ...completion?.kind === "keyed" || completion?.kind === "progress" ? {
753
+ completionKey: completion.kind === "progress" ? completion.distinct({ ...ctx, tracker: pinned }) : result.completionKey ?? completion.key({ ...ctx, tracker: pinned })
754
+ } : {}
755
+ } : result
308
756
  );
309
757
  }
310
- const result = definition.evaluate ? await definition.evaluate({
311
- settings: settings2,
312
- member: ctx.member,
313
- input: ctx.input,
314
- content: contentTrigger ? admittedContentSchema.parse(ctx.content) : null,
315
- observation: ctx.observation ?? null,
316
- tracker,
317
- eligibleAt: ctx.eligibleAt,
318
- review: ctx.review,
319
- trackers: ctx.trackers,
320
- wallet: ctx.wallet,
321
- x: ctx.x,
322
- images: ctx.images,
323
- discord: ctx.discord
324
- }) : decision.accept();
325
- if (!result) {
326
- throw new Error("Evaluator returned no decision");
327
- }
328
- if (result.kind === "review" && !ctx.humanReview) {
329
- return decisionSchema.parse({ kind: "reject", reason: result.reason });
330
- }
331
- return decisionSchema.parse(
332
- result.kind === "accept" || result.kind === "review" ? {
333
- ...result,
334
- rewards: result.rewards ?? ctx.rewards,
335
- ...definition.completion.kind === "keyed" ? {
336
- completionKey: definition.completion.key({
337
- ...ctx,
338
- tracker
339
- })
340
- } : {}
341
- } : result
342
- );
343
- }
758
+ });
344
759
  };
345
760
  }
346
- var defineQuestType = defineQuest;
761
+ var defineQuest = questBuilder(false);
762
+ var defineQuestType = questBuilder(true);
347
763
  export {
764
+ CapabilityError,
348
765
  automatic,
766
+ automaticFulfillment,
767
+ balanceAtLeast,
349
768
  budget,
769
+ calendar,
770
+ capabilityErrorCodes,
350
771
  completed,
351
772
  content,
773
+ cosmosDenomSchema,
774
+ cosmosNetworkSchema,
775
+ count,
352
776
  customSource,
353
777
  decision,
778
+ defineCampaign,
354
779
  defineEngagementReward,
780
+ defineGroup,
355
781
  defineLeaderboard,
782
+ defineLedger,
783
+ defineOffer,
356
784
  defineQuest,
357
785
  defineQuestType,
358
786
  defineReferral,
359
787
  defineTieredReward,
360
788
  defineTracker,
361
789
  discord,
362
- discordRole,
790
+ earned,
363
791
  evaluators,
364
792
  evidence,
365
793
  formatUnits,
794
+ inGroup,
366
795
  isEvmChainId,
367
796
  keyed,
368
797
  manual,
369
798
  once,
370
799
  parseUnits,
371
- perContent,
372
800
  pointLedgers,
373
801
  points,
802
+ progress,
803
+ questCompleted,
804
+ rolling,
374
805
  settings,
375
806
  staffHandover,
376
807
  streak,
808
+ trackerDataSchema,
377
809
  trackerTriggerContextSchema,
378
810
  untracked,
379
- x
811
+ x,
812
+ z2 as z
380
813
  };