@domino-sdk/relay 1.54.0 → 1.56.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.
- package/CHANGELOG.md +1859 -0
- package/dist/analytics-URV-mxjH.d.ts +482 -0
- package/dist/{attempt-captures-QC4kTA44.d.ts → attempt-captures-CxxO7DeD.d.ts} +1 -1
- package/dist/authoring.d.ts +277 -40
- package/dist/authoring.js +142 -14
- package/dist/{automatic-outcomes-CVyZHxir.d.ts → automatic-outcomes-DxW_v5Wc.d.ts} +5 -2
- package/dist/avatar.d.ts +1 -1
- package/dist/browser.d.ts +7 -7
- package/dist/browser.js +10 -10
- package/dist/{capability-error-FQGGb5xS.d.ts → capability-error-DSd8EMQC.d.ts} +76 -2
- package/dist/{chunk-RP4LLEON.js → chunk-ABJ4G47X.js} +1 -1
- package/dist/{chunk-IELNFM3V.js → chunk-BHHUSBSD.js} +97 -8
- package/dist/chunk-BJYMTCWG.js +950 -0
- package/dist/{chunk-K6TVMNU4.js → chunk-CK7HO32Z.js} +1 -1
- package/dist/{chunk-GTQJSDBS.js → chunk-CXKXPB7K.js} +32 -16
- package/dist/{chunk-TL5TL5RD.js → chunk-G6XVBYH7.js} +4 -4
- package/dist/{chunk-S2PMKZSX.js → chunk-H2MD6E7Z.js} +21 -2
- package/dist/{chunk-YVC3B72P.js → chunk-INSWI2VZ.js} +1 -1
- package/dist/{chunk-XFAYQFRO.js → chunk-KBVH4XZC.js} +1 -1
- package/dist/{chunk-MLIXJPKL.js → chunk-MHPKEVEW.js} +1 -0
- package/dist/{chunk-7BNCLWAO.js → chunk-MNU6UPXY.js} +1 -1
- package/dist/{chunk-JL7J3EQQ.js → chunk-NK6EER33.js} +46 -16
- package/dist/{chunk-QIDQWXSF.js → chunk-OTITLABX.js} +2 -2
- package/dist/{chunk-53JMTUWG.js → chunk-QNQNKIAA.js} +1 -1
- package/dist/{chunk-ZJ3FVZRP.js → chunk-RBPMT53S.js} +132 -99
- package/dist/chunk-ULQU7KC4.js +704 -0
- package/dist/chunk-VSHV7FEA.js +344 -0
- package/dist/{chunk-LPHXTZDA.js → chunk-W5U2WXG3.js} +2 -2
- package/dist/{client-CzNtl8UO.d.ts → client-bX3oTpRu.d.ts} +111 -8
- package/dist/{deployments-ovtqtAcx.d.ts → deployments-D3lxfQMB.d.ts} +1436 -185
- package/dist/{groups-B5R1RCsl.d.ts → groups-DPE004_V.d.ts} +135 -139
- package/dist/index.d.ts +8 -8
- package/dist/index.js +9 -9
- package/dist/integrations-CcD5NPEA.d.ts +633 -0
- package/dist/internal.d.ts +11 -10
- package/dist/internal.js +9 -9
- package/dist/{offers-CH1RZekg.d.ts → offers-CtzMeB7O.d.ts} +207 -2
- package/dist/{pages-BTlOg8sl.d.ts → pages-BlLqB8ZQ.d.ts} +82 -2
- package/dist/portal-proxy.d.ts +11 -1
- package/dist/portal-proxy.js +27 -6
- package/dist/{profiles-Cdv4BSd_.d.ts → profiles-BoOK03cR.d.ts} +2 -0
- package/dist/react/index.d.ts +566 -13
- package/dist/react/index.js +105 -15
- package/dist/runtime/authoring.js +1 -1
- package/dist/runtime/chunks/NDB42VFF.js +1 -0
- package/dist/runtime/manifest.json +3 -3
- package/dist/runtime/schema.js +1 -1
- package/dist/{schema-BJ9bOgpN.d.ts → schema-C4m9RhCR.d.ts} +166 -6
- package/dist/schema.d.ts +383 -79
- package/dist/schema.js +105 -41
- package/dist/server/index.d.ts +11 -10
- package/dist/server/index.js +1590 -1175
- package/dist/{submission-input-BNah53go.d.ts → submission-input-wTiYGdgA.d.ts} +2 -2
- package/dist/{telegram-browser-C36j_6v-.d.ts → telegram-browser-Bh3i0HiH.d.ts} +1 -1
- package/dist/testing.d.ts +85 -12
- package/dist/testing.js +322 -37
- package/dist/{transport-C8BlCzmF.d.ts → transport-CnKA-_RL.d.ts} +7625 -1265
- package/dist/vite.d.ts +54 -1
- package/dist/vite.js +35 -0
- package/dist/{x-BfQcsDp0.d.ts → x-1czZFVUW.d.ts} +1 -1
- package/package.json +3 -2
- package/dist/chunk-2NUD237O.js +0 -102
- package/dist/chunk-CCE2JWES.js +0 -1083
- package/dist/integrations-CllF-kWg.d.ts +0 -314
- package/dist/runtime/chunks/AVRVSLHX.js +0 -1
|
@@ -0,0 +1,704 @@
|
|
|
1
|
+
import {
|
|
2
|
+
identifier,
|
|
3
|
+
resourceHandle,
|
|
4
|
+
responseShape
|
|
5
|
+
} from "./chunk-BHHUSBSD.js";
|
|
6
|
+
|
|
7
|
+
// src/youtube.ts
|
|
8
|
+
import * as z from "zod";
|
|
9
|
+
var youtubeShortsNamespace = "youtube-shorts";
|
|
10
|
+
var youtubeShortBodySchema = z.object({
|
|
11
|
+
title: z.string(),
|
|
12
|
+
description: z.string(),
|
|
13
|
+
tags: z.array(z.string()),
|
|
14
|
+
durationSeconds: z.number().int().nonnegative(),
|
|
15
|
+
/** When the video was published, epoch milliseconds. */
|
|
16
|
+
publishedAt: z.number().int().nonnegative()
|
|
17
|
+
});
|
|
18
|
+
var youtubeShortMetrics = ["views", "likes", "comments"];
|
|
19
|
+
|
|
20
|
+
// src/tiktok.ts
|
|
21
|
+
import * as z2 from "zod";
|
|
22
|
+
var tiktokVideosNamespace = "tiktok-videos";
|
|
23
|
+
var tiktokVideoBodySchema = z2.object({
|
|
24
|
+
title: z2.string(),
|
|
25
|
+
/** The creator's caption, `video_description` in TikTok's API. */
|
|
26
|
+
description: z2.string(),
|
|
27
|
+
durationSeconds: z2.number().int().nonnegative(),
|
|
28
|
+
width: z2.number().int().nonnegative(),
|
|
29
|
+
height: z2.number().int().nonnegative(),
|
|
30
|
+
/** When the video was posted, epoch milliseconds. */
|
|
31
|
+
createdAt: z2.number().int().nonnegative(),
|
|
32
|
+
/** TikTok's shareable link to the video. */
|
|
33
|
+
shareUrl: z2.string(),
|
|
34
|
+
/** Whether the creator labelled the video as AI-generated content. */
|
|
35
|
+
isAigc: z2.boolean()
|
|
36
|
+
});
|
|
37
|
+
var tiktokVideoMetrics = [
|
|
38
|
+
"views",
|
|
39
|
+
"likes",
|
|
40
|
+
"comments",
|
|
41
|
+
"shares"
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
// src/server/analytics.ts
|
|
45
|
+
import * as z3 from "zod";
|
|
46
|
+
function once(build) {
|
|
47
|
+
let built;
|
|
48
|
+
return () => built ??= build();
|
|
49
|
+
}
|
|
50
|
+
function lazyShape(builders) {
|
|
51
|
+
const shape = {};
|
|
52
|
+
for (const [key, build] of Object.entries(builders)) {
|
|
53
|
+
Object.defineProperty(shape, key, {
|
|
54
|
+
get: once(build),
|
|
55
|
+
enumerable: true,
|
|
56
|
+
configurable: true
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return shape;
|
|
60
|
+
}
|
|
61
|
+
var analyticsRanges = ["7d", "30d", "90d", "all"];
|
|
62
|
+
var amount = once(() => z3.number().int().safe());
|
|
63
|
+
var amounts = once(() => z3.array(amount()));
|
|
64
|
+
var instant = once(() => z3.iso.datetime({ offset: true }));
|
|
65
|
+
var bucket = once(
|
|
66
|
+
() => z3.object({
|
|
67
|
+
/** The period's label in its time zone, such as `2026-10-08@Europe/Budapest` or `2026-W41@UTC`. */
|
|
68
|
+
key: z3.string(),
|
|
69
|
+
start: instant(),
|
|
70
|
+
end: instant()
|
|
71
|
+
})
|
|
72
|
+
);
|
|
73
|
+
var range = once(
|
|
74
|
+
() => z3.object({
|
|
75
|
+
key: z3.enum(analyticsRanges),
|
|
76
|
+
from: instant(),
|
|
77
|
+
/** Now, by the database's clock, when the read ran. */
|
|
78
|
+
to: instant(),
|
|
79
|
+
bucket: z3.enum(["day", "week"]),
|
|
80
|
+
/** The project calendar's time zone now. Buckets before a time zone change follow the old one. */
|
|
81
|
+
timeZone: z3.string(),
|
|
82
|
+
buckets: z3.array(bucket())
|
|
83
|
+
})
|
|
84
|
+
);
|
|
85
|
+
var previousRange = once(
|
|
86
|
+
() => z3.object({ from: instant(), to: instant() }).optional()
|
|
87
|
+
);
|
|
88
|
+
var analyticsQuerySchema = z3.strictObject(
|
|
89
|
+
lazyShape({
|
|
90
|
+
range: () => z3.enum(analyticsRanges),
|
|
91
|
+
/** A point ledger's key or ID. Defaults to the first ledger that is not archived. */
|
|
92
|
+
ledger: () => identifier.optional()
|
|
93
|
+
})
|
|
94
|
+
);
|
|
95
|
+
var metric = once(
|
|
96
|
+
() => z3.object({
|
|
97
|
+
total: amount(),
|
|
98
|
+
previousTotal: amount().optional(),
|
|
99
|
+
series: amounts()
|
|
100
|
+
})
|
|
101
|
+
);
|
|
102
|
+
var optionalMetric = once(() => metric().optional());
|
|
103
|
+
var quest = once(
|
|
104
|
+
() => z3.object({
|
|
105
|
+
quest: identifier,
|
|
106
|
+
title: z3.string(),
|
|
107
|
+
/** Attempts created in the range. */
|
|
108
|
+
attempts: amount(),
|
|
109
|
+
/** Completions in the range that were never revoked. */
|
|
110
|
+
completions: amount(),
|
|
111
|
+
/** Attempts decided in the range: accepted, progressed or rejected. */
|
|
112
|
+
decided: amount(),
|
|
113
|
+
/** Attempts rejected in the range. */
|
|
114
|
+
rejected: amount(),
|
|
115
|
+
/** Attempts created in the range that went to staff review. */
|
|
116
|
+
needsReview: amount(),
|
|
117
|
+
/** `rejected ÷ decided`, or null below 10 decided attempts. */
|
|
118
|
+
rejectionRate: z3.number().min(0).max(1).nullable(),
|
|
119
|
+
/**
|
|
120
|
+
* The 5 most common rejections, most frequent first. Rejections with a code (an evaluator's
|
|
121
|
+
* reject code or one Relay writes, such as `budget-exhausted`) group by it, and `reason` is the
|
|
122
|
+
* latest rejection's reason; rejections without one group by their exact reason.
|
|
123
|
+
*/
|
|
124
|
+
topReasons: z3.array(
|
|
125
|
+
z3.object({
|
|
126
|
+
reason: z3.string(),
|
|
127
|
+
code: z3.string().nullable(),
|
|
128
|
+
count: amount()
|
|
129
|
+
})
|
|
130
|
+
),
|
|
131
|
+
/** Rejections by who made them: the evaluator, staff, an exhausted budget, an expired deferral. */
|
|
132
|
+
rejectionsBySource: z3.object({
|
|
133
|
+
evaluator: amount(),
|
|
134
|
+
staff: amount(),
|
|
135
|
+
budget: amount(),
|
|
136
|
+
expired: amount()
|
|
137
|
+
}),
|
|
138
|
+
/** Attempts and completions per bucket. */
|
|
139
|
+
series: z3.object({ attempts: amounts(), completions: amounts() })
|
|
140
|
+
})
|
|
141
|
+
);
|
|
142
|
+
var analyticsReadSchema = z3.object(
|
|
143
|
+
lazyShape({
|
|
144
|
+
range,
|
|
145
|
+
previous: previousRange,
|
|
146
|
+
members: () => z3.object({
|
|
147
|
+
/** Members who joined the project. */
|
|
148
|
+
signUps: metric(),
|
|
149
|
+
/** Distinct members with an attempt; the total counts each member once over the range. */
|
|
150
|
+
active: metric()
|
|
151
|
+
}),
|
|
152
|
+
quests: () => z3.object({
|
|
153
|
+
attempts: metric(),
|
|
154
|
+
completions: metric(),
|
|
155
|
+
rejections: metric(),
|
|
156
|
+
needsReview: metric(),
|
|
157
|
+
/** Quests with activity in the range, most attempted first. */
|
|
158
|
+
byQuest: z3.array(quest())
|
|
159
|
+
}),
|
|
160
|
+
points: () => z3.object({
|
|
161
|
+
/** The project's point ledgers, oldest first. */
|
|
162
|
+
ledgers: z3.array(
|
|
163
|
+
z3.object({ key: identifier, name: z3.string(), archived: z3.boolean() })
|
|
164
|
+
),
|
|
165
|
+
/** The ledger this section reads, or null when the project has none to read. */
|
|
166
|
+
ledger: identifier.nullable(),
|
|
167
|
+
/** Whether the ledger has a spend entry. `spent` and `net` are present only when it does. */
|
|
168
|
+
hasSpending: z3.boolean(),
|
|
169
|
+
/** Points in award entries. */
|
|
170
|
+
awarded: metric(),
|
|
171
|
+
/** Points in spend entries, as a positive number. */
|
|
172
|
+
spent: optionalMetric(),
|
|
173
|
+
/** Every entry's signed amount: awards, spends, adjustments and reversals. */
|
|
174
|
+
net: optionalMetric(),
|
|
175
|
+
/** Distinct members awarded points; the total counts each member once over the range. */
|
|
176
|
+
membersEarning: metric()
|
|
177
|
+
}),
|
|
178
|
+
/** Fulfilled offer redemptions. Present only when the project has an offer. */
|
|
179
|
+
offers: () => z3.object({
|
|
180
|
+
redemptions: metric(),
|
|
181
|
+
/** Every offer, most redeemed first. */
|
|
182
|
+
byOffer: z3.array(
|
|
183
|
+
z3.object({
|
|
184
|
+
offer: identifier,
|
|
185
|
+
title: z3.string(),
|
|
186
|
+
redemptions: amount(),
|
|
187
|
+
series: amounts()
|
|
188
|
+
})
|
|
189
|
+
)
|
|
190
|
+
}).optional(),
|
|
191
|
+
/** Present only when the project has a referral program. */
|
|
192
|
+
referrals: () => z3.object({
|
|
193
|
+
/** Referrals accepted. */
|
|
194
|
+
accepted: metric(),
|
|
195
|
+
/** Active qualifications, by when they first qualified; suspended referrals still count. */
|
|
196
|
+
qualified: metric(),
|
|
197
|
+
/**
|
|
198
|
+
* Of the referrals accepted in the range (and in the range before), how many have an
|
|
199
|
+
* active qualification now, each counted once; suspended referrals still count.
|
|
200
|
+
* `acceptedQualified.total ÷ accepted.total` is the share of the range's friends who have
|
|
201
|
+
* qualified since, never above 1.
|
|
202
|
+
*/
|
|
203
|
+
acceptedQualified: z3.object({
|
|
204
|
+
total: amount(),
|
|
205
|
+
previousTotal: amount().optional()
|
|
206
|
+
})
|
|
207
|
+
}).optional(),
|
|
208
|
+
/** Present only in the test environment. */
|
|
209
|
+
testEnvironment: () => z3.object({
|
|
210
|
+
/** When a test member was last reset, deleting their history, or null. */
|
|
211
|
+
lastResetAt: instant().nullable(),
|
|
212
|
+
/** Test members whose clock is advanced, so their activity can be dated in the future. */
|
|
213
|
+
shiftedClockMembers: amount()
|
|
214
|
+
}).optional()
|
|
215
|
+
})
|
|
216
|
+
);
|
|
217
|
+
var analyticsBreakdowns = [
|
|
218
|
+
"quest",
|
|
219
|
+
"source",
|
|
220
|
+
"reason",
|
|
221
|
+
"offer",
|
|
222
|
+
"program",
|
|
223
|
+
"group",
|
|
224
|
+
"tracker",
|
|
225
|
+
"outcome",
|
|
226
|
+
"leaderboard"
|
|
227
|
+
];
|
|
228
|
+
var analyticsFilters = [
|
|
229
|
+
"quest",
|
|
230
|
+
"tracker",
|
|
231
|
+
"group",
|
|
232
|
+
"offer",
|
|
233
|
+
"program",
|
|
234
|
+
"leaderboard"
|
|
235
|
+
];
|
|
236
|
+
var analyticsCharts = ["number", "line", "bar", "table"];
|
|
237
|
+
var counts = ["number", "line", "bar", "table"];
|
|
238
|
+
var rates = ["number", "line", "table"];
|
|
239
|
+
var periodic = ["number", "bar", "table"];
|
|
240
|
+
var questFilters = ["quest", "group", "tracker"];
|
|
241
|
+
var analyticsCatalog = {
|
|
242
|
+
"members.signUps": {
|
|
243
|
+
definition: "Members who joined the project, by when they joined.",
|
|
244
|
+
kind: "count",
|
|
245
|
+
breakdowns: [],
|
|
246
|
+
filters: ["group"],
|
|
247
|
+
charts: counts,
|
|
248
|
+
ledger: false
|
|
249
|
+
},
|
|
250
|
+
"members.active": {
|
|
251
|
+
definition: "Distinct members with an attempt, by when it was created; the total counts each member once.",
|
|
252
|
+
kind: "distinct",
|
|
253
|
+
breakdowns: [],
|
|
254
|
+
filters: ["group"],
|
|
255
|
+
charts: counts,
|
|
256
|
+
ledger: false
|
|
257
|
+
},
|
|
258
|
+
"quests.attempts": {
|
|
259
|
+
definition: "Attempts, by when they were created.",
|
|
260
|
+
kind: "count",
|
|
261
|
+
breakdowns: ["quest"],
|
|
262
|
+
filters: questFilters,
|
|
263
|
+
charts: counts,
|
|
264
|
+
ledger: false
|
|
265
|
+
},
|
|
266
|
+
"quests.completions": {
|
|
267
|
+
definition: "Quest completions never revoked, by when they completed.",
|
|
268
|
+
kind: "count",
|
|
269
|
+
breakdowns: ["quest"],
|
|
270
|
+
filters: questFilters,
|
|
271
|
+
charts: counts,
|
|
272
|
+
ledger: false
|
|
273
|
+
},
|
|
274
|
+
"quests.needsReview": {
|
|
275
|
+
definition: "Attempts the evaluator sent to staff review, by when they were created, whatever staff decided later.",
|
|
276
|
+
kind: "count",
|
|
277
|
+
breakdowns: ["quest"],
|
|
278
|
+
filters: questFilters,
|
|
279
|
+
charts: counts,
|
|
280
|
+
ledger: false
|
|
281
|
+
},
|
|
282
|
+
"quests.rejections": {
|
|
283
|
+
definition: "Rejected attempts, by when they were decided.",
|
|
284
|
+
kind: "count",
|
|
285
|
+
breakdowns: ["quest", "source", "reason"],
|
|
286
|
+
filters: questFilters,
|
|
287
|
+
charts: counts,
|
|
288
|
+
ledger: false
|
|
289
|
+
},
|
|
290
|
+
"quests.rejectionRate": {
|
|
291
|
+
definition: "Rejected attempts divided by decided attempts (accepted, progressed or rejected), by when they were decided; empty below 10 decided.",
|
|
292
|
+
kind: "rate",
|
|
293
|
+
breakdowns: ["quest"],
|
|
294
|
+
filters: questFilters,
|
|
295
|
+
charts: rates,
|
|
296
|
+
ledger: false,
|
|
297
|
+
minimumDenominator: 10
|
|
298
|
+
},
|
|
299
|
+
"points.awarded": {
|
|
300
|
+
definition: "Points in the ledger's award entries, by effective date.",
|
|
301
|
+
kind: "count",
|
|
302
|
+
breakdowns: [],
|
|
303
|
+
filters: ["group"],
|
|
304
|
+
charts: counts,
|
|
305
|
+
ledger: true
|
|
306
|
+
},
|
|
307
|
+
"points.spent": {
|
|
308
|
+
definition: "Points in the ledger's spend entries, as a positive number, by effective date.",
|
|
309
|
+
kind: "count",
|
|
310
|
+
breakdowns: [],
|
|
311
|
+
filters: ["group"],
|
|
312
|
+
charts: counts,
|
|
313
|
+
ledger: true
|
|
314
|
+
},
|
|
315
|
+
"points.net": {
|
|
316
|
+
definition: "Every entry's signed amount in the ledger, by effective date; a reversal takes its original entry's date.",
|
|
317
|
+
kind: "count",
|
|
318
|
+
breakdowns: [],
|
|
319
|
+
filters: ["group"],
|
|
320
|
+
charts: counts,
|
|
321
|
+
ledger: true
|
|
322
|
+
},
|
|
323
|
+
"points.membersEarning": {
|
|
324
|
+
definition: "Distinct members awarded more than 0 points in the ledger, by effective date; the total counts each member once.",
|
|
325
|
+
kind: "distinct",
|
|
326
|
+
breakdowns: [],
|
|
327
|
+
filters: ["group"],
|
|
328
|
+
charts: counts,
|
|
329
|
+
ledger: true
|
|
330
|
+
},
|
|
331
|
+
"offers.redemptions": {
|
|
332
|
+
definition: "Fulfilled offer redemptions, by when they were redeemed.",
|
|
333
|
+
kind: "count",
|
|
334
|
+
breakdowns: ["offer"],
|
|
335
|
+
filters: ["offer", "group"],
|
|
336
|
+
charts: counts,
|
|
337
|
+
ledger: false
|
|
338
|
+
},
|
|
339
|
+
"referrals.accepted": {
|
|
340
|
+
definition: "Accepted referrals, by when they were accepted. A referral belongs to no program, so it takes no program breakdown or filter.",
|
|
341
|
+
kind: "count",
|
|
342
|
+
breakdowns: [],
|
|
343
|
+
filters: [],
|
|
344
|
+
charts: counts,
|
|
345
|
+
ledger: false
|
|
346
|
+
},
|
|
347
|
+
"referrals.qualified": {
|
|
348
|
+
definition: "Active referral qualifications, one per program and friend, by when they first qualified; suspended referrals count.",
|
|
349
|
+
kind: "count",
|
|
350
|
+
breakdowns: ["program"],
|
|
351
|
+
filters: ["program"],
|
|
352
|
+
charts: counts,
|
|
353
|
+
ledger: false
|
|
354
|
+
},
|
|
355
|
+
"referrals.qualificationRate": {
|
|
356
|
+
definition: "Of the referrals accepted in a bucket, the share whose friend has an active qualification now (in the program, for a program row or filter), by acceptance; empty below 10 accepted.",
|
|
357
|
+
kind: "rate",
|
|
358
|
+
breakdowns: ["program"],
|
|
359
|
+
filters: ["program"],
|
|
360
|
+
charts: rates,
|
|
361
|
+
ledger: false,
|
|
362
|
+
minimumDenominator: 10
|
|
363
|
+
},
|
|
364
|
+
"groups.joins": {
|
|
365
|
+
definition: "Members added to a group, by when they were added. A change that left membership as it was, and a membership recorded without a date, don't count. The group filter keeps changes in those groups.",
|
|
366
|
+
kind: "count",
|
|
367
|
+
breakdowns: ["group"],
|
|
368
|
+
filters: ["group"],
|
|
369
|
+
charts: counts,
|
|
370
|
+
ledger: false
|
|
371
|
+
},
|
|
372
|
+
"groups.leaves": {
|
|
373
|
+
definition: "Members removed from a group, by when they were removed. A change that left membership as it was, and a membership recorded without a date, don't count. The group filter keeps changes in those groups.",
|
|
374
|
+
kind: "count",
|
|
375
|
+
breakdowns: ["group"],
|
|
376
|
+
filters: ["group"],
|
|
377
|
+
charts: counts,
|
|
378
|
+
ledger: false
|
|
379
|
+
},
|
|
380
|
+
"trackers.records": {
|
|
381
|
+
definition: "Tracker trigger outcomes, one per quest and record observation, by when the record was observed, counted by their current outcome (admitted or skipped). A retry overwrites an outcome, so a bucket shows today's outcome of its records, not the outcome they had then.",
|
|
382
|
+
kind: "count",
|
|
383
|
+
breakdowns: ["outcome", "tracker"],
|
|
384
|
+
filters: ["tracker"],
|
|
385
|
+
charts: counts,
|
|
386
|
+
ledger: false
|
|
387
|
+
},
|
|
388
|
+
"leaderboards.settledParticipants": {
|
|
389
|
+
definition: "Participants in leaderboard settlements, one per member and settlement, by the settlement's cutoff. Pending and approved settlements count; a rejected settlement leaves its bucket.",
|
|
390
|
+
kind: "count",
|
|
391
|
+
breakdowns: ["leaderboard"],
|
|
392
|
+
filters: ["leaderboard"],
|
|
393
|
+
charts: periodic,
|
|
394
|
+
ledger: false
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
var analyticsMetricIds = Object.keys(
|
|
398
|
+
analyticsCatalog
|
|
399
|
+
);
|
|
400
|
+
var analyticsCatalogSchema = z3.object(
|
|
401
|
+
lazyShape({
|
|
402
|
+
metrics: () => z3.array(
|
|
403
|
+
z3.object({
|
|
404
|
+
id: z3.string(),
|
|
405
|
+
definition: z3.string(),
|
|
406
|
+
kind: z3.enum(["count", "distinct", "rate"]),
|
|
407
|
+
breakdowns: z3.array(z3.string()),
|
|
408
|
+
filters: z3.array(z3.string()),
|
|
409
|
+
charts: z3.array(z3.string()),
|
|
410
|
+
ledger: z3.boolean(),
|
|
411
|
+
/** For a rate, the denominator below which a value is empty. */
|
|
412
|
+
minimumDenominator: amount().optional()
|
|
413
|
+
})
|
|
414
|
+
)
|
|
415
|
+
})
|
|
416
|
+
);
|
|
417
|
+
var analyticsQueryLimit = 100;
|
|
418
|
+
var analyticsMetricQuerySchema = z3.strictObject(
|
|
419
|
+
lazyShape({
|
|
420
|
+
metric: () => z3.string().min(1).max(100),
|
|
421
|
+
range: () => z3.enum(analyticsRanges),
|
|
422
|
+
breakdown: () => z3.string().min(1).max(100).optional(),
|
|
423
|
+
/** Each filter names one key or a list of keys; a list matches any of them. */
|
|
424
|
+
filter: () => z3.record(
|
|
425
|
+
z3.string().min(1).max(100),
|
|
426
|
+
z3.union([identifier, z3.array(identifier).min(1).max(100)])
|
|
427
|
+
).optional(),
|
|
428
|
+
/** A point ledger's key or ID, archived ledgers included. Points metrics need one. */
|
|
429
|
+
ledger: () => identifier.optional(),
|
|
430
|
+
/** Breakdown rows to return, 1 to 100 (default 100); the rest are summed into `other`. */
|
|
431
|
+
limit: () => z3.number().int().optional()
|
|
432
|
+
})
|
|
433
|
+
);
|
|
434
|
+
var measureNumber = once(() => z3.number().nullable());
|
|
435
|
+
var measure = once(() => ({
|
|
436
|
+
total: measureNumber(),
|
|
437
|
+
previousTotal: measureNumber().optional(),
|
|
438
|
+
series: z3.array(measureNumber())
|
|
439
|
+
}));
|
|
440
|
+
var analyticsMetricResultSchema = z3.object(
|
|
441
|
+
lazyShape({
|
|
442
|
+
metric: () => z3.string(),
|
|
443
|
+
/** How the value reads: a `count`, a `distinct` count of members, or a `rate` from 0 to 1. */
|
|
444
|
+
kind: () => z3.enum(["count", "distinct", "rate"]),
|
|
445
|
+
range,
|
|
446
|
+
previous: previousRange,
|
|
447
|
+
/** The point ledger a points metric read, by key. */
|
|
448
|
+
ledger: () => identifier.optional(),
|
|
449
|
+
/** The metric over everything the filters match, every breakdown row included. */
|
|
450
|
+
value: () => z3.object(measure()),
|
|
451
|
+
breakdown: () => z3.object({
|
|
452
|
+
dimension: z3.string(),
|
|
453
|
+
/**
|
|
454
|
+
* Sorted by total, largest first (an empty rate last), at most `limit`. Every matching
|
|
455
|
+
* quest, offer, program, group, tracker or leaderboard that is not archived has a row, and an archived one has a row
|
|
456
|
+
* when it has activity in the range or the range before.
|
|
457
|
+
*/
|
|
458
|
+
rows: z3.array(
|
|
459
|
+
z3.object({
|
|
460
|
+
/** The resource's key (its ID when it has none), the source, the outcome, or the reason's code or text. */
|
|
461
|
+
key: z3.string(),
|
|
462
|
+
title: z3.string(),
|
|
463
|
+
archived: z3.boolean(),
|
|
464
|
+
...measure()
|
|
465
|
+
})
|
|
466
|
+
),
|
|
467
|
+
/** The rows past `limit`, summed; for a rate, their summed numerators over denominators. */
|
|
468
|
+
other: z3.object({ rows: amount(), ...measure() }).optional()
|
|
469
|
+
}).optional()
|
|
470
|
+
})
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
// src/dashboards.ts
|
|
474
|
+
import * as z4 from "zod";
|
|
475
|
+
var dashboardLimits = { panels: 24, dashboards: 20 };
|
|
476
|
+
var defaultPanelWidths = {
|
|
477
|
+
number: 1,
|
|
478
|
+
line: 2,
|
|
479
|
+
bar: 2,
|
|
480
|
+
table: 4
|
|
481
|
+
};
|
|
482
|
+
var title = z4.string().trim().min(1).max(120);
|
|
483
|
+
var description = z4.string().trim().min(1).max(500);
|
|
484
|
+
var filterValue = z4.union([identifier, z4.array(identifier).min(1).max(100)]);
|
|
485
|
+
var filter = z4.record(z4.string().min(1).max(100), filterValue);
|
|
486
|
+
var panelFields = {
|
|
487
|
+
key: identifier,
|
|
488
|
+
title,
|
|
489
|
+
description: description.optional(),
|
|
490
|
+
/** A metric ID from the catalog (`analytics.catalog`). */
|
|
491
|
+
metric: z4.string().min(1).max(100),
|
|
492
|
+
breakdown: z4.string().min(1).max(100).optional(),
|
|
493
|
+
/** Each filter names one key or a list of keys; a list matches any of them. */
|
|
494
|
+
filter: filter.optional(),
|
|
495
|
+
/** The point ledger a points metric reads, by key. */
|
|
496
|
+
ledger: identifier.optional(),
|
|
497
|
+
chart: z4.enum(analyticsCharts)
|
|
498
|
+
};
|
|
499
|
+
var dashboardPanelAuthoringSchema = z4.strictObject({
|
|
500
|
+
...panelFields,
|
|
501
|
+
width: z4.number().int().min(1).max(4).optional()
|
|
502
|
+
});
|
|
503
|
+
var dashboardAuthoringSchema = z4.strictObject({
|
|
504
|
+
key: identifier,
|
|
505
|
+
title,
|
|
506
|
+
description: description.optional(),
|
|
507
|
+
/** The range the dashboard opens with. Defaults to `30d`. */
|
|
508
|
+
defaultRange: z4.enum(analyticsRanges).optional(),
|
|
509
|
+
/** Orders dashboards in lists: by position, those without one last, then by title. */
|
|
510
|
+
position: z4.number().int().min(0).max(1e3).optional(),
|
|
511
|
+
sections: z4.array(
|
|
512
|
+
z4.strictObject({
|
|
513
|
+
title: title.optional(),
|
|
514
|
+
panels: z4.array(dashboardPanelAuthoringSchema).min(1).max(100)
|
|
515
|
+
})
|
|
516
|
+
).min(1).max(100)
|
|
517
|
+
});
|
|
518
|
+
var dashboardDefinitionSchema = z4.strictObject({
|
|
519
|
+
key: identifier,
|
|
520
|
+
title,
|
|
521
|
+
description: description.optional(),
|
|
522
|
+
defaultRange: z4.enum(analyticsRanges),
|
|
523
|
+
position: z4.number().int().min(0).max(1e3).optional(),
|
|
524
|
+
sections: z4.array(
|
|
525
|
+
z4.strictObject({
|
|
526
|
+
title: title.optional(),
|
|
527
|
+
panels: z4.array(
|
|
528
|
+
z4.strictObject({
|
|
529
|
+
...panelFields,
|
|
530
|
+
width: z4.number().int().min(1).max(4)
|
|
531
|
+
})
|
|
532
|
+
)
|
|
533
|
+
})
|
|
534
|
+
)
|
|
535
|
+
});
|
|
536
|
+
var dashboardArtifactSchema = z4.union([
|
|
537
|
+
dashboardAuthoringSchema,
|
|
538
|
+
z4.object({ code: z4.string().min(1).max(2e6) }).strict()
|
|
539
|
+
]);
|
|
540
|
+
function normalizeDashboard(input) {
|
|
541
|
+
const dashboard = dashboardAuthoringSchema.parse(input);
|
|
542
|
+
return dashboardDefinitionSchema.parse({
|
|
543
|
+
key: dashboard.key,
|
|
544
|
+
title: dashboard.title,
|
|
545
|
+
...dashboard.description === void 0 ? {} : { description: dashboard.description },
|
|
546
|
+
defaultRange: dashboard.defaultRange ?? "30d",
|
|
547
|
+
...dashboard.position === void 0 ? {} : { position: dashboard.position },
|
|
548
|
+
sections: dashboard.sections.map((section) => ({
|
|
549
|
+
...section.title === void 0 ? {} : { title: section.title },
|
|
550
|
+
panels: section.panels.map((panel) => ({
|
|
551
|
+
...panel,
|
|
552
|
+
width: panel.width ?? defaultPanelWidths[panel.chart]
|
|
553
|
+
}))
|
|
554
|
+
}))
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
function dashboardPanels(dashboard) {
|
|
558
|
+
return dashboard.sections.flatMap((section) => [...section.panels]);
|
|
559
|
+
}
|
|
560
|
+
function dashboardIssue(dashboard) {
|
|
561
|
+
const panels = dashboard.sections.flatMap((section) => [...section.panels]);
|
|
562
|
+
const where = `dashboard "${dashboard.key}"`;
|
|
563
|
+
if (panels.length > dashboardLimits.panels) {
|
|
564
|
+
return {
|
|
565
|
+
code: "too-many-panels",
|
|
566
|
+
message: `The ${where} has ${panels.length} panels; a dashboard holds at most ${dashboardLimits.panels}.`
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
const keys = /* @__PURE__ */ new Set();
|
|
570
|
+
for (const panel of panels) {
|
|
571
|
+
const at = `Panel "${panel.key}" of ${where}`;
|
|
572
|
+
if (keys.has(panel.key)) {
|
|
573
|
+
return {
|
|
574
|
+
code: "duplicate-key",
|
|
575
|
+
panel: panel.key,
|
|
576
|
+
message: `The ${where} has two panels with the key "${panel.key}".`
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
keys.add(panel.key);
|
|
580
|
+
if (!Object.hasOwn(analyticsCatalog, panel.metric)) {
|
|
581
|
+
return {
|
|
582
|
+
code: "unknown-metric",
|
|
583
|
+
panel: panel.key,
|
|
584
|
+
message: `${at} names the unknown metric "${panel.metric}". Run domino analytics metrics for the catalog.`
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
const entry = analyticsCatalog[panel.metric];
|
|
588
|
+
if (panel.breakdown !== void 0 && !entry.breakdowns.includes(panel.breakdown)) {
|
|
589
|
+
return {
|
|
590
|
+
code: "breakdown-not-accepted",
|
|
591
|
+
panel: panel.key,
|
|
592
|
+
message: `${at}: ${panel.metric} has no "${panel.breakdown}" breakdown; it takes ${entry.breakdowns.join(", ") || "none"}.`
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
for (const name of Object.keys(panel.filter ?? {})) {
|
|
596
|
+
if (!entry.filters.includes(name)) {
|
|
597
|
+
return {
|
|
598
|
+
code: "filter-not-accepted",
|
|
599
|
+
panel: panel.key,
|
|
600
|
+
message: `${at}: ${panel.metric} takes no "${name}" filter; it takes ${entry.filters.join(", ") || "none"}.`
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
if (!entry.charts.includes(panel.chart)) {
|
|
605
|
+
return {
|
|
606
|
+
code: "chart-not-accepted",
|
|
607
|
+
panel: panel.key,
|
|
608
|
+
message: `${at}: ${panel.metric} can't be drawn as "${panel.chart}"; it takes ${entry.charts.join(", ")}.`
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
if (panel.chart === "number" && panel.breakdown !== void 0) {
|
|
612
|
+
return {
|
|
613
|
+
code: "breakdown-not-accepted",
|
|
614
|
+
panel: panel.key,
|
|
615
|
+
message: `${at}: a number panel shows one value and takes no breakdown.`
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
if (panel.chart === "table" && panel.breakdown === void 0) {
|
|
619
|
+
return {
|
|
620
|
+
code: "breakdown-required",
|
|
621
|
+
panel: panel.key,
|
|
622
|
+
message: `${at}: a table panel needs a breakdown; ${panel.metric} takes ${entry.breakdowns.join(", ") || "none"}.`
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
if (entry.ledger && panel.ledger === void 0) {
|
|
626
|
+
return {
|
|
627
|
+
code: "ledger-required",
|
|
628
|
+
panel: panel.key,
|
|
629
|
+
message: `${at}: ${panel.metric} needs a point ledger; set ledger to its key.`
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
if (!entry.ledger && panel.ledger !== void 0) {
|
|
633
|
+
return {
|
|
634
|
+
code: "ledger-not-accepted",
|
|
635
|
+
panel: panel.key,
|
|
636
|
+
message: `${at}: ${panel.metric} reads no point ledger; remove ledger.`
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
642
|
+
var dashboardPreviewSchema = responseShape(
|
|
643
|
+
z4.object({
|
|
644
|
+
key: identifier,
|
|
645
|
+
title: z4.string(),
|
|
646
|
+
/** `unchanged`: the definition equals the published one, and publishing writes no version. */
|
|
647
|
+
change: z4.enum(["created", "updated", "unchanged"]),
|
|
648
|
+
definition: dashboardDefinitionSchema
|
|
649
|
+
})
|
|
650
|
+
);
|
|
651
|
+
var keyOf = (reference) => typeof reference === "string" ? reference : reference.key;
|
|
652
|
+
function defineDashboard(input) {
|
|
653
|
+
const authored = dashboardAuthoringSchema.parse({
|
|
654
|
+
...input,
|
|
655
|
+
sections: input.sections.map((section) => ({
|
|
656
|
+
...section,
|
|
657
|
+
panels: section.panels.map((panel) => ({
|
|
658
|
+
...panel,
|
|
659
|
+
...panel.ledger === void 0 ? {} : { ledger: keyOf(panel.ledger) },
|
|
660
|
+
...panel.filter === void 0 ? {} : {
|
|
661
|
+
filter: Object.fromEntries(
|
|
662
|
+
Object.entries(panel.filter).map(([name, value]) => [
|
|
663
|
+
name,
|
|
664
|
+
Array.isArray(value) ? value.map(keyOf) : keyOf(value)
|
|
665
|
+
])
|
|
666
|
+
)
|
|
667
|
+
}
|
|
668
|
+
}))
|
|
669
|
+
}))
|
|
670
|
+
});
|
|
671
|
+
return resourceHandle("dashboard", authored);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
export {
|
|
675
|
+
youtubeShortsNamespace,
|
|
676
|
+
youtubeShortBodySchema,
|
|
677
|
+
youtubeShortMetrics,
|
|
678
|
+
tiktokVideosNamespace,
|
|
679
|
+
tiktokVideoBodySchema,
|
|
680
|
+
tiktokVideoMetrics,
|
|
681
|
+
analyticsRanges,
|
|
682
|
+
analyticsQuerySchema,
|
|
683
|
+
analyticsReadSchema,
|
|
684
|
+
analyticsBreakdowns,
|
|
685
|
+
analyticsFilters,
|
|
686
|
+
analyticsCharts,
|
|
687
|
+
analyticsCatalog,
|
|
688
|
+
analyticsMetricIds,
|
|
689
|
+
analyticsCatalogSchema,
|
|
690
|
+
analyticsQueryLimit,
|
|
691
|
+
analyticsMetricQuerySchema,
|
|
692
|
+
analyticsMetricResultSchema,
|
|
693
|
+
dashboardLimits,
|
|
694
|
+
defaultPanelWidths,
|
|
695
|
+
dashboardPanelAuthoringSchema,
|
|
696
|
+
dashboardAuthoringSchema,
|
|
697
|
+
dashboardDefinitionSchema,
|
|
698
|
+
dashboardArtifactSchema,
|
|
699
|
+
normalizeDashboard,
|
|
700
|
+
dashboardPanels,
|
|
701
|
+
dashboardIssue,
|
|
702
|
+
dashboardPreviewSchema,
|
|
703
|
+
defineDashboard
|
|
704
|
+
};
|