@domino-sdk/relay 1.0.0-next.8 → 1.0.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 (59) hide show
  1. package/README.md +1 -1
  2. package/dist/activity-DeKJsRry.d.ts +9112 -0
  3. package/dist/authoring.d.ts +1135 -285
  4. package/dist/authoring.js +545 -269
  5. package/dist/automatic-outcomes-B1xrETxm.d.ts +758 -0
  6. package/dist/avatar.d.ts +2 -2
  7. package/dist/browser-DFe7sWSN.d.ts +4154 -0
  8. package/dist/browser.d.ts +11 -2528
  9. package/dist/browser.js +8 -6
  10. package/dist/capability-error-DNMwKmlL.d.ts +458 -0
  11. package/dist/{chunk-V7EZUZ3Z.js → chunk-36HTX2BM.js} +389 -169
  12. package/dist/chunk-6QJ3LADK.js +112 -0
  13. package/dist/{chunk-77H7APVD.js → chunk-BGGKNIRP.js} +1 -1
  14. package/dist/chunk-EYH5RDIE.js +496 -0
  15. package/dist/chunk-GQKYAQM6.js +84 -0
  16. package/dist/chunk-GUP7YRR4.js +42 -0
  17. package/dist/{chunk-V5R2DMGY.js → chunk-GZ6FJAXN.js} +380 -128
  18. package/dist/chunk-JON5SESL.js +2986 -0
  19. package/dist/chunk-MC6V6VMG.js +49 -0
  20. package/dist/chunk-NPGENI7K.js +37 -0
  21. package/dist/chunk-VFHHLN6A.js +284 -0
  22. package/dist/chunk-WJSSJ7AF.js +151 -0
  23. package/dist/{chunk-QBOHLT7M.js → chunk-XFMSUPNS.js} +1 -1
  24. package/dist/{chunk-6A6K5RUD.js → chunk-ZVIRG24J.js} +145 -42
  25. package/dist/{client-CK4nwt7V.d.ts → client-XiuFnug_.d.ts} +2193 -562
  26. package/dist/{submission-input-C9qm9ixd.d.ts → groups-Ck-OxzjD.d.ts} +919 -1142
  27. package/dist/identity-integration-GxAHdKR2.d.ts +58 -0
  28. package/dist/{images--vZ9U0kr.d.ts → images-Dv-P5VBw.d.ts} +2 -2
  29. package/dist/index.d.ts +13 -9
  30. package/dist/index.js +17 -6
  31. package/dist/internal.d.ts +82 -43
  32. package/dist/internal.js +58 -90
  33. package/dist/offers-Cy-e4o5x.d.ts +431 -0
  34. package/dist/{pages-BzqJuI69.d.ts → outcomes-kVB-Juzs.d.ts} +2386 -1490
  35. package/dist/pages-BBYD21IE.d.ts +359 -0
  36. package/dist/participant-rewards-3duLb8YJ.d.ts +668 -0
  37. package/dist/portal-proxy.js +22 -4
  38. package/dist/{profiles-DDMFBRKe.d.ts → profiles-D2aFcOxu.d.ts} +15 -15
  39. package/dist/react/index.d.ts +3122 -1011
  40. package/dist/react/index.js +1627 -779
  41. package/dist/request-error-DWduJCGV.d.ts +62 -0
  42. package/dist/schema-DfJzH2ci.d.ts +5644 -0
  43. package/dist/schema.d.ts +15 -4216
  44. package/dist/schema.js +322 -81
  45. package/dist/server/index.d.ts +81498 -0
  46. package/dist/server/index.js +2520 -0
  47. package/dist/{streaks-CwGReWGt.d.ts → streaks-CWyq8Px5.d.ts} +2 -4
  48. package/dist/submission-input-BZkm6ohU.d.ts +290 -0
  49. package/dist/vite.d.ts +1 -1
  50. package/dist/vite.js +1 -0
  51. package/dist/{wallet-BGOfFkG9.d.ts → wallet-YssWxGg8.d.ts} +35 -38
  52. package/package.json +8 -2
  53. package/dist/chunk-MRIURJE3.js +0 -257
  54. package/dist/chunk-VARGF4JS.js +0 -1733
  55. package/dist/chunk-ZWY3R6B3.js +0 -26
  56. package/dist/collection-BGneJ6Bk.d.ts +0 -114
  57. package/dist/integrations-C3Sp558f.d.ts +0 -1297
  58. package/dist/point-ledgers-iS00Z_v0.d.ts +0 -37
  59. package/dist/schema-pnuWqvGn.d.ts +0 -3396
package/dist/authoring.js CHANGED
@@ -3,80 +3,92 @@ import {
3
3
  parseUnits
4
4
  } from "./chunk-V7UKZLHA.js";
5
5
  import {
6
- authoringKey,
6
+ CapabilityError,
7
+ capabilityErrorCodes
8
+ } from "./chunk-MC6V6VMG.js";
9
+ import {
10
+ sourceDataSchema,
11
+ sourceRoles,
12
+ validateQuestShape
13
+ } from "./chunk-GQKYAQM6.js";
14
+ import {
15
+ cosmosDenomSchema,
16
+ cosmosNetworkSchema,
17
+ defineOffer,
7
18
  engagementDefinitionSchema,
8
19
  engagementScoreInputSchema,
9
20
  engagementScoreSchema,
10
21
  isEvmChainId,
11
22
  leaderboardAuthoringSchema,
23
+ pointLedgerDeclarationSchema,
12
24
  referralAuthoringSchema
13
- } from "./chunk-V7EZUZ3Z.js";
25
+ } from "./chunk-36HTX2BM.js";
26
+ import "./chunk-NPGENI7K.js";
14
27
  import {
15
- admittedContentSchema,
28
+ authoringKey,
29
+ balanceAtLeast,
16
30
  budgetSchema,
31
+ completed,
32
+ contentMetricNameSchema,
17
33
  contentPerformancePolicySchema,
18
34
  contentPerformanceSnapshotSchema,
19
- contentPolicySchema,
35
+ counterGoalSchema,
36
+ counterPolicySchema,
37
+ counterWindowSchema,
20
38
  customSourceSchema,
21
39
  decisionSchema,
22
40
  defaultTriggerMode,
41
+ defineCampaign,
42
+ defineGroup,
43
+ discordMessagesSourceSchema,
44
+ discordReactionsSourceSchema,
45
+ discordRewardSchema,
46
+ evaluatorIntegrationProviderSchema,
47
+ evaluatorProtocol,
23
48
  identifier,
24
- integrationRewardSchema,
49
+ inGroup,
50
+ ledgerKey,
51
+ ledgerSettingSchema,
25
52
  parseSettings,
26
53
  pointsRewardSchema,
27
54
  questDescriptionSchema,
28
55
  quizAnswersSchema,
29
56
  quizContentSchema,
57
+ resourceHandle,
30
58
  resourceSourceSchema,
31
59
  rewardBundleSchema,
60
+ schemaCheckedReader,
32
61
  settingFieldSchema,
33
62
  settingInstant,
34
63
  settingResourceId,
64
+ sourceContent,
35
65
  sourceIdentities,
36
66
  streakPolicySchema,
37
67
  textOptionsSchema,
38
68
  tierRewardSchema,
39
69
  tieredRewardDefinitionSchema,
70
+ trackerDataIssue,
71
+ trackerDataSchema,
40
72
  trackerDefinitionSchema,
41
73
  trackerTriggerContextSchema,
42
74
  trackerTriggerPolicySchema,
43
75
  urlOptionsSchema,
44
- xAccountRoles,
76
+ whereSchemaIssue,
77
+ whereSettings,
45
78
  xSearchSourceSchema
46
- } from "./chunk-VARGF4JS.js";
79
+ } from "./chunk-JON5SESL.js";
47
80
 
48
81
  // src/builders.ts
49
82
  import { z } from "zod";
50
- function content(tracker, policy = {}) {
51
- if (tracker.source.kind !== "x-search") {
52
- throw new Error("Content quests require an X tracker");
53
- }
54
- return {
55
- kind: "automatic",
56
- content: contentPolicySchema.parse({
57
- tracker: tracker.key,
58
- window: tracker.window,
59
- ...policy
60
- })
61
- };
62
- }
63
- function perContent() {
64
- return {
65
- kind: "keyed",
66
- perContent: true,
67
- key: (ctx) => {
68
- if (!ctx.content) {
69
- throw new Error("perContent requires an admitted content item");
70
- }
71
- return ctx.content.completionKey;
72
- }
73
- };
83
+ function defineLedger(input) {
84
+ return resourceHandle("ledger", pointLedgerDeclarationSchema.parse(input));
74
85
  }
75
86
  function defineEngagementReward(input) {
76
- const { target, ...config } = input;
87
+ const { target, ledger, ...config } = input;
77
88
  const definition = engagementDefinitionSchema.parse({
78
89
  ...config,
79
- qualifiedBy: typeof input.qualifiedBy === "string" ? input.qualifiedBy : input.qualifiedBy.key
90
+ qualifiedBy: input.qualifiedBy.key,
91
+ balance: ledgerKey(ledger)
80
92
  });
81
93
  const selected = z.record(
82
94
  z.enum(input.metrics),
@@ -94,35 +106,139 @@ function defineEngagementReward(input) {
94
106
  }
95
107
  };
96
108
  }
97
- var streak = (options) => streakPolicySchema.parse({ kind: "streak", ...options });
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
+ });
98
124
  function defineReferral(input) {
99
- const definition = referralAuthoringSchema.parse(input);
100
- return {
101
- ...definition,
102
- /** @deprecated Use key. */
103
- id: definition.key
104
- };
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 };
105
145
  }
106
146
  function defineLeaderboard(input) {
107
- const definition = leaderboardAuthoringSchema.parse(input);
108
- return {
109
- ...definition,
110
- /** @deprecated Use key. */
111
- id: definition.key
112
- };
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);
113
155
  }
114
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
+ */
115
163
  search: (input) => xSearchSourceSchema.parse({ kind: "x-search", ...input })
116
164
  };
117
- var customSource = (namespace, options = {}) => customSourceSchema.parse({
118
- kind: "custom",
119
- namespace,
120
- ...options
121
- });
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
+ }
122
217
  function defineTracker(input) {
123
- return trackerDefinitionSchema.parse(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" });
124
231
  }
125
232
  var discord = {
233
+ provider: "discord",
234
+ grantRole: (roleId) => discordRewardSchema.parse({
235
+ kind: "integration",
236
+ provider: "discord",
237
+ action: "grant-role",
238
+ roleId
239
+ }),
240
+ messages,
241
+ reactions,
126
242
  roles(options = {}) {
127
243
  return resourceSourceSchema.parse({
128
244
  provider: "discord",
@@ -142,14 +258,20 @@ var pointLedgers = () => ({
142
258
  provider: "domino",
143
259
  resource: "point-ledger"
144
260
  });
261
+ function setting(_kind, schema) {
262
+ return schema;
263
+ }
145
264
  var settings = {
146
265
  pointLedger(options) {
147
266
  const field = {
148
267
  ...options,
149
268
  kind: "point-ledger",
150
- default: identifier.parse(options.default)
269
+ default: options.default ? identifier.parse(options.default.key) : ""
151
270
  };
152
- return identifier.default(field.default).meta(field);
271
+ return setting(
272
+ "point-ledger",
273
+ ledgerSettingSchema.prefault(field.default).meta(field)
274
+ );
153
275
  },
154
276
  datetime(options) {
155
277
  const field = {
@@ -157,7 +279,10 @@ var settings = {
157
279
  kind: "datetime",
158
280
  default: settingInstant.parse(options.default)
159
281
  };
160
- return settingInstant.default(field.default).meta(field);
282
+ return setting(
283
+ "datetime",
284
+ settingInstant.default(field.default).meta(field)
285
+ );
161
286
  },
162
287
  resource(options) {
163
288
  const field = {
@@ -166,11 +291,17 @@ var settings = {
166
291
  kind: "resource",
167
292
  default: settingResourceId.parse(options.default ?? "")
168
293
  };
169
- return settingResourceId.default(field.default).meta(field);
294
+ return setting(
295
+ "resource",
296
+ settingResourceId.default(field.default).meta(field)
297
+ );
170
298
  },
171
299
  quiz(options) {
172
300
  const value = quizContentSchema.parse(options.default);
173
- return quizContentSchema.default(value).meta({ ...options, default: value, kind: "quiz" });
301
+ return setting(
302
+ "quiz",
303
+ quizContentSchema.default(value).meta({ ...options, default: value, kind: "quiz" })
304
+ );
174
305
  },
175
306
  text(options) {
176
307
  const field = {
@@ -180,17 +311,30 @@ var settings = {
180
311
  maxLength: options.maxLength ?? 2e3,
181
312
  multiline: options.multiline ?? false
182
313
  };
183
- return z.string().min(field.minLength).max(field.maxLength).default(field.default).meta(field);
314
+ return setting(
315
+ "text",
316
+ z.string().min(field.minLength).max(field.maxLength).default(field.default).meta(field)
317
+ );
184
318
  },
185
319
  integer(options) {
186
- return z.number().int().min(options.min).max(options.max).default(options.default).meta({ ...options, kind: "integer" });
320
+ return setting(
321
+ "integer",
322
+ z.number().int().min(options.min).max(options.max).default(options.default).meta({ ...options, kind: "integer" })
323
+ );
187
324
  },
188
325
  boolean(options) {
189
- return z.boolean().default(options.default).meta({ ...options, kind: "boolean" });
326
+ return setting(
327
+ "boolean",
328
+ z.boolean().default(options.default).meta({ ...options, kind: "boolean" })
329
+ );
190
330
  },
191
331
  choice(options) {
192
- return z.enum(options.options).default(options.default).meta({ ...options, kind: "choice" });
332
+ return setting(
333
+ "choice",
334
+ z.enum(options.options).default(options.default).meta({ ...options, kind: "choice" })
335
+ );
193
336
  },
337
+ /** A quest's settings: every field is built with one of the builders above. */
194
338
  object(shape) {
195
339
  return z.object(shape).strict();
196
340
  }
@@ -198,12 +342,11 @@ var settings = {
198
342
 
199
343
  // src/authoring.ts
200
344
  import { z as z2 } from "zod";
201
- var points = (balance, amount) => pointsRewardSchema.parse({ kind: "points", balance, amount });
202
- var discordRole = (roleId) => integrationRewardSchema.parse({
203
- kind: "integration",
204
- provider: "discord",
205
- action: "grant-role",
206
- roleId
345
+ import { z as z3 } from "zod";
346
+ var points = (ledger, amount) => pointsRewardSchema.parse({
347
+ kind: "points",
348
+ balance: ledgerKey(ledger),
349
+ amount
207
350
  });
208
351
  var evidence = {
209
352
  text: (options = {}) => ({
@@ -232,60 +375,129 @@ function manual(options) {
232
375
  ...options?.actor ? { actor: options.actor } : {}
233
376
  };
234
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
+ }
235
446
  function automatic(input) {
236
447
  if (!input) {
237
448
  return { kind: "automatic" };
238
449
  }
239
- if ("tracker" in input) {
240
- const { tracker, beneficiary = "author", on, where } = input;
241
- const { source } = tracker;
242
- if (source.kind === "x-search" && !xAccountRoles.includes(beneficiary)) {
243
- throw new Error(
244
- `X trackers pay "author" or "inReplyTo", not "${beneficiary}"`
245
- );
246
- }
247
- if (!sourceIdentities(source)) {
248
- throw new Error(
249
- 'Tracker triggers need attributable records: declare customSource(namespace, { identities: ["wallet"] }) or other identity kinds'
250
- );
251
- }
252
- if (source.kind === "custom" && beneficiary !== "author" && !source.accounts?.includes(beneficiary)) {
253
- throw new Error(
254
- `Tracker "${tracker.key}" has no "${beneficiary}" account: declare it in customSource(namespace, { accounts: ["${beneficiary}"] })`
255
- );
256
- }
257
- return {
258
- kind: "automatic",
259
- tracker: trackerTriggerPolicySchema.parse({
260
- tracker: tracker.key,
261
- source: source.kind,
262
- // The source's default mode is left out, so default quests keep the policy shape
263
- // earlier SDKs and servers read.
264
- ...on && on !== defaultTriggerMode(source.kind) ? { on } : {},
265
- ...where ? { where } : {},
266
- beneficiary
267
- })
268
- };
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
+ );
269
457
  }
270
- return { kind: "automatic", observation: input };
458
+ return trackerTrigger({ ...input, content: true }, "content");
271
459
  }
272
- var budget = (definition) => budgetSchema.parse(definition);
273
- var keyed = (key) => ({ kind: "keyed", key });
274
- var completed = (quest) => ({
275
- 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
276
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
+ };
277
483
  var untracked = () => ({ kind: "untracked" });
278
484
  var staffHandover = () => ({ kind: "staff-handover" });
485
+ var automaticFulfillment = () => ({ kind: "automatic" });
279
486
  function defineTieredReward(input) {
280
- const { key: _, id: __, ...properties } = input;
487
+ const { key: _, tiers, ...properties } = input;
281
488
  const key = authoringKey(input);
282
- const reward = tieredRewardDefinitionSchema.parse({ id: key, ...properties });
283
- 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", {
284
498
  key: reward.id,
285
- /** @deprecated Authoring key alias. Use key. */
286
- id: reward.id,
287
499
  tier: (tier) => tierRewardSchema.parse({ kind: "tier", reward, tier })
288
- };
500
+ });
289
501
  }
290
502
  var once = () => ({ kind: "once" });
291
503
  function accept(options = {}) {
@@ -296,6 +508,11 @@ function review(options) {
296
508
  }
297
509
  var decision = {
298
510
  accept,
511
+ defer: (options) => ({ kind: "defer", ...options }),
512
+ progress: (options) => ({
513
+ kind: "progress",
514
+ ...options
515
+ }),
299
516
  reject: (options) => ({
300
517
  kind: "reject",
301
518
  ...options
@@ -306,7 +523,7 @@ var evaluators = {
306
523
  quiz(answerKey, options = {}) {
307
524
  const expected = quizAnswersSchema.parse(answerKey);
308
525
  const questions = Object.keys(expected);
309
- const minimum = z2.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);
310
527
  return async ({ input }) => {
311
528
  const complete = Object.keys(input.answers).length === questions.length && questions.every((question) => Object.hasOwn(input.answers, question));
312
529
  const correct = questions.filter(
@@ -329,209 +546,268 @@ var evaluators = {
329
546
  };
330
547
  }
331
548
  };
332
- function defineQuest(definition) {
333
- const key = authoringKey(definition);
334
- const contentTrigger = definition.trigger.kind === "automatic" && definition.trigger.content !== void 0;
335
- if (definition.performance && !contentTrigger) {
336
- throw new Error("Performance requires a content trigger");
337
- }
338
- const trackerTrigger = definition.trigger.kind === "automatic" && definition.trigger.tracker !== void 0;
339
- const contentCompletion = "perContent" in definition.completion;
340
- if (contentTrigger !== contentCompletion) {
341
- throw new Error(
342
- "Content triggers require perContent completion, and perContent requires a content trigger"
343
- );
344
- }
345
- const bareAutomatic = definition.trigger.kind === "automatic" && !definition.trigger.observation && !definition.trigger.content && !definition.trigger.tracker;
346
- if (bareAutomatic && !definition.requires?.length) {
347
- throw new Error(
348
- "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."
349
- );
350
- }
351
- if (bareAutomatic && definition.completion.kind !== "once") {
352
- throw new Error(
353
- `A bare automatic() quest cannot use ${definition.completion.kind}() completion. Give it a tracker or observation trigger, or use once().`
354
- );
355
- }
356
- if (definition.trigger.kind === "manual" && definition.trigger.input !== "none" && !definition.evaluate) {
357
- throw new Error("Evidence quests require an evaluator");
358
- }
359
- const fields = Object.fromEntries(
360
- Object.entries(definition.settings.shape).map(([key2, schema]) => [
361
- key2,
362
- settingFieldSchema.parse(schema.meta())
363
- ])
364
- );
365
- const where = definition.trigger.kind === "automatic" ? definition.trigger.tracker?.where : void 0;
366
- for (const bound of where && !("equals" in where) ? [where.gt, where.gte, where.lt, where.lte] : []) {
367
- 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) {
368
563
  throw new Error(
369
- `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"
370
565
  );
371
566
  }
372
- }
373
- function describe(input) {
374
- const settings2 = definition.settings.parse(input);
375
- const values = parseSettings(fields, settings2);
376
- const rewards = rewardBundleSchema.parse(
377
- typeof definition.rewards === "function" ? definition.rewards({ settings: settings2 }) : definition.rewards
378
- );
379
- return questDescriptionSchema.parse({
380
- quest: key,
381
- runtimeVersion: 2,
382
- title: typeof definition.title === "function" ? definition.title({ settings: settings2 }) : definition.title,
383
- presentation: definition.presentation?.({ settings: settings2 }) ?? {},
384
- fields,
385
- values,
386
- rewards,
387
- humanReview: definition.review?.({ settings: settings2 })?.enabled ?? true,
388
- trigger: definition.trigger,
389
- ...definition.performance ? {
390
- 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({
391
578
  metrics: definition.performance.metrics,
392
579
  maxAgeMs: definition.performance.maxAgeMs
393
- })
394
- } : {},
395
- integrations: [
396
- .../* @__PURE__ */ new Set([
397
- ...definition.integrations ?? [],
398
- ...rewards.flatMap(
399
- (reward) => reward.kind === "integration" ? [reward.provider] : []
400
- ),
401
- ...Object.values(fields).flatMap(
402
- (field) => field.kind === "resource" && field.source.provider !== "domino" ? [field.source.provider] : []
403
- )
404
- ])
405
- ],
406
- completion: definition.completion.kind === "streak" ? definition.completion : { kind: definition.completion.kind },
407
- visibility: definition.visibility ?? "visible",
408
- // Only when set: a release without the key is hidden and stays readable by older clients.
409
- ...definition.profile ? { profile: definition.profile } : {},
410
- budgets: definition.budgets ?? [],
411
- requires: definition.requires ?? []
412
- });
413
- }
414
- return {
415
- key,
416
- /** @deprecated Authoring key alias. Use key. */
417
- id: key,
418
- describe,
419
- async evaluate(ctx) {
420
- const settings2 = definition.settings.parse(ctx.settings);
421
- const tracker = trackerTrigger ? trackerTriggerContextSchema.parse(ctx.tracker) : null;
422
- if (ctx.performance) {
423
- if (!definition.performance) {
424
- 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
+ );
605
+ }
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`);
425
635
  }
426
- const sample = contentPerformanceSnapshotSchema.parse(ctx.performance);
427
- const metrics = z2.record(
428
- z2.enum(definition.performance.metrics),
429
- z2.number().int().safe().nonnegative()
430
- ).parse(sample.metrics);
431
- const result2 = await definition.performance.evaluate({
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({
432
722
  settings: settings2,
433
723
  member: ctx.member,
434
- content: admittedContentSchema.parse(ctx.content),
435
- metrics,
436
- observedAt: sample.observedAt
437
- });
438
- if (!result2) {
439
- 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");
440
740
  }
441
- if (result2.rewards !== void 0) {
442
- throw new Error(
443
- "A performance evaluator cannot select rewards. It gates the rewards content evaluation selected: return accept, review or reject with a reason only."
444
- );
741
+ if (result.kind === "review" && !ctx.humanReview) {
742
+ return decisionSchema.parse({
743
+ kind: "reject",
744
+ reason: result.reason,
745
+ requestedReview: true
746
+ });
445
747
  }
446
748
  return decisionSchema.parse(
447
- result2.kind === "accept" ? {
448
- kind: "accept",
449
- rewards: sample.rewards,
450
- completionKey: admittedContentSchema.parse(ctx.content).completionKey
451
- } : result2.kind === "review" && ctx.humanReview ? {
452
- kind: "review",
453
- reason: result2.reason,
454
- rewards: sample.rewards
455
- } : {
456
- kind: "reject",
457
- reason: result2.reason,
458
- ...result2.kind === "review" ? { requestedReview: true } : {}
459
- }
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
460
756
  );
461
757
  }
462
- const result = definition.evaluate ? await definition.evaluate({
463
- settings: settings2,
464
- member: ctx.member,
465
- input: ctx.input,
466
- content: contentTrigger ? admittedContentSchema.parse(ctx.content) : null,
467
- observation: ctx.observation ?? null,
468
- tracker,
469
- eligibleAt: ctx.eligibleAt,
470
- now: ctx.now,
471
- review: ctx.review,
472
- trackers: ctx.trackers,
473
- wallet: ctx.wallet,
474
- x: ctx.x,
475
- images: ctx.images,
476
- discord: ctx.discord
477
- }) : decision.accept();
478
- if (!result) {
479
- throw new Error("Evaluator returned no decision");
480
- }
481
- if (result.kind === "review" && !ctx.humanReview) {
482
- return decisionSchema.parse({
483
- kind: "reject",
484
- reason: result.reason,
485
- requestedReview: true
486
- });
487
- }
488
- return decisionSchema.parse(
489
- result.kind === "accept" || result.kind === "review" ? {
490
- ...result,
491
- rewards: result.rewards ?? ctx.rewards,
492
- ...definition.completion.kind === "keyed" ? {
493
- completionKey: definition.completion.key({
494
- ...ctx,
495
- tracker
496
- })
497
- } : {}
498
- } : result
499
- );
500
- }
758
+ });
501
759
  };
502
760
  }
503
- var defineQuestType = defineQuest;
761
+ var defineQuest = questBuilder(false);
762
+ var defineQuestType = questBuilder(true);
504
763
  export {
764
+ CapabilityError,
505
765
  automatic,
766
+ automaticFulfillment,
767
+ balanceAtLeast,
506
768
  budget,
769
+ calendar,
770
+ capabilityErrorCodes,
507
771
  completed,
508
772
  content,
773
+ cosmosDenomSchema,
774
+ cosmosNetworkSchema,
775
+ count,
509
776
  customSource,
510
777
  decision,
778
+ defineCampaign,
511
779
  defineEngagementReward,
780
+ defineGroup,
512
781
  defineLeaderboard,
782
+ defineLedger,
783
+ defineOffer,
513
784
  defineQuest,
514
785
  defineQuestType,
515
786
  defineReferral,
516
787
  defineTieredReward,
517
788
  defineTracker,
518
789
  discord,
519
- discordRole,
790
+ earned,
520
791
  evaluators,
521
792
  evidence,
522
793
  formatUnits,
794
+ inGroup,
523
795
  isEvmChainId,
524
796
  keyed,
525
797
  manual,
526
798
  once,
527
799
  parseUnits,
528
- perContent,
529
800
  pointLedgers,
530
801
  points,
802
+ progress,
803
+ questCompleted,
804
+ rolling,
531
805
  settings,
532
806
  staffHandover,
533
807
  streak,
808
+ trackerDataSchema,
534
809
  trackerTriggerContextSchema,
535
810
  untracked,
536
- x
811
+ x,
812
+ z2 as z
537
813
  };