@dappermountain/payload-plugin-achievements 0.1.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/LICENSE +21 -0
- package/README.md +319 -0
- package/dist/access/helpers/index.d.ts +9 -0
- package/dist/access/helpers/index.d.ts.map +1 -0
- package/dist/access/helpers/index.js +25 -0
- package/dist/access/index.d.ts +3 -0
- package/dist/access/index.d.ts.map +1 -0
- package/dist/access/index.js +2 -0
- package/dist/access/roles/index.d.ts +15 -0
- package/dist/access/roles/index.d.ts.map +1 -0
- package/dist/access/roles/index.js +55 -0
- package/dist/admin/ActorField.d.ts +9 -0
- package/dist/admin/ActorField.d.ts.map +1 -0
- package/dist/admin/ActorField.js +19 -0
- package/dist/admin/MetricChangeField.d.ts +9 -0
- package/dist/admin/MetricChangeField.d.ts.map +1 -0
- package/dist/admin/MetricChangeField.js +21 -0
- package/dist/admin/MetricField.d.ts +9 -0
- package/dist/admin/MetricField.d.ts.map +1 -0
- package/dist/admin/MetricField.js +23 -0
- package/dist/admin/useEventTypeFlags.d.ts +7 -0
- package/dist/admin/useEventTypeFlags.d.ts.map +1 -0
- package/dist/admin/useEventTypeFlags.js +68 -0
- package/dist/catalog.d.ts +21 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +9 -0
- package/dist/collections/AchievementRequests/access/index.d.ts +3 -0
- package/dist/collections/AchievementRequests/access/index.d.ts.map +1 -0
- package/dist/collections/AchievementRequests/access/index.js +8 -0
- package/dist/collections/AchievementRequests/hooks/index.d.ts +3 -0
- package/dist/collections/AchievementRequests/hooks/index.d.ts.map +1 -0
- package/dist/collections/AchievementRequests/hooks/index.js +5 -0
- package/dist/collections/AchievementRequests/hooks/prepareAchievementRequest.d.ts +12 -0
- package/dist/collections/AchievementRequests/hooks/prepareAchievementRequest.d.ts.map +1 -0
- package/dist/collections/AchievementRequests/hooks/prepareAchievementRequest.js +105 -0
- package/dist/collections/AchievementRequests/index.d.ts +3 -0
- package/dist/collections/AchievementRequests/index.d.ts.map +1 -0
- package/dist/collections/AchievementRequests/index.js +39 -0
- package/dist/collections/Achievements/access/index.d.ts +3 -0
- package/dist/collections/Achievements/access/index.d.ts.map +1 -0
- package/dist/collections/Achievements/access/index.js +2 -0
- package/dist/collections/Achievements/hooks/index.d.ts +3 -0
- package/dist/collections/Achievements/hooks/index.d.ts.map +1 -0
- package/dist/collections/Achievements/hooks/index.js +4 -0
- package/dist/collections/Achievements/hooks/validateEligibilityRules.d.ts +3 -0
- package/dist/collections/Achievements/hooks/validateEligibilityRules.d.ts.map +1 -0
- package/dist/collections/Achievements/hooks/validateEligibilityRules.js +8 -0
- package/dist/collections/Achievements/index.d.ts +3 -0
- package/dist/collections/Achievements/index.d.ts.map +1 -0
- package/dist/collections/Achievements/index.js +62 -0
- package/dist/collections/EventTypes/access/index.d.ts +12 -0
- package/dist/collections/EventTypes/access/index.d.ts.map +1 -0
- package/dist/collections/EventTypes/access/index.js +2 -0
- package/dist/collections/EventTypes/hooks/index.d.ts +3 -0
- package/dist/collections/EventTypes/hooks/index.d.ts.map +1 -0
- package/dist/collections/EventTypes/hooks/index.js +6 -0
- package/dist/collections/EventTypes/index.d.ts +3 -0
- package/dist/collections/EventTypes/index.d.ts.map +1 -0
- package/dist/collections/EventTypes/index.js +67 -0
- package/dist/collections/Grants/access/index.d.ts +3 -0
- package/dist/collections/Grants/access/index.d.ts.map +1 -0
- package/dist/collections/Grants/access/index.js +8 -0
- package/dist/collections/Grants/hooks/ensureUniqueGrant.d.ts +4 -0
- package/dist/collections/Grants/hooks/ensureUniqueGrant.d.ts.map +1 -0
- package/dist/collections/Grants/hooks/ensureUniqueGrant.js +34 -0
- package/dist/collections/Grants/hooks/index.d.ts +3 -0
- package/dist/collections/Grants/hooks/index.d.ts.map +1 -0
- package/dist/collections/Grants/hooks/index.js +7 -0
- package/dist/collections/Grants/hooks/setGrantTitle.d.ts +4 -0
- package/dist/collections/Grants/hooks/setGrantTitle.d.ts.map +1 -0
- package/dist/collections/Grants/hooks/setGrantTitle.js +47 -0
- package/dist/collections/Grants/index.d.ts +3 -0
- package/dist/collections/Grants/index.d.ts.map +1 -0
- package/dist/collections/Grants/index.js +62 -0
- package/dist/collections/Logs/access/index.d.ts +3 -0
- package/dist/collections/Logs/access/index.d.ts.map +1 -0
- package/dist/collections/Logs/access/index.js +8 -0
- package/dist/collections/Logs/fields/validators.d.ts +5 -0
- package/dist/collections/Logs/fields/validators.d.ts.map +1 -0
- package/dist/collections/Logs/fields/validators.js +44 -0
- package/dist/collections/Logs/hooks/ensureTypeRequirements.d.ts +3 -0
- package/dist/collections/Logs/hooks/ensureTypeRequirements.d.ts.map +1 -0
- package/dist/collections/Logs/hooks/ensureTypeRequirements.js +35 -0
- package/dist/collections/Logs/hooks/index.d.ts +3 -0
- package/dist/collections/Logs/hooks/index.d.ts.map +1 -0
- package/dist/collections/Logs/hooks/index.js +4 -0
- package/dist/collections/Logs/index.d.ts +4 -0
- package/dist/collections/Logs/index.d.ts.map +1 -0
- package/dist/collections/Logs/index.js +113 -0
- package/dist/collections/Metrics/access/index.d.ts +12 -0
- package/dist/collections/Metrics/access/index.d.ts.map +1 -0
- package/dist/collections/Metrics/access/index.js +2 -0
- package/dist/collections/Metrics/hooks/index.d.ts +3 -0
- package/dist/collections/Metrics/hooks/index.d.ts.map +1 -0
- package/dist/collections/Metrics/hooks/index.js +6 -0
- package/dist/collections/Metrics/index.d.ts +3 -0
- package/dist/collections/Metrics/index.d.ts.map +1 -0
- package/dist/collections/Metrics/index.js +35 -0
- package/dist/collections/Tiers/access/index.d.ts +3 -0
- package/dist/collections/Tiers/access/index.d.ts.map +1 -0
- package/dist/collections/Tiers/access/index.js +2 -0
- package/dist/collections/Tiers/hooks/index.d.ts +3 -0
- package/dist/collections/Tiers/hooks/index.d.ts.map +1 -0
- package/dist/collections/Tiers/hooks/index.js +4 -0
- package/dist/collections/Tiers/hooks/validateUnlockRules.d.ts +3 -0
- package/dist/collections/Tiers/hooks/validateUnlockRules.d.ts.map +1 -0
- package/dist/collections/Tiers/hooks/validateUnlockRules.js +7 -0
- package/dist/collections/Tiers/index.d.ts +3 -0
- package/dist/collections/Tiers/index.d.ts.map +1 -0
- package/dist/collections/Tiers/index.js +81 -0
- package/dist/collections/access/definitions.d.ts +5 -0
- package/dist/collections/access/definitions.d.ts.map +1 -0
- package/dist/collections/access/definitions.js +11 -0
- package/dist/collections/applyOverrides.d.ts +9 -0
- package/dist/collections/applyOverrides.d.ts.map +1 -0
- package/dist/collections/applyOverrides.js +34 -0
- package/dist/collections/fields/catalogFilter.d.ts +8 -0
- package/dist/collections/fields/catalogFilter.d.ts.map +1 -0
- package/dist/collections/fields/catalogFilter.js +10 -0
- package/dist/collections/fields/index.d.ts +6 -0
- package/dist/collections/fields/index.d.ts.map +1 -0
- package/dist/collections/fields/index.js +5 -0
- package/dist/collections/fields/localizedText.d.ts +10 -0
- package/dist/collections/fields/localizedText.d.ts.map +1 -0
- package/dist/collections/fields/localizedText.js +20 -0
- package/dist/collections/fields/ruleFields.d.ts +8 -0
- package/dist/collections/fields/ruleFields.d.ts.map +1 -0
- package/dist/collections/fields/ruleFields.js +123 -0
- package/dist/collections/fields/scopeField.d.ts +7 -0
- package/dist/collections/fields/scopeField.d.ts.map +1 -0
- package/dist/collections/fields/scopeField.js +22 -0
- package/dist/collections/fields/userField.d.ts +4 -0
- package/dist/collections/fields/userField.d.ts.map +1 -0
- package/dist/collections/fields/userField.js +12 -0
- package/dist/collections/fields/usersAchievementsGroup.d.ts +8 -0
- package/dist/collections/fields/usersAchievementsGroup.d.ts.map +1 -0
- package/dist/collections/fields/usersAchievementsGroup.js +36 -0
- package/dist/collections/helpers.d.ts +16 -0
- package/dist/collections/helpers.d.ts.map +1 -0
- package/dist/collections/helpers.js +31 -0
- package/dist/collections/hooks/eachRuleLeaf.d.ts +3 -0
- package/dist/collections/hooks/eachRuleLeaf.d.ts.map +1 -0
- package/dist/collections/hooks/eachRuleLeaf.js +20 -0
- package/dist/collections/hooks/index.d.ts +4 -0
- package/dist/collections/hooks/index.d.ts.map +1 -0
- package/dist/collections/hooks/index.js +3 -0
- package/dist/collections/hooks/lockSystemCatalogFields.d.ts +7 -0
- package/dist/collections/hooks/lockSystemCatalogFields.d.ts.map +1 -0
- package/dist/collections/hooks/lockSystemCatalogFields.js +15 -0
- package/dist/collections/hooks/preventCatalogDelete.d.ts +5 -0
- package/dist/collections/hooks/preventCatalogDelete.d.ts.map +1 -0
- package/dist/collections/hooks/preventCatalogDelete.js +89 -0
- package/dist/collections/index.d.ts +4 -0
- package/dist/collections/index.d.ts.map +1 -0
- package/dist/collections/index.js +23 -0
- package/dist/collections/slugs.d.ts +22 -0
- package/dist/collections/slugs.d.ts.map +1 -0
- package/dist/collections/slugs.js +51 -0
- package/dist/defaults.d.ts +21 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/defaults.js +31 -0
- package/dist/enabled.d.ts +4 -0
- package/dist/enabled.d.ts.map +1 -0
- package/dist/enabled.js +9 -0
- package/dist/endpoints/index.d.ts +4 -0
- package/dist/endpoints/index.d.ts.map +1 -0
- package/dist/endpoints/index.js +6 -0
- package/dist/endpoints/me.d.ts +7 -0
- package/dist/endpoints/me.d.ts.map +1 -0
- package/dist/endpoints/me.js +50 -0
- package/dist/exports/client.d.ts +4 -0
- package/dist/exports/client.d.ts.map +1 -0
- package/dist/exports/client.js +3 -0
- package/dist/extensions/registry.d.ts +3 -0
- package/dist/extensions/registry.d.ts.map +1 -0
- package/dist/extensions/registry.js +12 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/options-store.d.ts +7 -0
- package/dist/options-store.d.ts.map +1 -0
- package/dist/options-store.js +15 -0
- package/dist/plugin.d.ts +8 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +31 -0
- package/dist/seed/index.d.ts +63 -0
- package/dist/seed/index.d.ts.map +1 -0
- package/dist/seed/index.js +375 -0
- package/dist/services/achievement/buildCatalogProgressGroups.d.ts +37 -0
- package/dist/services/achievement/buildCatalogProgressGroups.d.ts.map +1 -0
- package/dist/services/achievement/buildCatalogProgressGroups.js +70 -0
- package/dist/services/achievement/builtInRules.d.ts +3 -0
- package/dist/services/achievement/builtInRules.d.ts.map +1 -0
- package/dist/services/achievement/builtInRules.js +280 -0
- package/dist/services/achievement/evaluateRules.d.ts +48 -0
- package/dist/services/achievement/evaluateRules.d.ts.map +1 -0
- package/dist/services/achievement/evaluateRules.js +206 -0
- package/dist/services/achievement/eventTypeRequiresActor.d.ts +24 -0
- package/dist/services/achievement/eventTypeRequiresActor.d.ts.map +1 -0
- package/dist/services/achievement/eventTypeRequiresActor.js +46 -0
- package/dist/services/achievement/getUserProgress.d.ts +53 -0
- package/dist/services/achievement/getUserProgress.d.ts.map +1 -0
- package/dist/services/achievement/getUserProgress.js +163 -0
- package/dist/services/achievement/index.d.ts +14 -0
- package/dist/services/achievement/index.d.ts.map +1 -0
- package/dist/services/achievement/index.js +9 -0
- package/dist/services/achievement/recordEvent.d.ts +41 -0
- package/dist/services/achievement/recordEvent.d.ts.map +1 -0
- package/dist/services/achievement/recordEvent.js +126 -0
- package/dist/services/achievement/relationId.d.ts +3 -0
- package/dist/services/achievement/relationId.d.ts.map +1 -0
- package/dist/services/achievement/relationId.js +15 -0
- package/dist/services/achievement/resolveCatalog.d.ts +14 -0
- package/dist/services/achievement/resolveCatalog.d.ts.map +1 -0
- package/dist/services/achievement/resolveCatalog.js +34 -0
- package/dist/services/achievement/resolveCurrentTier.d.ts +21 -0
- package/dist/services/achievement/resolveCurrentTier.d.ts.map +1 -0
- package/dist/services/achievement/resolveCurrentTier.js +51 -0
- package/dist/services/achievement/resolveTierProgress.d.ts +35 -0
- package/dist/services/achievement/resolveTierProgress.d.ts.map +1 -0
- package/dist/services/achievement/resolveTierProgress.js +99 -0
- package/dist/services/achievement/reviewAchievementRequest.d.ts +11 -0
- package/dist/services/achievement/reviewAchievementRequest.d.ts.map +1 -0
- package/dist/services/achievement/reviewAchievementRequest.js +37 -0
- package/dist/services/achievement/submitAchievementRequest.d.ts +21 -0
- package/dist/services/achievement/submitAchievementRequest.d.ts.map +1 -0
- package/dist/services/achievement/submitAchievementRequest.js +126 -0
- package/dist/services/achievement/validateRules.d.ts +11 -0
- package/dist/services/achievement/validateRules.d.ts.map +1 -0
- package/dist/services/achievement/validateRules.js +74 -0
- package/dist/services/achievement/where.d.ts +9 -0
- package/dist/services/achievement/where.d.ts.map +1 -0
- package/dist/services/achievement/where.js +23 -0
- package/dist/types.d.ts +117 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +85 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { collectionOf } from '../../collections/helpers';
|
|
2
|
+
import { relationId } from './relationId';
|
|
3
|
+
import { userScopeIncludingUnscopedWhere, userScopeWhere } from './where';
|
|
4
|
+
function clamp01(value) {
|
|
5
|
+
if (!Number.isFinite(value))
|
|
6
|
+
return 0;
|
|
7
|
+
return Math.max(0, Math.min(1, value));
|
|
8
|
+
}
|
|
9
|
+
async function resolveMetricId(args) {
|
|
10
|
+
const metricId = relationId(args.rule.metric);
|
|
11
|
+
if (metricId)
|
|
12
|
+
return metricId;
|
|
13
|
+
const metricSlug = typeof args.rule.metricSlug === 'string' ? args.rule.metricSlug : '';
|
|
14
|
+
if (!metricSlug)
|
|
15
|
+
return null;
|
|
16
|
+
const found = await args.payload.find({
|
|
17
|
+
collection: collectionOf('metrics'),
|
|
18
|
+
depth: 0,
|
|
19
|
+
limit: 1,
|
|
20
|
+
overrideAccess: true,
|
|
21
|
+
where: { slug: { equals: metricSlug } },
|
|
22
|
+
});
|
|
23
|
+
return relationId(found.docs[0]);
|
|
24
|
+
}
|
|
25
|
+
async function metricValue(args) {
|
|
26
|
+
const logs = await args.payload.find({
|
|
27
|
+
collection: collectionOf('logs'),
|
|
28
|
+
depth: 0,
|
|
29
|
+
limit: 1000,
|
|
30
|
+
pagination: false,
|
|
31
|
+
overrideAccess: true,
|
|
32
|
+
where: {
|
|
33
|
+
and: [userScopeWhere(args.userId, args.scopeId), { metric: { equals: args.metricId } }],
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
return logs.docs.reduce((sum, doc) => {
|
|
37
|
+
const change = doc.change;
|
|
38
|
+
return sum + (typeof change === 'number' && Number.isFinite(change) ? change : 0);
|
|
39
|
+
}, 0);
|
|
40
|
+
}
|
|
41
|
+
async function resolveEventTypeId(args) {
|
|
42
|
+
let eventTypeId = relationId(args.rule.eventType);
|
|
43
|
+
if (eventTypeId) {
|
|
44
|
+
// Seeded rules may store a slug string in `eventType` before resolve; only treat
|
|
45
|
+
// opaque ids as final when a catalog row exists.
|
|
46
|
+
const byId = await args.payload.find({
|
|
47
|
+
collection: collectionOf('eventTypes'),
|
|
48
|
+
depth: 0,
|
|
49
|
+
limit: 1,
|
|
50
|
+
overrideAccess: true,
|
|
51
|
+
where: { id: { equals: eventTypeId } },
|
|
52
|
+
});
|
|
53
|
+
if (relationId(byId.docs[0]))
|
|
54
|
+
return eventTypeId;
|
|
55
|
+
}
|
|
56
|
+
const slugCandidate = (typeof args.rule.eventTypeSlug === 'string' && args.rule.eventTypeSlug) ||
|
|
57
|
+
(typeof args.rule.eventType === 'string' && args.rule.eventType) ||
|
|
58
|
+
'';
|
|
59
|
+
if (!slugCandidate)
|
|
60
|
+
return null;
|
|
61
|
+
const found = await args.payload.find({
|
|
62
|
+
collection: collectionOf('eventTypes'),
|
|
63
|
+
depth: 0,
|
|
64
|
+
limit: 1,
|
|
65
|
+
overrideAccess: true,
|
|
66
|
+
where: { slug: { equals: slugCandidate } },
|
|
67
|
+
});
|
|
68
|
+
return relationId(found.docs[0]);
|
|
69
|
+
}
|
|
70
|
+
async function resolveAchievementRef(args) {
|
|
71
|
+
let requiredId = relationId(args.rule.achievement);
|
|
72
|
+
let requiredSlug = typeof args.rule.achievementSlug === 'string' ? args.rule.achievementSlug : '';
|
|
73
|
+
let eligibilityRules;
|
|
74
|
+
let completionRules;
|
|
75
|
+
if (!requiredId && requiredSlug) {
|
|
76
|
+
const found = await args.payload.find({
|
|
77
|
+
collection: collectionOf('achievements'),
|
|
78
|
+
depth: 0,
|
|
79
|
+
limit: 1,
|
|
80
|
+
overrideAccess: true,
|
|
81
|
+
where: args.scopeId
|
|
82
|
+
? {
|
|
83
|
+
and: [{ slug: { equals: requiredSlug } }, { scope: { equals: args.scopeId } }],
|
|
84
|
+
}
|
|
85
|
+
: { slug: { equals: requiredSlug } },
|
|
86
|
+
});
|
|
87
|
+
const doc = found.docs[0];
|
|
88
|
+
requiredId = relationId(doc);
|
|
89
|
+
requiredSlug = String(doc?.slug ?? requiredSlug);
|
|
90
|
+
eligibilityRules = doc?.eligibilityRules;
|
|
91
|
+
completionRules = doc?.completionRules;
|
|
92
|
+
}
|
|
93
|
+
if (requiredId && (eligibilityRules === undefined || completionRules === undefined || !requiredSlug)) {
|
|
94
|
+
const doc = (await args.payload.findByID({
|
|
95
|
+
collection: collectionOf('achievements'),
|
|
96
|
+
id: requiredId,
|
|
97
|
+
depth: 0,
|
|
98
|
+
overrideAccess: true,
|
|
99
|
+
}));
|
|
100
|
+
requiredSlug = requiredSlug || String(doc?.slug ?? '');
|
|
101
|
+
eligibilityRules = eligibilityRules ?? doc?.eligibilityRules;
|
|
102
|
+
completionRules = completionRules ?? doc?.completionRules;
|
|
103
|
+
}
|
|
104
|
+
return { id: requiredId, slug: requiredSlug, eligibilityRules, completionRules };
|
|
105
|
+
}
|
|
106
|
+
async function isAchievementGranted(args) {
|
|
107
|
+
if (!args.requiredId && !args.requiredSlug)
|
|
108
|
+
return false;
|
|
109
|
+
const grantClauses = [{ user: { equals: args.userId } }];
|
|
110
|
+
if (args.scopeId)
|
|
111
|
+
grantClauses.push({ scope: { equals: args.scopeId } });
|
|
112
|
+
if (args.requiredId)
|
|
113
|
+
grantClauses.push({ achievement: { equals: args.requiredId } });
|
|
114
|
+
const grants = await args.payload.find({
|
|
115
|
+
collection: collectionOf('grants'),
|
|
116
|
+
depth: args.requiredSlug && !args.requiredId ? 1 : 0,
|
|
117
|
+
limit: args.requiredId ? 1 : 50,
|
|
118
|
+
overrideAccess: true,
|
|
119
|
+
where: { and: grantClauses },
|
|
120
|
+
});
|
|
121
|
+
if (args.requiredId)
|
|
122
|
+
return grants.docs.length > 0;
|
|
123
|
+
return grants.docs.some((doc) => {
|
|
124
|
+
const achievement = doc.achievement;
|
|
125
|
+
if (achievement && typeof achievement === 'object') {
|
|
126
|
+
return achievement.slug === args.requiredSlug;
|
|
127
|
+
}
|
|
128
|
+
return false;
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
export const builtInRuleTypes = [
|
|
132
|
+
{
|
|
133
|
+
type: 'tier-at-least',
|
|
134
|
+
progressRole: 'gate',
|
|
135
|
+
async evaluate({ payload, req, rule, userId, scopeId, ladderRank }) {
|
|
136
|
+
const tierId = relationId(rule.tier);
|
|
137
|
+
let requiredRank;
|
|
138
|
+
if (tierId) {
|
|
139
|
+
const tier = (await payload.findByID({
|
|
140
|
+
collection: collectionOf('tiers'),
|
|
141
|
+
id: tierId,
|
|
142
|
+
depth: 0,
|
|
143
|
+
overrideAccess: true,
|
|
144
|
+
}));
|
|
145
|
+
requiredRank = tier?.rank;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
const tierSlug = String(rule.tierSlug ?? '');
|
|
149
|
+
if (!tierSlug)
|
|
150
|
+
return false;
|
|
151
|
+
const tiers = await payload.find({
|
|
152
|
+
collection: collectionOf('tiers'),
|
|
153
|
+
depth: 0,
|
|
154
|
+
limit: 1,
|
|
155
|
+
overrideAccess: true,
|
|
156
|
+
where: scopeId
|
|
157
|
+
? { and: [{ slug: { equals: tierSlug } }, { scope: { equals: scopeId } }] }
|
|
158
|
+
: { slug: { equals: tierSlug } },
|
|
159
|
+
});
|
|
160
|
+
requiredRank = tiers.docs[0]?.rank;
|
|
161
|
+
}
|
|
162
|
+
if (typeof requiredRank !== 'number')
|
|
163
|
+
return false;
|
|
164
|
+
let currentRank = ladderRank;
|
|
165
|
+
if (currentRank === undefined) {
|
|
166
|
+
const { resolveCurrentTier } = await import('./resolveCurrentTier');
|
|
167
|
+
const current = await resolveCurrentTier({
|
|
168
|
+
payload,
|
|
169
|
+
req,
|
|
170
|
+
userId,
|
|
171
|
+
scopeId,
|
|
172
|
+
});
|
|
173
|
+
currentRank = current?.rank ?? null;
|
|
174
|
+
}
|
|
175
|
+
return typeof currentRank === 'number' && currentRank >= requiredRank;
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
type: 'achievement-complete',
|
|
180
|
+
async evaluate({ payload, rule, userId, scopeId }) {
|
|
181
|
+
const ref = await resolveAchievementRef({ payload, rule, scopeId });
|
|
182
|
+
return isAchievementGranted({
|
|
183
|
+
payload,
|
|
184
|
+
userId,
|
|
185
|
+
scopeId,
|
|
186
|
+
requiredId: ref.id,
|
|
187
|
+
requiredSlug: ref.slug,
|
|
188
|
+
});
|
|
189
|
+
},
|
|
190
|
+
async progress(args) {
|
|
191
|
+
const { payload, req, rule, userId, scopeId, ladderRank, progressVisited } = args;
|
|
192
|
+
const ref = await resolveAchievementRef({ payload, rule, scopeId });
|
|
193
|
+
if (!ref.id && !ref.slug)
|
|
194
|
+
return 0;
|
|
195
|
+
const granted = await isAchievementGranted({
|
|
196
|
+
payload,
|
|
197
|
+
userId,
|
|
198
|
+
scopeId,
|
|
199
|
+
requiredId: ref.id,
|
|
200
|
+
requiredSlug: ref.slug,
|
|
201
|
+
});
|
|
202
|
+
if (granted)
|
|
203
|
+
return 1;
|
|
204
|
+
const visitKey = ref.id || ref.slug;
|
|
205
|
+
const visited = progressVisited ?? new Set();
|
|
206
|
+
if (visited.has(visitKey))
|
|
207
|
+
return 0;
|
|
208
|
+
visited.add(visitKey);
|
|
209
|
+
const { evaluateRuleProgress, ruleGroupHasProgressRequirements } = await import('./evaluateRules');
|
|
210
|
+
const composition = ruleGroupHasProgressRequirements(ref.completionRules)
|
|
211
|
+
? ref.completionRules
|
|
212
|
+
: ref.eligibilityRules;
|
|
213
|
+
if (!ruleGroupHasProgressRequirements(composition))
|
|
214
|
+
return 0;
|
|
215
|
+
return evaluateRuleProgress({
|
|
216
|
+
payload,
|
|
217
|
+
req,
|
|
218
|
+
rules: composition,
|
|
219
|
+
userId,
|
|
220
|
+
scopeId,
|
|
221
|
+
ladderRank,
|
|
222
|
+
progressVisited: visited,
|
|
223
|
+
});
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
type: 'metric-minimum',
|
|
228
|
+
async evaluate({ payload, rule, userId, scopeId }) {
|
|
229
|
+
const resolvedId = await resolveMetricId({ payload, rule });
|
|
230
|
+
if (!resolvedId)
|
|
231
|
+
return false;
|
|
232
|
+
const minimum = Number(rule.minimum ?? 0);
|
|
233
|
+
const value = await metricValue({ payload, userId, scopeId, metricId: resolvedId });
|
|
234
|
+
return value >= minimum;
|
|
235
|
+
},
|
|
236
|
+
async progress({ payload, rule, userId, scopeId }) {
|
|
237
|
+
const resolvedId = await resolveMetricId({ payload, rule });
|
|
238
|
+
if (!resolvedId)
|
|
239
|
+
return 0;
|
|
240
|
+
const minimum = Number(rule.minimum ?? 0);
|
|
241
|
+
if (!(minimum > 0))
|
|
242
|
+
return 1;
|
|
243
|
+
const value = await metricValue({ payload, userId, scopeId, metricId: resolvedId });
|
|
244
|
+
return clamp01(value / minimum);
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
type: 'event-count',
|
|
249
|
+
async evaluate({ payload, rule, userId, scopeId }) {
|
|
250
|
+
const eventTypeId = await resolveEventTypeId({ payload, rule });
|
|
251
|
+
if (!eventTypeId)
|
|
252
|
+
return false;
|
|
253
|
+
const needed = Number(rule.count ?? 1);
|
|
254
|
+
const result = await payload.count({
|
|
255
|
+
collection: collectionOf('logs'),
|
|
256
|
+
overrideAccess: true,
|
|
257
|
+
where: {
|
|
258
|
+
and: [userScopeIncludingUnscopedWhere(userId, scopeId), { type: { equals: eventTypeId } }],
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
return result.totalDocs >= needed;
|
|
262
|
+
},
|
|
263
|
+
async progress({ payload, rule, userId, scopeId }) {
|
|
264
|
+
const eventTypeId = await resolveEventTypeId({ payload, rule });
|
|
265
|
+
if (!eventTypeId)
|
|
266
|
+
return 0;
|
|
267
|
+
const needed = Number(rule.count ?? 1);
|
|
268
|
+
if (!(needed > 0))
|
|
269
|
+
return 1;
|
|
270
|
+
const result = await payload.count({
|
|
271
|
+
collection: collectionOf('logs'),
|
|
272
|
+
overrideAccess: true,
|
|
273
|
+
where: {
|
|
274
|
+
and: [userScopeIncludingUnscopedWhere(userId, scopeId), { type: { equals: eventTypeId } }],
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
return clamp01(result.totalDocs / needed);
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
];
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { PayloadRequest } from 'payload';
|
|
2
|
+
export type RuleGroup = {
|
|
3
|
+
combinator?: 'and' | 'or' | null;
|
|
4
|
+
rules?: Record<string, unknown>[] | null;
|
|
5
|
+
};
|
|
6
|
+
export declare function normalizeRuleGroup(input: RuleGroup | Record<string, unknown>[] | null | undefined): {
|
|
7
|
+
combinator: 'and' | 'or';
|
|
8
|
+
rules: Record<string, unknown>[];
|
|
9
|
+
};
|
|
10
|
+
/** True when the tree has at least one non-gate requirement (nested achievements, counts, metrics). */
|
|
11
|
+
export declare function ruleGroupHasProgressRequirements(input: RuleGroup | Record<string, unknown>[] | null | undefined): boolean;
|
|
12
|
+
export type AchievementCompleteRef = {
|
|
13
|
+
id: string | null;
|
|
14
|
+
slug: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Achievement-complete leaves in document order (nested groups included).
|
|
18
|
+
* First appearance wins when the same achievement is listed twice.
|
|
19
|
+
*/
|
|
20
|
+
export declare function collectAchievementCompleteRefs(input: RuleGroup | Record<string, unknown>[] | null | undefined): AchievementCompleteRef[];
|
|
21
|
+
export declare function evaluateRules(args: {
|
|
22
|
+
payload: PayloadRequest['payload'];
|
|
23
|
+
req: PayloadRequest;
|
|
24
|
+
rules: RuleGroup | Record<string, unknown>[] | null | undefined;
|
|
25
|
+
userId: string;
|
|
26
|
+
scopeId: string | null;
|
|
27
|
+
/** Rank unlocked so far during a ladder walk (`null` = none). */
|
|
28
|
+
ladderRank?: number | null;
|
|
29
|
+
}): Promise<boolean>;
|
|
30
|
+
/**
|
|
31
|
+
* Fractional progress (0..1) toward satisfying a rule tree.
|
|
32
|
+
*
|
|
33
|
+
* Derived from the tree — no catalog `weight` field:
|
|
34
|
+
* - **AND**: equal average of *requirement* children (`tier-at-least` is a gate and is skipped).
|
|
35
|
+
* - **OR**: best child (max).
|
|
36
|
+
* - Leaves: rule-type `progress` when provided; `achievement-complete` rolls up nested
|
|
37
|
+
* eligibility when the grant is missing; otherwise `evaluate` → 0|1.
|
|
38
|
+
*/
|
|
39
|
+
export declare function evaluateRuleProgress(args: {
|
|
40
|
+
payload: PayloadRequest['payload'];
|
|
41
|
+
req: PayloadRequest;
|
|
42
|
+
rules: RuleGroup | Record<string, unknown>[] | null | undefined;
|
|
43
|
+
userId: string;
|
|
44
|
+
scopeId: string | null;
|
|
45
|
+
ladderRank?: number | null;
|
|
46
|
+
progressVisited?: Set<string>;
|
|
47
|
+
}): Promise<number>;
|
|
48
|
+
//# sourceMappingURL=evaluateRules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluateRules.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/evaluateRules.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAI7C,MAAM,MAAM,SAAS,GAAG;IACtB,UAAU,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,IAAI,CAAA;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,CAAA;CACzC,CAAA;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,GAC9D;IAAE,UAAU,EAAE,KAAK,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;CAAE,CAOhE;AAED,uGAAuG;AACvG,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,GAC9D,OAAO,CAET;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,GAC9D,sBAAsB,EAAE,CA4B1B;AAgBD,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,OAAO,EAAE,cAAc,CAAC,SAAS,CAAC,CAAA;IAClC,GAAG,EAAE,cAAc,CAAA;IACnB,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,CAAA;IAC/D,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B,GAAG,OAAO,CAAC,OAAO,CAAC,CAUnB;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,OAAO,EAAE,cAAc,CAAC,SAAS,CAAC,CAAA;IAClC,GAAG,EAAE,cAAc,CAAA;IACnB,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,CAAA;IAC/D,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAC9B,GAAG,OAAO,CAAC,MAAM,CAAC,CAWlB"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { relationId } from './relationId';
|
|
2
|
+
import { getRuleTypeMap } from '../../extensions/registry';
|
|
3
|
+
export function normalizeRuleGroup(input) {
|
|
4
|
+
if (!input)
|
|
5
|
+
return { combinator: 'and', rules: [] };
|
|
6
|
+
if (Array.isArray(input))
|
|
7
|
+
return { combinator: 'and', rules: input };
|
|
8
|
+
return {
|
|
9
|
+
combinator: input.combinator === 'or' ? 'or' : 'and',
|
|
10
|
+
rules: input.rules ?? [],
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/** True when the tree has at least one non-gate requirement (nested achievements, counts, metrics). */
|
|
14
|
+
export function ruleGroupHasProgressRequirements(input) {
|
|
15
|
+
return normalizeRuleGroup(input).rules.some((rule) => contributesToProgress(rule));
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Achievement-complete leaves in document order (nested groups included).
|
|
19
|
+
* First appearance wins when the same achievement is listed twice.
|
|
20
|
+
*/
|
|
21
|
+
export function collectAchievementCompleteRefs(input) {
|
|
22
|
+
const out = [];
|
|
23
|
+
const seen = new Set();
|
|
24
|
+
const walk = (rules) => {
|
|
25
|
+
for (const rule of rules) {
|
|
26
|
+
if (rule.type === 'group' && Array.isArray(rule.rules)) {
|
|
27
|
+
walk(rule.rules);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (rule.type !== 'achievement-complete')
|
|
31
|
+
continue;
|
|
32
|
+
const id = relationId(rule.achievement);
|
|
33
|
+
const populatedSlug = rule.achievement && typeof rule.achievement === 'object' && 'slug' in rule.achievement
|
|
34
|
+
? String(rule.achievement.slug ?? '')
|
|
35
|
+
: '';
|
|
36
|
+
const slug = (typeof rule.achievementSlug === 'string' ? rule.achievementSlug : '') || populatedSlug;
|
|
37
|
+
const key = id || slug;
|
|
38
|
+
if (!key || seen.has(key))
|
|
39
|
+
continue;
|
|
40
|
+
seen.add(key);
|
|
41
|
+
out.push({ id, slug });
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
walk(normalizeRuleGroup(input).rules);
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
function clamp01(value) {
|
|
48
|
+
if (!Number.isFinite(value))
|
|
49
|
+
return 0;
|
|
50
|
+
return Math.max(0, Math.min(1, value));
|
|
51
|
+
}
|
|
52
|
+
function contributesToProgress(rule) {
|
|
53
|
+
if (rule.type === 'group') {
|
|
54
|
+
const children = Array.isArray(rule.rules) ? rule.rules : [];
|
|
55
|
+
return children.some((child) => contributesToProgress(child));
|
|
56
|
+
}
|
|
57
|
+
const impl = getRuleTypeMap().get(String(rule.type ?? ''));
|
|
58
|
+
return impl?.progressRole !== 'gate';
|
|
59
|
+
}
|
|
60
|
+
export async function evaluateRules(args) {
|
|
61
|
+
return evaluateGroup({
|
|
62
|
+
payload: args.payload,
|
|
63
|
+
req: args.req,
|
|
64
|
+
group: normalizeRuleGroup(args.rules),
|
|
65
|
+
userId: args.userId,
|
|
66
|
+
scopeId: args.scopeId,
|
|
67
|
+
ladderRank: args.ladderRank,
|
|
68
|
+
isRoot: true,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Fractional progress (0..1) toward satisfying a rule tree.
|
|
73
|
+
*
|
|
74
|
+
* Derived from the tree — no catalog `weight` field:
|
|
75
|
+
* - **AND**: equal average of *requirement* children (`tier-at-least` is a gate and is skipped).
|
|
76
|
+
* - **OR**: best child (max).
|
|
77
|
+
* - Leaves: rule-type `progress` when provided; `achievement-complete` rolls up nested
|
|
78
|
+
* eligibility when the grant is missing; otherwise `evaluate` → 0|1.
|
|
79
|
+
*/
|
|
80
|
+
export async function evaluateRuleProgress(args) {
|
|
81
|
+
return progressGroup({
|
|
82
|
+
payload: args.payload,
|
|
83
|
+
req: args.req,
|
|
84
|
+
group: normalizeRuleGroup(args.rules),
|
|
85
|
+
userId: args.userId,
|
|
86
|
+
scopeId: args.scopeId,
|
|
87
|
+
ladderRank: args.ladderRank,
|
|
88
|
+
progressVisited: args.progressVisited,
|
|
89
|
+
isRoot: true,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
async function evaluateGroup(args) {
|
|
93
|
+
const { combinator, rules } = args.group;
|
|
94
|
+
if (rules.length === 0) {
|
|
95
|
+
if (args.isRoot)
|
|
96
|
+
return true;
|
|
97
|
+
return combinator !== 'or';
|
|
98
|
+
}
|
|
99
|
+
const map = getRuleTypeMap();
|
|
100
|
+
const evalNode = async (rule) => {
|
|
101
|
+
if (rule.type === 'group') {
|
|
102
|
+
return evaluateGroup({
|
|
103
|
+
payload: args.payload,
|
|
104
|
+
req: args.req,
|
|
105
|
+
group: {
|
|
106
|
+
combinator: rule.combinator === 'or' ? 'or' : 'and',
|
|
107
|
+
rules: Array.isArray(rule.rules) ? rule.rules : [],
|
|
108
|
+
},
|
|
109
|
+
userId: args.userId,
|
|
110
|
+
scopeId: args.scopeId,
|
|
111
|
+
ladderRank: args.ladderRank,
|
|
112
|
+
isRoot: false,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
const type = String(rule.type ?? '');
|
|
116
|
+
const impl = map.get(type);
|
|
117
|
+
if (!impl)
|
|
118
|
+
return false;
|
|
119
|
+
return Boolean(await impl.evaluate({
|
|
120
|
+
payload: args.payload,
|
|
121
|
+
req: args.req,
|
|
122
|
+
rule,
|
|
123
|
+
userId: args.userId,
|
|
124
|
+
scopeId: args.scopeId,
|
|
125
|
+
ladderRank: args.ladderRank,
|
|
126
|
+
}));
|
|
127
|
+
};
|
|
128
|
+
if (combinator === 'or') {
|
|
129
|
+
for (const rule of rules) {
|
|
130
|
+
if (await evalNode(rule))
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
for (const rule of rules) {
|
|
136
|
+
if (!(await evalNode(rule)))
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
async function progressGroup(args) {
|
|
142
|
+
const { combinator, rules } = args.group;
|
|
143
|
+
if (rules.length === 0) {
|
|
144
|
+
if (args.isRoot)
|
|
145
|
+
return 1;
|
|
146
|
+
return combinator === 'or' ? 0 : 1;
|
|
147
|
+
}
|
|
148
|
+
const map = getRuleTypeMap();
|
|
149
|
+
const evalArgs = (rule) => ({
|
|
150
|
+
payload: args.payload,
|
|
151
|
+
req: args.req,
|
|
152
|
+
rule,
|
|
153
|
+
userId: args.userId,
|
|
154
|
+
scopeId: args.scopeId,
|
|
155
|
+
ladderRank: args.ladderRank,
|
|
156
|
+
progressVisited: args.progressVisited,
|
|
157
|
+
});
|
|
158
|
+
const progressNode = async (rule) => {
|
|
159
|
+
if (rule.type === 'group') {
|
|
160
|
+
return progressGroup({
|
|
161
|
+
payload: args.payload,
|
|
162
|
+
req: args.req,
|
|
163
|
+
group: {
|
|
164
|
+
combinator: rule.combinator === 'or' ? 'or' : 'and',
|
|
165
|
+
rules: Array.isArray(rule.rules) ? rule.rules : [],
|
|
166
|
+
},
|
|
167
|
+
userId: args.userId,
|
|
168
|
+
scopeId: args.scopeId,
|
|
169
|
+
ladderRank: args.ladderRank,
|
|
170
|
+
progressVisited: args.progressVisited,
|
|
171
|
+
isRoot: false,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
const type = String(rule.type ?? '');
|
|
175
|
+
const impl = map.get(type);
|
|
176
|
+
if (!impl)
|
|
177
|
+
return 0;
|
|
178
|
+
if (impl.progress) {
|
|
179
|
+
return clamp01(Number(await impl.progress(evalArgs(rule))));
|
|
180
|
+
}
|
|
181
|
+
const passed = await impl.evaluate(evalArgs(rule));
|
|
182
|
+
return passed ? 1 : 0;
|
|
183
|
+
};
|
|
184
|
+
if (combinator === 'or') {
|
|
185
|
+
let best = 0;
|
|
186
|
+
for (const rule of rules) {
|
|
187
|
+
best = Math.max(best, await progressNode(rule));
|
|
188
|
+
if (best >= 1)
|
|
189
|
+
return 1;
|
|
190
|
+
}
|
|
191
|
+
return best;
|
|
192
|
+
}
|
|
193
|
+
const requirements = rules.filter((rule) => contributesToProgress(rule));
|
|
194
|
+
if (requirements.length === 0) {
|
|
195
|
+
for (const rule of rules) {
|
|
196
|
+
if ((await progressNode(rule)) < 1)
|
|
197
|
+
return 0;
|
|
198
|
+
}
|
|
199
|
+
return 1;
|
|
200
|
+
}
|
|
201
|
+
let sum = 0;
|
|
202
|
+
for (const rule of requirements) {
|
|
203
|
+
sum += await progressNode(rule);
|
|
204
|
+
}
|
|
205
|
+
return clamp01(sum / requirements.length);
|
|
206
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Payload, PayloadRequest } from 'payload';
|
|
2
|
+
export type EventTypeFlags = {
|
|
3
|
+
requiresActor: boolean;
|
|
4
|
+
requiresMetric: boolean;
|
|
5
|
+
};
|
|
6
|
+
/** Read flags from a loaded event-type document (or populated relationship). */
|
|
7
|
+
export declare function readEventTypeFlags(doc: unknown): EventTypeFlags | null;
|
|
8
|
+
/** Flags from an event-type catalog row (or its id). */
|
|
9
|
+
export declare function getEventTypeFlags(args: {
|
|
10
|
+
payload: Payload;
|
|
11
|
+
req?: PayloadRequest;
|
|
12
|
+
typeIdOrDoc: unknown;
|
|
13
|
+
}): Promise<EventTypeFlags>;
|
|
14
|
+
export declare function eventTypeRequiresActor(args: {
|
|
15
|
+
payload: Payload;
|
|
16
|
+
req?: PayloadRequest;
|
|
17
|
+
typeIdOrDoc: unknown;
|
|
18
|
+
}): Promise<boolean>;
|
|
19
|
+
export declare function eventTypeRequiresMetric(args: {
|
|
20
|
+
payload: Payload;
|
|
21
|
+
req?: PayloadRequest;
|
|
22
|
+
typeIdOrDoc: unknown;
|
|
23
|
+
}): Promise<boolean>;
|
|
24
|
+
//# sourceMappingURL=eventTypeRequiresActor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eventTypeRequiresActor.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/eventTypeRequiresActor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAKtD,MAAM,MAAM,cAAc,GAAG;IAC3B,aAAa,EAAE,OAAO,CAAA;IACtB,cAAc,EAAE,OAAO,CAAA;CACxB,CAAA;AAED,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CAUtE;AAMD,wDAAwD;AACxD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,cAAc,CAAA;IACpB,WAAW,EAAE,OAAO,CAAA;CACrB,GAAG,OAAO,CAAC,cAAc,CAAC,CAsB1B;AAED,wBAAsB,sBAAsB,CAAC,IAAI,EAAE;IACjD,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,cAAc,CAAA;IACpB,WAAW,EAAE,OAAO,CAAA;CACrB,GAAG,OAAO,CAAC,OAAO,CAAC,CAEnB;AAED,wBAAsB,uBAAuB,CAAC,IAAI,EAAE;IAClD,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,cAAc,CAAA;IACpB,WAAW,EAAE,OAAO,CAAA;CACrB,GAAG,OAAO,CAAC,OAAO,CAAC,CAEnB"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { collectionOf } from '../../collections/helpers';
|
|
2
|
+
import { relationId } from './relationId';
|
|
3
|
+
/** Read flags from a loaded event-type document (or populated relationship). */
|
|
4
|
+
export function readEventTypeFlags(doc) {
|
|
5
|
+
if (!doc || typeof doc !== 'object')
|
|
6
|
+
return null;
|
|
7
|
+
const row = doc;
|
|
8
|
+
const hasFlagKeys = 'requiresActor' in row || 'requiresMetric' in row || typeof row.slug === 'string';
|
|
9
|
+
if (!hasFlagKeys)
|
|
10
|
+
return null;
|
|
11
|
+
return {
|
|
12
|
+
requiresActor: Boolean(row.requiresActor),
|
|
13
|
+
requiresMetric: Boolean(row.requiresMetric) || row.slug === 'metric.delta',
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function flagsFromDoc(doc) {
|
|
17
|
+
return readEventTypeFlags(doc);
|
|
18
|
+
}
|
|
19
|
+
/** Flags from an event-type catalog row (or its id). */
|
|
20
|
+
export async function getEventTypeFlags(args) {
|
|
21
|
+
const embedded = flagsFromDoc(args.typeIdOrDoc);
|
|
22
|
+
if (embedded && args.typeIdOrDoc && typeof args.typeIdOrDoc === 'object') {
|
|
23
|
+
const row = args.typeIdOrDoc;
|
|
24
|
+
if ('requiresActor' in row || 'requiresMetric' in row || 'slug' in row) {
|
|
25
|
+
return embedded;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const typeId = relationId(args.typeIdOrDoc);
|
|
29
|
+
if (!typeId)
|
|
30
|
+
return { requiresActor: false, requiresMetric: false };
|
|
31
|
+
const found = (await args.payload.findByID({
|
|
32
|
+
collection: collectionOf('eventTypes'),
|
|
33
|
+
id: typeId,
|
|
34
|
+
depth: 0,
|
|
35
|
+
overrideAccess: true,
|
|
36
|
+
req: args.req,
|
|
37
|
+
select: { requiresActor: true, requiresMetric: true, slug: true },
|
|
38
|
+
}));
|
|
39
|
+
return flagsFromDoc(found) ?? { requiresActor: false, requiresMetric: false };
|
|
40
|
+
}
|
|
41
|
+
export async function eventTypeRequiresActor(args) {
|
|
42
|
+
return (await getEventTypeFlags(args)).requiresActor;
|
|
43
|
+
}
|
|
44
|
+
export async function eventTypeRequiresMetric(args) {
|
|
45
|
+
return (await getEventTypeFlags(args)).requiresMetric;
|
|
46
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { PaginatedDocs, Payload, PayloadRequest } from 'payload';
|
|
2
|
+
type LeanCatalog = {
|
|
3
|
+
id: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
slug?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
};
|
|
8
|
+
type LeanTier = LeanCatalog & {
|
|
9
|
+
rank?: number;
|
|
10
|
+
scopeId?: string | null;
|
|
11
|
+
};
|
|
12
|
+
export type GetUserProgressPaging = {
|
|
13
|
+
limit?: number;
|
|
14
|
+
page?: number;
|
|
15
|
+
requestsLimit?: number;
|
|
16
|
+
};
|
|
17
|
+
declare function leanGrant(doc: Record<string, unknown>): {
|
|
18
|
+
id: unknown;
|
|
19
|
+
achievement: LeanCatalog | null;
|
|
20
|
+
completedAt: {} | null;
|
|
21
|
+
note: {} | null;
|
|
22
|
+
scopeId: string | null;
|
|
23
|
+
updatedAt: {} | null;
|
|
24
|
+
createdAt: {} | null;
|
|
25
|
+
};
|
|
26
|
+
declare function leanRequest(doc: Record<string, unknown>): {
|
|
27
|
+
id: unknown;
|
|
28
|
+
status: {} | null;
|
|
29
|
+
achievement: LeanCatalog | null;
|
|
30
|
+
reason: {} | null;
|
|
31
|
+
updatedAt: {} | null;
|
|
32
|
+
createdAt: {} | null;
|
|
33
|
+
};
|
|
34
|
+
export type UserProgressMeResponse = PaginatedDocs<ReturnType<typeof leanGrant>> & {
|
|
35
|
+
/** Ladder-derived current tier(s). */
|
|
36
|
+
tiers: LeanTier[];
|
|
37
|
+
requests: ReturnType<typeof leanRequest>[];
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Current-user snapshot. Top-level Payload `find` shape for **grants**;
|
|
41
|
+
* ladder-derived `tiers` and lean `requests` ride along.
|
|
42
|
+
*/
|
|
43
|
+
export declare function getUserProgress(args: {
|
|
44
|
+
payload: Payload;
|
|
45
|
+
req?: PayloadRequest;
|
|
46
|
+
userId: string;
|
|
47
|
+
scopeId?: string | null;
|
|
48
|
+
paging?: GetUserProgressPaging;
|
|
49
|
+
/** Content locale for localized achievement / tier names (falls back to `req.locale`). */
|
|
50
|
+
locale?: string;
|
|
51
|
+
}): Promise<UserProgressMeResponse>;
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=getUserProgress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getUserProgress.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/getUserProgress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAOrE,KAAK,WAAW,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AACrF,KAAK,QAAQ,GAAG,WAAW,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAA;AAExE,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAiBD,iBAAS,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;EAU9C;AAED,iBAAS,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;EAShD;AAoBD,MAAM,MAAM,sBAAsB,GAAG,aAAa,CAAC,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,GAAG;IACjF,sCAAsC;IACtC,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,QAAQ,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,EAAE,CAAA;CAC3C,CAAA;AAkED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,cAAc,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,MAAM,CAAC,EAAE,qBAAqB,CAAA;IAC9B,0FAA0F;IAC1F,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAiDlC"}
|