@dappermountain/payload-plugin-achievements 0.3.0 → 0.4.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/README.md +91 -12
- package/dist/admin/SubjectField.d.ts +14 -0
- package/dist/admin/SubjectField.d.ts.map +1 -0
- package/dist/admin/SubjectField.js +38 -0
- package/dist/admin/useEventTypeFlags.d.ts +2 -0
- package/dist/admin/useEventTypeFlags.d.ts.map +1 -1
- package/dist/admin/useEventTypeFlags.js +21 -3
- package/dist/cli/reconcile.d.ts +1 -1
- package/dist/cli/reconcile.d.ts.map +1 -1
- package/dist/collections/EventTypes/index.d.ts.map +1 -1
- package/dist/collections/EventTypes/index.js +30 -0
- package/dist/collections/Logs/fields/validators.d.ts +1 -0
- package/dist/collections/Logs/fields/validators.d.ts.map +1 -1
- package/dist/collections/Logs/fields/validators.js +37 -1
- package/dist/collections/Logs/hooks/ensureTypeRequirements.d.ts.map +1 -1
- package/dist/collections/Logs/hooks/ensureTypeRequirements.js +22 -13
- package/dist/collections/Logs/index.d.ts.map +1 -1
- package/dist/collections/Logs/index.js +34 -3
- package/dist/defaults.d.ts +5 -1
- package/dist/defaults.d.ts.map +1 -1
- package/dist/defaults.js +17 -1
- package/dist/endpoints/me.d.ts +2 -1
- package/dist/endpoints/me.d.ts.map +1 -1
- package/dist/endpoints/me.js +7 -1
- package/dist/exports/client.d.ts +1 -0
- package/dist/exports/client.d.ts.map +1 -1
- package/dist/exports/client.js +1 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/seed/index.d.ts +7 -0
- package/dist/seed/index.d.ts.map +1 -1
- package/dist/seed/index.js +4 -0
- package/dist/services/achievement/buildUnlockRequirementLeaves.d.ts +48 -0
- package/dist/services/achievement/buildUnlockRequirementLeaves.d.ts.map +1 -0
- package/dist/services/achievement/buildUnlockRequirementLeaves.js +192 -0
- package/dist/services/achievement/duration.js +1 -1
- package/dist/services/achievement/eventTypeRequiresActor.d.ts +17 -0
- package/dist/services/achievement/eventTypeRequiresActor.d.ts.map +1 -1
- package/dist/services/achievement/eventTypeRequiresActor.js +56 -5
- package/dist/services/achievement/getUserProgress.d.ts +7 -0
- package/dist/services/achievement/getUserProgress.d.ts.map +1 -1
- package/dist/services/achievement/getUserProgress.js +11 -1
- package/dist/services/achievement/index.d.ts +7 -0
- package/dist/services/achievement/index.d.ts.map +1 -1
- package/dist/services/achievement/index.js +3 -0
- package/dist/services/achievement/loadUserProgressReviews.d.ts +23 -0
- package/dist/services/achievement/loadUserProgressReviews.d.ts.map +1 -0
- package/dist/services/achievement/loadUserProgressReviews.js +79 -0
- package/dist/services/achievement/logSubject.d.ts +13 -0
- package/dist/services/achievement/logSubject.d.ts.map +1 -0
- package/dist/services/achievement/logSubject.js +20 -0
- package/dist/services/achievement/recordEvent.d.ts +6 -0
- package/dist/services/achievement/recordEvent.d.ts.map +1 -1
- package/dist/services/achievement/recordEvent.js +32 -4
- package/dist/types.d.ts +19 -3
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -7
package/dist/defaults.js
CHANGED
|
@@ -3,6 +3,20 @@ export const DEFAULT_USERS_COLLECTION = 'users';
|
|
|
3
3
|
export const DEFAULT_ME_ENDPOINT_PATH = '/achievements/me';
|
|
4
4
|
export const DEFAULT_LEADERBOARD_ENDPOINT_PATH = '/achievements/leaderboard';
|
|
5
5
|
export const DEFAULT_RECONCILE_ENDPOINT_PATH = '/achievements/reconcile';
|
|
6
|
+
/** Unique, non-empty collection slugs from plugin `subjects.collections`. */
|
|
7
|
+
export function resolveSubjectCollections(options) {
|
|
8
|
+
const raw = options?.subjects?.collections ?? [];
|
|
9
|
+
const seen = new Set();
|
|
10
|
+
const out = [];
|
|
11
|
+
for (const slug of raw) {
|
|
12
|
+
const trimmed = typeof slug === 'string' ? slug.trim() : '';
|
|
13
|
+
if (!trimmed || seen.has(trimmed))
|
|
14
|
+
continue;
|
|
15
|
+
seen.add(trimmed);
|
|
16
|
+
out.push(trimmed);
|
|
17
|
+
}
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
6
20
|
function normalizeEndpointPath(path, fallback) {
|
|
7
21
|
const trimmed = path.trim();
|
|
8
22
|
if (!trimmed)
|
|
@@ -31,14 +45,16 @@ export function resolveReconcileEndpointPath(path) {
|
|
|
31
45
|
return normalizeEndpointPath(path, DEFAULT_RECONCILE_ENDPOINT_PATH);
|
|
32
46
|
}
|
|
33
47
|
export function resolveOptions(options = {}) {
|
|
48
|
+
const { subjects: _subjects, ...rest } = options;
|
|
34
49
|
return {
|
|
35
|
-
...
|
|
50
|
+
...rest,
|
|
36
51
|
enabled: options.enabled !== false,
|
|
37
52
|
seedSystemCatalog: options.seedSystemCatalog !== false,
|
|
38
53
|
usersCollectionSlug: options.usersCollectionSlug ?? DEFAULT_USERS_COLLECTION,
|
|
39
54
|
users: {
|
|
40
55
|
includeJoins: options.users?.includeJoins !== false,
|
|
41
56
|
},
|
|
57
|
+
subjectCollections: resolveSubjectCollections(options),
|
|
42
58
|
collectionSlugs: resolveCollectionSlugs(options),
|
|
43
59
|
adminGroup: resolveAdminGroup(options),
|
|
44
60
|
endpoints: {
|
package/dist/endpoints/me.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { Endpoint } from 'payload';
|
|
2
2
|
/**
|
|
3
3
|
* `GET` current user’s snapshot. Body is Payload `find`-shaped for **grants**,
|
|
4
|
-
* plus ladder-derived `tiers` and lean `requests`. Optional `?scope=` / `limit` / `page
|
|
4
|
+
* plus ladder-derived `tiers` and lean `requests`. Optional `?scope=` / `limit` / `page`
|
|
5
|
+
* / `reviews=1` (pending achievement keys + open/rejected tier requests).
|
|
5
6
|
*/
|
|
6
7
|
export declare function buildMeEndpoint(path: string): Endpoint;
|
|
7
8
|
//# sourceMappingURL=me.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"me.d.ts","sourceRoot":"","sources":["../../src/endpoints/me.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAkB,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"me.d.ts","sourceRoot":"","sources":["../../src/endpoints/me.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAkB,MAAM,SAAS,CAAA;AAoCvD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAsBtD"}
|
package/dist/endpoints/me.js
CHANGED
|
@@ -24,9 +24,14 @@ function pagingFromRequest(req) {
|
|
|
24
24
|
requestsLimit: positiveInt(params?.get('requestsLimit') ?? null, 20),
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
|
+
function reviewsFromRequest(req) {
|
|
28
|
+
const raw = requestUrl(req)?.searchParams.get('reviews');
|
|
29
|
+
return raw === '1' || raw === 'true';
|
|
30
|
+
}
|
|
27
31
|
/**
|
|
28
32
|
* `GET` current user’s snapshot. Body is Payload `find`-shaped for **grants**,
|
|
29
|
-
* plus ladder-derived `tiers` and lean `requests`. Optional `?scope=` / `limit` / `page
|
|
33
|
+
* plus ladder-derived `tiers` and lean `requests`. Optional `?scope=` / `limit` / `page`
|
|
34
|
+
* / `reviews=1` (pending achievement keys + open/rejected tier requests).
|
|
30
35
|
*/
|
|
31
36
|
export function buildMeEndpoint(path) {
|
|
32
37
|
return {
|
|
@@ -43,6 +48,7 @@ export function buildMeEndpoint(path) {
|
|
|
43
48
|
userId,
|
|
44
49
|
scopeId: scopeFromRequest(req),
|
|
45
50
|
paging: pagingFromRequest(req),
|
|
51
|
+
include: reviewsFromRequest(req) ? { reviews: true } : undefined,
|
|
46
52
|
});
|
|
47
53
|
return Response.json(body);
|
|
48
54
|
},
|
package/dist/exports/client.d.ts
CHANGED
|
@@ -3,4 +3,5 @@ export { LucideIconField } from '../admin/LucideIconField.js';
|
|
|
3
3
|
export { MetricChangeField } from '../admin/MetricChangeField.js';
|
|
4
4
|
export { MetricField } from '../admin/MetricField.js';
|
|
5
5
|
export { ReconcileProgressionButton } from '../admin/ReconcileProgressionButton.js';
|
|
6
|
+
export { SubjectField } from '../admin/SubjectField.js';
|
|
6
7
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/exports/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AACrD,OAAO,EAAE,0BAA0B,EAAE,MAAM,wCAAwC,CAAA"}
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/exports/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AACrD,OAAO,EAAE,0BAA0B,EAAE,MAAM,wCAAwC,CAAA;AACnF,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA"}
|
package/dist/exports/client.js
CHANGED
|
@@ -3,3 +3,4 @@ export { LucideIconField } from '../admin/LucideIconField.js';
|
|
|
3
3
|
export { MetricChangeField } from '../admin/MetricChangeField.js';
|
|
4
4
|
export { MetricField } from '../admin/MetricField.js';
|
|
5
5
|
export { ReconcileProgressionButton } from '../admin/ReconcileProgressionButton.js';
|
|
6
|
+
export { SubjectField } from '../admin/SubjectField.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -3,12 +3,13 @@ export type { EngineEventTypeSlug, EngineMetricSlug } from './catalog.js';
|
|
|
3
3
|
export { achievementPlugin } from './plugin.js';
|
|
4
4
|
export { seedAchievementCatalog, seedAchievements } from './seed/index.js';
|
|
5
5
|
export type { AchievementSeedAchievement, AchievementSeedCatalogRow, AchievementSeedInput, AchievementSeedLocalizedText, AchievementSeedTier, } from './seed/index.js';
|
|
6
|
-
export { evaluateRules, evaluateRuleProgress, collectAchievementCompleteRefs, buildCatalogProgressGroups, getUserProgress, getMetricLeaderboard, grantAchievement, loadMetricDoc, recordEvent, recordLog, recordMetricChange, rebuildMetricBalance, rebuildMetricBalancesForUser, reconcileProgression, reconcileUserProgression, releasePendingAchievementReviews, releasePendingTierReviews, resolveCurrentTier, resolveElapsedAnchor, resolveMetricValue, resolveTierProgress, reviewAchievementRequest, submitAchievementRequest, syncTierChangedLog, syncTierProgression, syncCompositeProgression, catalogRef, transitionLogData, transitionToId, validateRuleGroup, validateRuleNode, } from './services/achievement/index.js';
|
|
7
|
-
export type { ResolvedTier, RuleGroup, TierLadderProgress, CatalogProgressGroup, CatalogProgressItem, CatalogRef, TransitionLogData, MetricLeaderboardResult, MetricLeaderboardRow, ReconcileProgressionArgs, ReconcileProgressionResult, ReleaseReviewGateResult, } from './services/achievement/index.js';
|
|
8
|
-
export type { BuiltInEventType, BuiltInRuleType, AchievementCollectionKey, AchievementCollectionOverride, AchievementCollectionsOptions, AchievementEndpointsOptions, AchievementMetricAnchor, AchievementMetricAnchorConfig, AchievementMetricAnchorEntry, AchievementPluginOptions, AchievementRuleType, AchievementScopeConfig, AchievementUsersOptions, } from './types.js';
|
|
6
|
+
export { evaluateRules, evaluateRuleProgress, collectAchievementCompleteRefs, buildCatalogProgressGroups, buildUnlockRequirementLeaves, getUserProgress, loadUserProgressReviews, getMetricLeaderboard, grantAchievement, loadMetricDoc, recordEvent, recordLog, recordMetricChange, rebuildMetricBalance, rebuildMetricBalancesForUser, reconcileProgression, reconcileUserProgression, releasePendingAchievementReviews, releasePendingTierReviews, resolveCurrentTier, resolveElapsedAnchor, resolveMetricValue, resolveTierProgress, reviewAchievementRequest, submitAchievementRequest, syncTierChangedLog, syncTierProgression, syncCompositeProgression, catalogRef, transitionLogData, transitionToId, logSubject, readLogSubject, validateRuleGroup, validateRuleNode, } from './services/achievement/index.js';
|
|
7
|
+
export type { ResolvedTier, RuleGroup, TierLadderProgress, CatalogProgressGroup, CatalogProgressItem, UnlockRequirementLeaf, UnlockRequirementRelation, GetUserProgressInclude, UserProgressMeResponse, UserProgressReviews, UserProgressTierRequest, CatalogRef, TransitionLogData, LogSubjectRef, MetricLeaderboardResult, MetricLeaderboardRow, ReconcileProgressionArgs, ReconcileProgressionResult, ReleaseReviewGateResult, } from './services/achievement/index.js';
|
|
8
|
+
export type { BuiltInEventType, BuiltInRuleType, AchievementCollectionKey, AchievementCollectionOverride, AchievementCollectionsOptions, AchievementEndpointsOptions, AchievementMetricAnchor, AchievementMetricAnchorConfig, AchievementMetricAnchorEntry, AchievementPluginOptions, AchievementRuleType, AchievementScopeConfig, AchievementSubjectsOptions, AchievementUsersOptions, } from './types.js';
|
|
9
9
|
export { DEFAULT_COLLECTION_BASE_SLUGS, DEFAULT_COLLECTION_PREFIX, DEFAULT_ADMIN_GROUP } from './collections/slugs.js';
|
|
10
|
-
export { DEFAULT_ME_ENDPOINT_PATH, DEFAULT_LEADERBOARD_ENDPOINT_PATH, DEFAULT_RECONCILE_ENDPOINT_PATH, } from './defaults.js';
|
|
10
|
+
export { DEFAULT_ME_ENDPOINT_PATH, DEFAULT_LEADERBOARD_ENDPOINT_PATH, DEFAULT_RECONCILE_ENDPOINT_PATH, resolveSubjectCollections, } from './defaults.js';
|
|
11
11
|
export { collectionOf, slugOf } from './collections/helpers.js';
|
|
12
|
+
export { eachRuleLeaf } from './collections/hooks/eachRuleLeaf.js';
|
|
12
13
|
export { isAchievementPluginEnabled } from './enabled.js';
|
|
13
14
|
export { descriptionToPlaintext } from './fields/descriptionPlaintext.js';
|
|
14
15
|
export { plainTextToLexical } from './fields/plainTextToLexical.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,+BAA+B,EAC/B,kBAAkB,EAClB,cAAc,GACf,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAC1E,YAAY,EACV,0BAA0B,EAC1B,yBAAyB,EACzB,oBAAoB,EACpB,4BAA4B,EAC5B,mBAAmB,GACpB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,8BAA8B,EAC9B,0BAA0B,EAC1B,eAAe,EACf,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,SAAS,EACT,kBAAkB,EAClB,oBAAoB,EACpB,4BAA4B,EAC5B,oBAAoB,EACpB,wBAAwB,EACxB,gCAAgC,EAChC,yBAAyB,EACzB,kBAAkB,EAClB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,EACxB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,iCAAiC,CAAA;AACxC,YAAY,EACV,YAAY,EACZ,SAAS,EACT,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,UAAU,EACV,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,EACpB,wBAAwB,EACxB,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,iCAAiC,CAAA;AACxC,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,wBAAwB,EACxB,6BAA6B,EAC7B,6BAA6B,EAC7B,2BAA2B,EAC3B,uBAAuB,EACvB,6BAA6B,EAC7B,4BAA4B,EAC5B,wBAAwB,EACxB,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,6BAA6B,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AACtH,OAAO,EACL,wBAAwB,EACxB,iCAAiC,EACjC,+BAA+B,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,+BAA+B,EAC/B,kBAAkB,EAClB,cAAc,GACf,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAC1E,YAAY,EACV,0BAA0B,EAC1B,yBAAyB,EACzB,oBAAoB,EACpB,4BAA4B,EAC5B,mBAAmB,GACpB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,8BAA8B,EAC9B,0BAA0B,EAC1B,4BAA4B,EAC5B,eAAe,EACf,uBAAuB,EACvB,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,SAAS,EACT,kBAAkB,EAClB,oBAAoB,EACpB,4BAA4B,EAC5B,oBAAoB,EACpB,wBAAwB,EACxB,gCAAgC,EAChC,yBAAyB,EACzB,kBAAkB,EAClB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,EACxB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,UAAU,EACV,cAAc,EACd,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,iCAAiC,CAAA;AACxC,YAAY,EACV,YAAY,EACZ,SAAS,EACT,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,yBAAyB,EACzB,sBAAsB,EACtB,sBAAsB,EACtB,mBAAmB,EACnB,uBAAuB,EACvB,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,uBAAuB,EACvB,oBAAoB,EACpB,wBAAwB,EACxB,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,iCAAiC,CAAA;AACxC,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,wBAAwB,EACxB,6BAA6B,EAC7B,6BAA6B,EAC7B,2BAA2B,EAC3B,uBAAuB,EACvB,6BAA6B,EAC7B,4BAA4B,EAC5B,wBAAwB,EACxB,mBAAmB,EACnB,sBAAsB,EACtB,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,6BAA6B,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AACtH,OAAO,EACL,wBAAwB,EACxB,iCAAiC,EACjC,+BAA+B,EAC/B,yBAAyB,GAC1B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAA;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAA;AAClE,OAAO,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAA;AACzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
export { DEFAULT_ACHIEVEMENT_METRIC, DEFAULT_ACHIEVEMENT_METRIC_SLUG, ENGINE_EVENT_TYPES, ENGINE_METRICS, } from './catalog.js';
|
|
2
2
|
export { achievementPlugin } from './plugin.js';
|
|
3
3
|
export { seedAchievementCatalog, seedAchievements } from './seed/index.js';
|
|
4
|
-
export { evaluateRules, evaluateRuleProgress, collectAchievementCompleteRefs, buildCatalogProgressGroups, getUserProgress, getMetricLeaderboard, grantAchievement, loadMetricDoc, recordEvent, recordLog, recordMetricChange, rebuildMetricBalance, rebuildMetricBalancesForUser, reconcileProgression, reconcileUserProgression, releasePendingAchievementReviews, releasePendingTierReviews, resolveCurrentTier, resolveElapsedAnchor, resolveMetricValue, resolveTierProgress, reviewAchievementRequest, submitAchievementRequest, syncTierChangedLog, syncTierProgression, syncCompositeProgression, catalogRef, transitionLogData, transitionToId, validateRuleGroup, validateRuleNode, } from './services/achievement/index.js';
|
|
4
|
+
export { evaluateRules, evaluateRuleProgress, collectAchievementCompleteRefs, buildCatalogProgressGroups, buildUnlockRequirementLeaves, getUserProgress, loadUserProgressReviews, getMetricLeaderboard, grantAchievement, loadMetricDoc, recordEvent, recordLog, recordMetricChange, rebuildMetricBalance, rebuildMetricBalancesForUser, reconcileProgression, reconcileUserProgression, releasePendingAchievementReviews, releasePendingTierReviews, resolveCurrentTier, resolveElapsedAnchor, resolveMetricValue, resolveTierProgress, reviewAchievementRequest, submitAchievementRequest, syncTierChangedLog, syncTierProgression, syncCompositeProgression, catalogRef, transitionLogData, transitionToId, logSubject, readLogSubject, validateRuleGroup, validateRuleNode, } from './services/achievement/index.js';
|
|
5
5
|
export { DEFAULT_COLLECTION_BASE_SLUGS, DEFAULT_COLLECTION_PREFIX, DEFAULT_ADMIN_GROUP } from './collections/slugs.js';
|
|
6
|
-
export { DEFAULT_ME_ENDPOINT_PATH, DEFAULT_LEADERBOARD_ENDPOINT_PATH, DEFAULT_RECONCILE_ENDPOINT_PATH, } from './defaults.js';
|
|
6
|
+
export { DEFAULT_ME_ENDPOINT_PATH, DEFAULT_LEADERBOARD_ENDPOINT_PATH, DEFAULT_RECONCILE_ENDPOINT_PATH, resolveSubjectCollections, } from './defaults.js';
|
|
7
7
|
export { collectionOf, slugOf } from './collections/helpers.js';
|
|
8
|
+
export { eachRuleLeaf } from './collections/hooks/eachRuleLeaf.js';
|
|
8
9
|
export { isAchievementPluginEnabled } from './enabled.js';
|
|
9
10
|
export { descriptionToPlaintext } from './fields/descriptionPlaintext.js';
|
|
10
11
|
export { plainTextToLexical } from './fields/plainTextToLexical.js';
|
package/dist/seed/index.d.ts
CHANGED
|
@@ -57,6 +57,13 @@ export type AchievementSeedCatalogRow = {
|
|
|
57
57
|
requiresActor?: boolean;
|
|
58
58
|
/** Event types only — when true, logging the event requires metric + change. */
|
|
59
59
|
requiresMetric?: boolean;
|
|
60
|
+
/** Event types only — when true, logging the event requires a polymorphic `subject`. */
|
|
61
|
+
requiresSubject?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Event types only — allowed host collections for `subject.relationTo`.
|
|
64
|
+
* Empty / omit = any collection from plugin `subjects.collections`.
|
|
65
|
+
*/
|
|
66
|
+
subjectRelationTo?: string[];
|
|
60
67
|
};
|
|
61
68
|
export type AchievementSeedInput = {
|
|
62
69
|
eventTypes?: AchievementSeedCatalogRow[];
|
package/dist/seed/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/seed/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAOtC,mGAAmG;AACnG,MAAM,MAAM,4BAA4B,GAAG,MAAM,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAAE,CAAA;AAExG,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,4BAA4B,CAAA;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,4BAA4B,CAAA;IAC1C,sFAAsF;IACtF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qFAAqF;IACrF,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,8FAA8F;IAC9F,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;KAAE,CAAA;CACrG,CAAA;AAED,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,4BAA4B,CAAA;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,4BAA4B,CAAA;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;KAAE,CAAA;IACzG,qGAAqG;IACrG,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;KAAE,CAAA;CACzG,CAAA;AAED,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,4BAA4B,CAAA;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,kDAAkD;IAClD,IAAI,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAA;IAC5B,4CAA4C;IAC5C,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,qDAAqD;IACrD,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAA;IACzD,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,yEAAyE;IACzE,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,gFAAgF;IAChF,cAAc,CAAC,EAAE,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/seed/index.ts"],"names":[],"mappings":"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"}
|
package/dist/seed/index.js
CHANGED
|
@@ -74,6 +74,10 @@ async function upsertCatalog(payload, key, row) {
|
|
|
74
74
|
? {
|
|
75
75
|
requiresActor: row.requiresActor ?? false,
|
|
76
76
|
requiresMetric: row.requiresMetric ?? false,
|
|
77
|
+
requiresSubject: row.requiresSubject ?? false,
|
|
78
|
+
...(row.subjectRelationTo && row.subjectRelationTo.length > 0
|
|
79
|
+
? { subjectRelationTo: row.subjectRelationTo }
|
|
80
|
+
: { subjectRelationTo: [] }),
|
|
77
81
|
}
|
|
78
82
|
: metricComputed),
|
|
79
83
|
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Payload, PayloadRequest } from 'payload';
|
|
2
|
+
export declare const UNLOCK_REQUIREMENT_RELATION_FIELDS: {
|
|
3
|
+
readonly tier: "tiers";
|
|
4
|
+
readonly achievement: "achievements";
|
|
5
|
+
readonly metric: "metrics";
|
|
6
|
+
readonly eventType: "eventTypes";
|
|
7
|
+
};
|
|
8
|
+
export type UnlockRequirementCollectionKey = (typeof UNLOCK_REQUIREMENT_RELATION_FIELDS)[keyof typeof UNLOCK_REQUIREMENT_RELATION_FIELDS];
|
|
9
|
+
export type UnlockRequirementRelation = {
|
|
10
|
+
field: keyof typeof UNLOCK_REQUIREMENT_RELATION_FIELDS;
|
|
11
|
+
collectionKey: UnlockRequirementCollectionKey;
|
|
12
|
+
id?: string;
|
|
13
|
+
slug?: string;
|
|
14
|
+
name?: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Structured unlock/eligibility leaf — catalog names only, no host UI copy.
|
|
18
|
+
* Hosts format `label` / `detail` from these fields.
|
|
19
|
+
*/
|
|
20
|
+
export type UnlockRequirementLeaf = {
|
|
21
|
+
id: string;
|
|
22
|
+
type: string;
|
|
23
|
+
met: boolean;
|
|
24
|
+
/** 0..1 from {@link evaluateRuleProgress}. */
|
|
25
|
+
progress: number;
|
|
26
|
+
relations: UnlockRequirementRelation[];
|
|
27
|
+
target?: number;
|
|
28
|
+
unit?: string;
|
|
29
|
+
since?: string;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Walk every leaf in a rule tree, evaluate met/progress, and return catalog-backed
|
|
33
|
+
* subjects. Payload rows often carry empty `rules: []` + default combinator — those
|
|
34
|
+
* are still leaves ({@link eachRuleLeaf}).
|
|
35
|
+
*
|
|
36
|
+
* `achievement-complete` is omitted by default so hosts can keep those on catalog groups.
|
|
37
|
+
*/
|
|
38
|
+
export declare function buildUnlockRequirementLeaves(args: {
|
|
39
|
+
payload: Payload;
|
|
40
|
+
req?: PayloadRequest;
|
|
41
|
+
userId: string;
|
|
42
|
+
scopeId: string | null;
|
|
43
|
+
rules: unknown;
|
|
44
|
+
ladderRank?: number | null;
|
|
45
|
+
locale?: string;
|
|
46
|
+
includeAchievementComplete?: boolean;
|
|
47
|
+
}): Promise<UnlockRequirementLeaf[]>;
|
|
48
|
+
//# sourceMappingURL=buildUnlockRequirementLeaves.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildUnlockRequirementLeaves.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/buildUnlockRequirementLeaves.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAOtD,eAAO,MAAM,kCAAkC;;;;;CAKyC,CAAA;AAExF,MAAM,MAAM,8BAA8B,GACxC,CAAC,OAAO,kCAAkC,CAAC,CAAC,MAAM,OAAO,kCAAkC,CAAC,CAAA;AAE9F,MAAM,MAAM,yBAAyB,GAAG;IACtC,KAAK,EAAE,MAAM,OAAO,kCAAkC,CAAA;IACtD,aAAa,EAAE,8BAA8B,CAAA;IAC7C,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,OAAO,CAAA;IACZ,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,yBAAyB,EAAE,CAAA;IACtC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAqHD;;;;;;GAMG;AACH,wBAAsB,4BAA4B,CAAC,IAAI,EAAE;IACvD,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,cAAc,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,KAAK,EAAE,OAAO,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0BAA0B,CAAC,EAAE,OAAO,CAAA;CACrC,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC,CA8EnC"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { eachRuleLeaf } from '../../collections/hooks/eachRuleLeaf.js';
|
|
2
|
+
import { collectionOf } from '../../collections/helpers.js';
|
|
3
|
+
import { evaluateRuleProgress, evaluateRules } from './evaluateRules.js';
|
|
4
|
+
import { relationId } from './relationId.js';
|
|
5
|
+
export const UNLOCK_REQUIREMENT_RELATION_FIELDS = {
|
|
6
|
+
tier: 'tiers',
|
|
7
|
+
achievement: 'achievements',
|
|
8
|
+
metric: 'metrics',
|
|
9
|
+
eventType: 'eventTypes',
|
|
10
|
+
};
|
|
11
|
+
function relationName(value) {
|
|
12
|
+
if (value && typeof value === 'object' && 'name' in value) {
|
|
13
|
+
const name = value.name;
|
|
14
|
+
if (typeof name === 'string' && name)
|
|
15
|
+
return name;
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
function relationSlug(value) {
|
|
20
|
+
if (value && typeof value === 'object' && 'slug' in value) {
|
|
21
|
+
const slug = value.slug;
|
|
22
|
+
if (typeof slug === 'string' && slug)
|
|
23
|
+
return slug;
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
function slugField(rule, relationKey) {
|
|
28
|
+
const raw = rule[`${relationKey}Slug`];
|
|
29
|
+
return typeof raw === 'string' && raw ? raw : null;
|
|
30
|
+
}
|
|
31
|
+
function numericTarget(rule) {
|
|
32
|
+
for (const key of ['amount', 'minimum', 'count']) {
|
|
33
|
+
const value = rule[key];
|
|
34
|
+
if (typeof value === 'number' && Number.isFinite(value) && value >= 0)
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
async function resolveCatalogName(args) {
|
|
40
|
+
const named = relationName(args.value);
|
|
41
|
+
const slug = args.slug || relationSlug(args.value);
|
|
42
|
+
const id = relationId(args.value);
|
|
43
|
+
if (named) {
|
|
44
|
+
return {
|
|
45
|
+
...(id ? { id } : {}),
|
|
46
|
+
...(slug ? { slug } : {}),
|
|
47
|
+
name: named,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (slug) {
|
|
51
|
+
const cached = args.cache.get(`slug:${args.collectionKey}:${slug}`);
|
|
52
|
+
if (cached)
|
|
53
|
+
return { ...(id ? { id } : {}), slug, name: cached };
|
|
54
|
+
}
|
|
55
|
+
if (id) {
|
|
56
|
+
const cached = args.cache.get(`id:${args.collectionKey}:${id}`);
|
|
57
|
+
if (cached) {
|
|
58
|
+
return { id, ...(slug ? { slug } : {}), name: cached };
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const doc = (await args.payload.findByID({
|
|
62
|
+
collection: collectionOf(args.collectionKey),
|
|
63
|
+
id,
|
|
64
|
+
depth: 0,
|
|
65
|
+
overrideAccess: true,
|
|
66
|
+
req: args.req,
|
|
67
|
+
...(args.locale ? { locale: args.locale } : {}),
|
|
68
|
+
}));
|
|
69
|
+
const name = (typeof doc?.name === 'string' && doc.name) ||
|
|
70
|
+
(typeof doc?.slug === 'string' && doc.slug) ||
|
|
71
|
+
null;
|
|
72
|
+
if (name) {
|
|
73
|
+
args.cache.set(`id:${args.collectionKey}:${id}`, name);
|
|
74
|
+
if (doc?.slug)
|
|
75
|
+
args.cache.set(`slug:${args.collectionKey}:${doc.slug}`, name);
|
|
76
|
+
return { id, ...(doc?.slug ? { slug: doc.slug } : slug ? { slug } : {}), name };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
/* fall through */
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (slug && !id) {
|
|
84
|
+
try {
|
|
85
|
+
const found = await args.payload.find({
|
|
86
|
+
collection: collectionOf(args.collectionKey),
|
|
87
|
+
depth: 0,
|
|
88
|
+
limit: 1,
|
|
89
|
+
overrideAccess: true,
|
|
90
|
+
req: args.req,
|
|
91
|
+
where: { slug: { equals: slug } },
|
|
92
|
+
...(args.locale ? { locale: args.locale } : {}),
|
|
93
|
+
});
|
|
94
|
+
const doc = found.docs[0];
|
|
95
|
+
const resolvedId = relationId(doc);
|
|
96
|
+
const name = (typeof doc?.name === 'string' && doc.name) ||
|
|
97
|
+
(typeof doc?.slug === 'string' && doc.slug) ||
|
|
98
|
+
slug;
|
|
99
|
+
if (resolvedId)
|
|
100
|
+
args.cache.set(`id:${args.collectionKey}:${resolvedId}`, name);
|
|
101
|
+
args.cache.set(`slug:${args.collectionKey}:${slug}`, name);
|
|
102
|
+
return { ...(resolvedId ? { id: resolvedId } : {}), slug, name };
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
/* fall through */
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
...(id ? { id } : {}),
|
|
110
|
+
...(slug ? { slug } : {}),
|
|
111
|
+
...(slug || id ? { name: slug || id || undefined } : {}),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Walk every leaf in a rule tree, evaluate met/progress, and return catalog-backed
|
|
116
|
+
* subjects. Payload rows often carry empty `rules: []` + default combinator — those
|
|
117
|
+
* are still leaves ({@link eachRuleLeaf}).
|
|
118
|
+
*
|
|
119
|
+
* `achievement-complete` is omitted by default so hosts can keep those on catalog groups.
|
|
120
|
+
*/
|
|
121
|
+
export async function buildUnlockRequirementLeaves(args) {
|
|
122
|
+
const req = args.req ?? { payload: args.payload };
|
|
123
|
+
const cache = new Map();
|
|
124
|
+
const leaves = [];
|
|
125
|
+
eachRuleLeaf(args.rules, (rule) => {
|
|
126
|
+
const type = String(rule.type ?? '');
|
|
127
|
+
if (!type)
|
|
128
|
+
return;
|
|
129
|
+
if (type === 'achievement-complete' && !args.includeAchievementComplete)
|
|
130
|
+
return;
|
|
131
|
+
leaves.push(rule);
|
|
132
|
+
});
|
|
133
|
+
const out = [];
|
|
134
|
+
for (let index = 0; index < leaves.length; index += 1) {
|
|
135
|
+
const rule = leaves[index];
|
|
136
|
+
const type = String(rule.type);
|
|
137
|
+
const leafRules = [rule];
|
|
138
|
+
const met = await evaluateRules({
|
|
139
|
+
payload: args.payload,
|
|
140
|
+
req,
|
|
141
|
+
rules: leafRules,
|
|
142
|
+
userId: args.userId,
|
|
143
|
+
scopeId: args.scopeId,
|
|
144
|
+
ladderRank: args.ladderRank,
|
|
145
|
+
});
|
|
146
|
+
const progress = await evaluateRuleProgress({
|
|
147
|
+
payload: args.payload,
|
|
148
|
+
req,
|
|
149
|
+
rules: leafRules,
|
|
150
|
+
userId: args.userId,
|
|
151
|
+
scopeId: args.scopeId,
|
|
152
|
+
ladderRank: args.ladderRank,
|
|
153
|
+
});
|
|
154
|
+
const relations = [];
|
|
155
|
+
for (const [field, collectionKey] of Object.entries(UNLOCK_REQUIREMENT_RELATION_FIELDS)) {
|
|
156
|
+
const key = field;
|
|
157
|
+
if (rule[key] == null && !slugField(rule, key))
|
|
158
|
+
continue;
|
|
159
|
+
const resolved = await resolveCatalogName({
|
|
160
|
+
payload: args.payload,
|
|
161
|
+
req,
|
|
162
|
+
locale: args.locale,
|
|
163
|
+
collectionKey,
|
|
164
|
+
value: rule[key],
|
|
165
|
+
slug: slugField(rule, key),
|
|
166
|
+
cache,
|
|
167
|
+
});
|
|
168
|
+
if (!resolved.id && !resolved.slug && !resolved.name)
|
|
169
|
+
continue;
|
|
170
|
+
relations.push({ field: key, collectionKey, ...resolved });
|
|
171
|
+
}
|
|
172
|
+
const target = numericTarget(rule);
|
|
173
|
+
const unit = typeof rule.unit === 'string' && rule.unit ? rule.unit : undefined;
|
|
174
|
+
// `since` / `unit` only matter when a duration/count target is set (defaults pollute other leaves).
|
|
175
|
+
const since = target != null && typeof rule.since === 'string' && rule.since ? rule.since : undefined;
|
|
176
|
+
const subjectKey = relations
|
|
177
|
+
.map((row) => row.name || row.slug || row.id)
|
|
178
|
+
.filter(Boolean)
|
|
179
|
+
.join(':') || 'leaf';
|
|
180
|
+
out.push({
|
|
181
|
+
id: `${type}:${index}:${subjectKey}`,
|
|
182
|
+
type,
|
|
183
|
+
met,
|
|
184
|
+
progress,
|
|
185
|
+
relations,
|
|
186
|
+
...(target != null ? { target } : {}),
|
|
187
|
+
...(unit ? { unit } : {}),
|
|
188
|
+
...(since ? { since } : {}),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
@@ -2,7 +2,7 @@ const MS_PER_SECOND = 1_000;
|
|
|
2
2
|
const MS_PER_MINUTE = 60 * MS_PER_SECOND;
|
|
3
3
|
const MS_PER_HOUR = 60 * MS_PER_MINUTE;
|
|
4
4
|
const MS_PER_DAY = 24 * MS_PER_HOUR;
|
|
5
|
-
/** Fixed-length year for elapsed floors (
|
|
5
|
+
/** Fixed-length year for elapsed floors (365-day year). */
|
|
6
6
|
const MS_PER_YEAR = 365 * MS_PER_DAY;
|
|
7
7
|
export function durationMs(amount, unit) {
|
|
8
8
|
if (!(typeof amount === 'number') || !Number.isFinite(amount) || amount < 0)
|
|
@@ -2,6 +2,9 @@ import type { Payload, PayloadRequest } from 'payload';
|
|
|
2
2
|
export type EventTypeFlags = {
|
|
3
3
|
requiresActor: boolean;
|
|
4
4
|
requiresMetric: boolean;
|
|
5
|
+
requiresSubject: boolean;
|
|
6
|
+
/** Allowed host collections for `subject.relationTo` (empty = any plugin allowlist entry). */
|
|
7
|
+
subjectRelationTo: string[];
|
|
5
8
|
};
|
|
6
9
|
/** Read flags from a loaded event-type document (or populated relationship). */
|
|
7
10
|
export declare function readEventTypeFlags(doc: unknown): EventTypeFlags | null;
|
|
@@ -21,4 +24,18 @@ export declare function eventTypeRequiresMetric(args: {
|
|
|
21
24
|
req?: PayloadRequest;
|
|
22
25
|
typeIdOrDoc: unknown;
|
|
23
26
|
}): Promise<boolean>;
|
|
27
|
+
export declare function eventTypeRequiresSubject(args: {
|
|
28
|
+
payload: Payload;
|
|
29
|
+
req?: PayloadRequest;
|
|
30
|
+
typeIdOrDoc: unknown;
|
|
31
|
+
}): Promise<boolean>;
|
|
32
|
+
/**
|
|
33
|
+
* Allowed `subject.relationTo` values for this event type.
|
|
34
|
+
* Empty event-type list → full plugin allowlist. Feature off → [].
|
|
35
|
+
*/
|
|
36
|
+
export declare function eventTypeSubjectRelationTo(args: {
|
|
37
|
+
payload: Payload;
|
|
38
|
+
req?: PayloadRequest;
|
|
39
|
+
typeIdOrDoc: unknown;
|
|
40
|
+
}): Promise<string[]>;
|
|
24
41
|
//# sourceMappingURL=eventTypeRequiresActor.d.ts.map
|
|
@@ -1 +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;
|
|
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;AAMtD,MAAM,MAAM,cAAc,GAAG;IAC3B,aAAa,EAAE,OAAO,CAAA;IACtB,cAAc,EAAE,OAAO,CAAA;IACvB,eAAe,EAAE,OAAO,CAAA;IACxB,8FAA8F;IAC9F,iBAAiB,EAAE,MAAM,EAAE,CAAA;CAC5B,CAAA;AAkBD,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CAsBtE;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,CAwC1B;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;AAED,wBAAsB,wBAAwB,CAAC,IAAI,EAAE;IACnD,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,cAAc,CAAA;IACpB,WAAW,EAAE,OAAO,CAAA;CACrB,GAAG,OAAO,CAAC,OAAO,CAAC,CAEnB;AAED;;;GAGG;AACH,wBAAsB,0BAA0B,CAAC,IAAI,EAAE;IACrD,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,cAAc,CAAA;IACpB,WAAW,EAAE,OAAO,CAAA;CACrB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAOpB"}
|
|
@@ -1,16 +1,39 @@
|
|
|
1
|
+
import { getAchievementOptions } from '../../options-store.js';
|
|
1
2
|
import { collectionOf } from '../../collections/helpers.js';
|
|
2
3
|
import { relationId } from './relationId.js';
|
|
4
|
+
const emptyFlags = {
|
|
5
|
+
requiresActor: false,
|
|
6
|
+
requiresMetric: false,
|
|
7
|
+
requiresSubject: false,
|
|
8
|
+
subjectRelationTo: [],
|
|
9
|
+
};
|
|
10
|
+
function readSubjectRelationTo(value) {
|
|
11
|
+
if (!Array.isArray(value))
|
|
12
|
+
return [];
|
|
13
|
+
const out = [];
|
|
14
|
+
for (const entry of value) {
|
|
15
|
+
if (typeof entry === 'string' && entry.trim())
|
|
16
|
+
out.push(entry.trim());
|
|
17
|
+
}
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
3
20
|
/** Read flags from a loaded event-type document (or populated relationship). */
|
|
4
21
|
export function readEventTypeFlags(doc) {
|
|
5
22
|
if (!doc || typeof doc !== 'object')
|
|
6
23
|
return null;
|
|
7
24
|
const row = doc;
|
|
8
|
-
const hasFlagKeys = 'requiresActor' in row ||
|
|
25
|
+
const hasFlagKeys = 'requiresActor' in row ||
|
|
26
|
+
'requiresMetric' in row ||
|
|
27
|
+
'requiresSubject' in row ||
|
|
28
|
+
'subjectRelationTo' in row ||
|
|
29
|
+
typeof row.slug === 'string';
|
|
9
30
|
if (!hasFlagKeys)
|
|
10
31
|
return null;
|
|
11
32
|
return {
|
|
12
33
|
requiresActor: Boolean(row.requiresActor),
|
|
13
34
|
requiresMetric: Boolean(row.requiresMetric) || row.slug === 'metric.delta',
|
|
35
|
+
requiresSubject: Boolean(row.requiresSubject),
|
|
36
|
+
subjectRelationTo: readSubjectRelationTo(row.subjectRelationTo),
|
|
14
37
|
};
|
|
15
38
|
}
|
|
16
39
|
function flagsFromDoc(doc) {
|
|
@@ -21,22 +44,32 @@ export async function getEventTypeFlags(args) {
|
|
|
21
44
|
const embedded = flagsFromDoc(args.typeIdOrDoc);
|
|
22
45
|
if (embedded && args.typeIdOrDoc && typeof args.typeIdOrDoc === 'object') {
|
|
23
46
|
const row = args.typeIdOrDoc;
|
|
24
|
-
if ('requiresActor' in row ||
|
|
47
|
+
if ('requiresActor' in row ||
|
|
48
|
+
'requiresMetric' in row ||
|
|
49
|
+
'requiresSubject' in row ||
|
|
50
|
+
'subjectRelationTo' in row ||
|
|
51
|
+
'slug' in row) {
|
|
25
52
|
return embedded;
|
|
26
53
|
}
|
|
27
54
|
}
|
|
28
55
|
const typeId = relationId(args.typeIdOrDoc);
|
|
29
56
|
if (!typeId)
|
|
30
|
-
return {
|
|
57
|
+
return { ...emptyFlags };
|
|
31
58
|
const found = (await args.payload.findByID({
|
|
32
59
|
collection: collectionOf('eventTypes'),
|
|
33
60
|
id: typeId,
|
|
34
61
|
depth: 0,
|
|
35
62
|
overrideAccess: true,
|
|
36
63
|
req: args.req,
|
|
37
|
-
select: {
|
|
64
|
+
select: {
|
|
65
|
+
requiresActor: true,
|
|
66
|
+
requiresMetric: true,
|
|
67
|
+
requiresSubject: true,
|
|
68
|
+
subjectRelationTo: true,
|
|
69
|
+
slug: true,
|
|
70
|
+
},
|
|
38
71
|
}));
|
|
39
|
-
return flagsFromDoc(found) ?? {
|
|
72
|
+
return flagsFromDoc(found) ?? { ...emptyFlags };
|
|
40
73
|
}
|
|
41
74
|
export async function eventTypeRequiresActor(args) {
|
|
42
75
|
return (await getEventTypeFlags(args)).requiresActor;
|
|
@@ -44,3 +77,21 @@ export async function eventTypeRequiresActor(args) {
|
|
|
44
77
|
export async function eventTypeRequiresMetric(args) {
|
|
45
78
|
return (await getEventTypeFlags(args)).requiresMetric;
|
|
46
79
|
}
|
|
80
|
+
export async function eventTypeRequiresSubject(args) {
|
|
81
|
+
return (await getEventTypeFlags(args)).requiresSubject;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Allowed `subject.relationTo` values for this event type.
|
|
85
|
+
* Empty event-type list → full plugin allowlist. Feature off → [].
|
|
86
|
+
*/
|
|
87
|
+
export async function eventTypeSubjectRelationTo(args) {
|
|
88
|
+
const allowlist = getAchievementOptions().subjectCollections;
|
|
89
|
+
if (allowlist.length === 0)
|
|
90
|
+
return [];
|
|
91
|
+
const flags = await getEventTypeFlags(args);
|
|
92
|
+
if (!flags.requiresSubject)
|
|
93
|
+
return [];
|
|
94
|
+
if (flags.subjectRelationTo.length === 0)
|
|
95
|
+
return [...allowlist];
|
|
96
|
+
return flags.subjectRelationTo.filter((slug) => allowlist.includes(slug));
|
|
97
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PaginatedDocs, Payload, PayloadRequest } from 'payload';
|
|
2
|
+
import { type UserProgressReviews } from './loadUserProgressReviews.js';
|
|
2
3
|
/** Catalog slice — `description` matches the collection (Lexical JSON, or legacy string). */
|
|
3
4
|
type LeanCatalog = {
|
|
4
5
|
id: string;
|
|
@@ -32,10 +33,15 @@ declare function leanRequest(doc: Record<string, unknown>): {
|
|
|
32
33
|
updatedAt: {} | null;
|
|
33
34
|
createdAt: {} | null;
|
|
34
35
|
};
|
|
36
|
+
export type GetUserProgressInclude = {
|
|
37
|
+
/** Pending achievement keys + open/rejected tier requests. */
|
|
38
|
+
reviews?: boolean;
|
|
39
|
+
};
|
|
35
40
|
export type UserProgressMeResponse = PaginatedDocs<ReturnType<typeof leanGrant>> & {
|
|
36
41
|
/** Ladder-derived current tier(s). */
|
|
37
42
|
tiers: LeanTier[];
|
|
38
43
|
requests: ReturnType<typeof leanRequest>[];
|
|
44
|
+
reviews?: UserProgressReviews;
|
|
39
45
|
};
|
|
40
46
|
/**
|
|
41
47
|
* Current-user snapshot. Top-level Payload `find` shape for **grants**;
|
|
@@ -49,6 +55,7 @@ export declare function getUserProgress(args: {
|
|
|
49
55
|
paging?: GetUserProgressPaging;
|
|
50
56
|
/** Content locale for localized achievement / tier names (falls back to `req.locale`). */
|
|
51
57
|
locale?: string;
|
|
58
|
+
include?: GetUserProgressInclude;
|
|
52
59
|
}): Promise<UserProgressMeResponse>;
|
|
53
60
|
export {};
|
|
54
61
|
//# sourceMappingURL=getUserProgress.d.ts.map
|
|
@@ -1 +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;
|
|
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;AAGrE,OAAO,EAA2B,KAAK,mBAAmB,EAAE,MAAM,8BAA8B,CAAA;AAKhG,6FAA6F;AAC7F,KAAK,WAAW,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AACtF,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;AAmBD,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;IACnC,8DAA8D;IAC9D,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AAED,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;IAC1C,OAAO,CAAC,EAAE,mBAAmB,CAAA;CAC9B,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;IACf,OAAO,CAAC,EAAE,sBAAsB,CAAA;CACjC,GAAG,OAAO,CAAC,sBAAsB,CAAC,CA0DlC"}
|