@dappermountain/payload-plugin-achievements 0.2.1 → 0.2.2

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 CHANGED
@@ -279,6 +279,18 @@ await runReconcileCli({ config })
279
279
 
280
280
  Pass catalog **slugs** (or ids) for `type` and `metric`. Metric totals are the sum of `change` on matching logs. Creating or deleting **grants** (Admin or API) writes `achievement.granted` / `achievement.revoked`, syncs composed achievements, and may open tier requests / write `tier.changed`.
281
281
 
282
+ Engine log `data` for those system events uses a shared transition envelope:
283
+
284
+ ```ts
285
+ { from: { id, slug? } | null, to: { id, slug? } | null }
286
+ ```
287
+
288
+ | type | `from` | `to` |
289
+ | --- | --- | --- |
290
+ | `tier.changed` | previous tier (null on first climb) | current tier |
291
+ | `achievement.granted` | `null` | achievement |
292
+ | `achievement.revoked` | achievement | `null` |
293
+
282
294
  Turning **Requires review** off on a definition or tier **approves pending requests** for that row only (each waiting user goes through the existing request hooks).
283
295
 
284
296
  **Admin:** Grants list includes **Repair progression**, which `POST`s the reconcile endpoint (requires `canReview`).
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/collections/Logs/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAS,MAAM,SAAS,CAAA;AAUtD,+EAA+E;AAC/E,wBAAgB,mBAAmB,IAAI,gBAAgB,CA0GtD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/collections/Logs/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAS,MAAM,SAAS,CAAA;AAUtD,+EAA+E;AAC/E,wBAAgB,mBAAmB,IAAI,gBAAgB,CA6GtD"}
@@ -104,7 +104,9 @@ export function buildLogsCollection() {
104
104
  {
105
105
  name: 'data',
106
106
  type: 'json',
107
- admin: { description: 'Any extra structured details.' },
107
+ admin: {
108
+ description: 'Engine transition envelope: `{ from, to }` each `{ id, slug? }` or null (tier.changed, achievement.granted/revoked). Hosts may add extra keys.',
109
+ },
108
110
  },
109
111
  ],
110
112
  },
package/dist/index.d.ts CHANGED
@@ -3,8 +3,8 @@ export type { EngineEventTypeSlug, EngineMetricSlug } from './catalog';
3
3
  export { achievementPlugin } from './plugin';
4
4
  export { seedAchievementCatalog, seedAchievements } from './seed/index';
5
5
  export type { AchievementSeedAchievement, AchievementSeedCatalogRow, AchievementSeedInput, AchievementSeedLocalizedText, AchievementSeedTier, } from './seed/index';
6
- export { evaluateRules, evaluateRuleProgress, collectAchievementCompleteRefs, buildCatalogProgressGroups, getUserProgress, grantAchievement, recordEvent, recordLog, recordMetricChange, reconcileProgression, reconcileUserProgression, releasePendingAchievementReviews, releasePendingTierReviews, resolveCurrentTier, resolveTierProgress, reviewAchievementRequest, submitAchievementRequest, syncTierChangedLog, syncTierProgression, syncCompositeProgression, validateRuleGroup, validateRuleNode, } from './services/achievement/index';
7
- export type { ResolvedTier, RuleGroup, TierLadderProgress, CatalogProgressGroup, CatalogProgressItem, ReconcileProgressionArgs, ReconcileProgressionResult, ReleaseReviewGateResult, } from './services/achievement/index';
6
+ export { evaluateRules, evaluateRuleProgress, collectAchievementCompleteRefs, buildCatalogProgressGroups, getUserProgress, grantAchievement, recordEvent, recordLog, recordMetricChange, reconcileProgression, reconcileUserProgression, releasePendingAchievementReviews, releasePendingTierReviews, resolveCurrentTier, resolveTierProgress, reviewAchievementRequest, submitAchievementRequest, syncTierChangedLog, syncTierProgression, syncCompositeProgression, catalogRef, transitionLogData, transitionToId, validateRuleGroup, validateRuleNode, } from './services/achievement/index';
7
+ export type { ResolvedTier, RuleGroup, TierLadderProgress, CatalogProgressGroup, CatalogProgressItem, CatalogRef, TransitionLogData, ReconcileProgressionArgs, ReconcileProgressionResult, ReleaseReviewGateResult, } from './services/achievement/index';
8
8
  export type { BuiltInEventType, BuiltInRuleType, AchievementCollectionKey, AchievementCollectionOverride, AchievementCollectionsOptions, AchievementEndpointsOptions, AchievementPluginOptions, AchievementRuleType, AchievementScopeConfig, AchievementUsersOptions, } from './types';
9
9
  export { DEFAULT_COLLECTION_BASE_SLUGS, DEFAULT_COLLECTION_PREFIX, DEFAULT_ADMIN_GROUP } from './collections/slugs';
10
10
  export { DEFAULT_ME_ENDPOINT_PATH, DEFAULT_RECONCILE_ENDPOINT_PATH } from './defaults';
@@ -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,WAAW,CAAA;AAClB,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAC5C,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AACvE,YAAY,EACV,0BAA0B,EAC1B,yBAAyB,EACzB,oBAAoB,EACpB,4BAA4B,EAC5B,mBAAmB,GACpB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,8BAA8B,EAC9B,0BAA0B,EAC1B,eAAe,EACf,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACxB,gCAAgC,EAChC,yBAAyB,EACzB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,EACxB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,8BAA8B,CAAA;AACrC,YAAY,EACV,YAAY,EACZ,SAAS,EACT,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,8BAA8B,CAAA;AACrC,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,wBAAwB,EACxB,6BAA6B,EAC7B,6BAA6B,EAC7B,2BAA2B,EAC3B,wBAAwB,EACxB,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,6BAA6B,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AACnH,OAAO,EAAE,wBAAwB,EAAE,+BAA+B,EAAE,MAAM,YAAY,CAAA;AACtF,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAC5D,OAAO,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAA"}
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,WAAW,CAAA;AAClB,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAC5C,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AACvE,YAAY,EACV,0BAA0B,EAC1B,yBAAyB,EACzB,oBAAoB,EACpB,4BAA4B,EAC5B,mBAAmB,GACpB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,8BAA8B,EAC9B,0BAA0B,EAC1B,eAAe,EACf,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACxB,gCAAgC,EAChC,yBAAyB,EACzB,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,8BAA8B,CAAA;AACrC,YAAY,EACV,YAAY,EACZ,SAAS,EACT,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,UAAU,EACV,iBAAiB,EACjB,wBAAwB,EACxB,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,8BAA8B,CAAA;AACrC,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,wBAAwB,EACxB,6BAA6B,EAC7B,6BAA6B,EAC7B,2BAA2B,EAC3B,wBAAwB,EACxB,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,6BAA6B,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AACnH,OAAO,EAAE,wBAAwB,EAAE,+BAA+B,EAAE,MAAM,YAAY,CAAA;AACtF,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAC5D,OAAO,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAA"}
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  export { DEFAULT_ACHIEVEMENT_METRIC, DEFAULT_ACHIEVEMENT_METRIC_SLUG, ENGINE_EVENT_TYPES, ENGINE_METRICS, } from './catalog';
2
2
  export { achievementPlugin } from './plugin';
3
3
  export { seedAchievementCatalog, seedAchievements } from './seed/index';
4
- export { evaluateRules, evaluateRuleProgress, collectAchievementCompleteRefs, buildCatalogProgressGroups, getUserProgress, grantAchievement, recordEvent, recordLog, recordMetricChange, reconcileProgression, reconcileUserProgression, releasePendingAchievementReviews, releasePendingTierReviews, resolveCurrentTier, resolveTierProgress, reviewAchievementRequest, submitAchievementRequest, syncTierChangedLog, syncTierProgression, syncCompositeProgression, validateRuleGroup, validateRuleNode, } from './services/achievement/index';
4
+ export { evaluateRules, evaluateRuleProgress, collectAchievementCompleteRefs, buildCatalogProgressGroups, getUserProgress, grantAchievement, recordEvent, recordLog, recordMetricChange, reconcileProgression, reconcileUserProgression, releasePendingAchievementReviews, releasePendingTierReviews, resolveCurrentTier, resolveTierProgress, reviewAchievementRequest, submitAchievementRequest, syncTierChangedLog, syncTierProgression, syncCompositeProgression, catalogRef, transitionLogData, transitionToId, validateRuleGroup, validateRuleNode, } from './services/achievement/index';
5
5
  export { DEFAULT_COLLECTION_BASE_SLUGS, DEFAULT_COLLECTION_PREFIX, DEFAULT_ADMIN_GROUP } from './collections/slugs';
6
6
  export { DEFAULT_ME_ENDPOINT_PATH, DEFAULT_RECONCILE_ENDPOINT_PATH } from './defaults';
7
7
  export { collectionOf, slugOf } from './collections/helpers';
@@ -1 +1 @@
1
- {"version":3,"file":"grantSideEffects.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/grantSideEffects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAS,MAAM,SAAS,CAAA;AAUpD,OAAO,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAStG,wEAAwE;AACxE,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,GAAG,EAAE,cAAc,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA+BnC;AAED,6EAA6E;AAC7E,wBAAsB,wBAAwB,CAAC,IAAI,EAAE;IACnD,GAAG,EAAE,cAAc,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB,GAAG,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAuC9D;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAAC,IAAI,EAAE;IACnD,GAAG,EAAE,cAAc,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAsElD;AAED,oFAAoF;AACpF,wBAAsB,0BAA0B,CAAC,IAAI,EAAE;IACrD,GAAG,EAAE,cAAc,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiDlB;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,GAAG,EAAE,cAAc,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+BhB;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,GAAG,EAAE,cAAc,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuChB"}
1
+ {"version":3,"file":"grantSideEffects.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/grantSideEffects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAS,MAAM,SAAS,CAAA;AAWpD,OAAO,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAStG,wEAAwE;AACxE,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,GAAG,EAAE,cAAc,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA+BnC;AAED,6EAA6E;AAC7E,wBAAsB,wBAAwB,CAAC,IAAI,EAAE;IACnD,GAAG,EAAE,cAAc,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB,GAAG,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAuC9D;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAAC,IAAI,EAAE;IACnD,GAAG,EAAE,cAAc,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAsElD;AAED,oFAAoF;AACpF,wBAAsB,0BAA0B,CAAC,IAAI,EAAE;IACrD,GAAG,EAAE,cAAc,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiDlB;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,GAAG,EAAE,cAAc,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+BhB;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,GAAG,EAAE,cAAc,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuChB"}
@@ -1,6 +1,7 @@
1
1
  import { collectionOf } from '../../collections/helpers';
2
2
  import { CTX_CASCADING_REVOKE, CTX_SYSTEM_REQUEST } from './contextFlags';
3
3
  import { evaluateRules, ruleGroupHasProgressRequirements } from './evaluateRules';
4
+ import { transitionLogData } from './logData';
4
5
  import { recordLog, syncTierChangedLog } from './recordEvent';
5
6
  import { relationId } from './relationId';
6
7
  import { syncTierProgression } from './tierProgression';
@@ -209,10 +210,10 @@ export async function runAfterGrantCreated(args) {
209
210
  userId: args.userId,
210
211
  scopeId: args.scopeId,
211
212
  type: 'achievement.granted',
212
- data: {
213
- achievement: args.achievementId,
214
- ...(args.achievementSlug ? { achievementSlug: args.achievementSlug } : {}),
215
- },
213
+ data: transitionLogData({
214
+ from: null,
215
+ to: { id: args.achievementId, slug: args.achievementSlug },
216
+ }),
216
217
  });
217
218
  await syncCompositeProgression({
218
219
  req: args.req,
@@ -238,10 +239,10 @@ export async function runAfterGrantDeleted(args) {
238
239
  userId: args.userId,
239
240
  scopeId: args.scopeId,
240
241
  type: 'achievement.revoked',
241
- data: {
242
- achievement: args.achievementId,
243
- ...(args.achievementSlug ? { achievementSlug: args.achievementSlug } : {}),
244
- },
242
+ data: transitionLogData({
243
+ from: { id: args.achievementId, slug: args.achievementSlug },
244
+ to: null,
245
+ }),
245
246
  });
246
247
  if (args.cascading)
247
248
  return;
@@ -17,4 +17,6 @@ export { reconcileProgression, reconcileUserProgression } from './reconcile';
17
17
  export type { ReconcileProgressionArgs, ReconcileProgressionResult } from './reconcile';
18
18
  export { didDisableRequiresReview, releasePendingAchievementReviews, releasePendingTierReviews, } from './releaseReviewGate';
19
19
  export type { ReleaseReviewGateResult } from './releaseReviewGate';
20
+ export { catalogRef, transitionLogData, transitionToId, } from './logData';
21
+ export type { CatalogRef, TransitionLogData } from './logData';
20
22
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,8BAA8B,EAAE,MAAM,iBAAiB,CAAA;AACzH,YAAY,EAAE,SAAS,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AACxE,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAA;AACzE,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAA;AAC7F,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAC9F,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,YAAY,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC3D,YAAY,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAC/D,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAA;AACvF,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAA;AACrE,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAA;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AAC5E,YAAY,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAA;AACvF,OAAO,EACL,wBAAwB,EACxB,gCAAgC,EAChC,yBAAyB,GAC1B,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,8BAA8B,EAAE,MAAM,iBAAiB,CAAA;AACzH,YAAY,EAAE,SAAS,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AACxE,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAA;AACzE,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAA;AAC7F,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAC9F,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,YAAY,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC3D,YAAY,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAC/D,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAA;AACvF,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAA;AACrE,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAA;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AAC5E,YAAY,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAA;AACvF,OAAO,EACL,wBAAwB,EACxB,gCAAgC,EAChC,yBAAyB,GAC1B,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,cAAc,GACf,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA"}
@@ -11,3 +11,4 @@ export { syncCompositeProgression } from './grantSideEffects';
11
11
  export { syncTierProgression } from './tierProgression';
12
12
  export { reconcileProgression, reconcileUserProgression } from './reconcile';
13
13
  export { didDisableRequiresReview, releasePendingAchievementReviews, releasePendingTierReviews, } from './releaseReviewGate';
14
+ export { catalogRef, transitionLogData, transitionToId, } from './logData';
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Shared engine shape for achievement-log `data` on system event types.
3
+ * Hosts may add extra keys; readers should treat unknown keys as opaque.
4
+ *
5
+ * Transition events always use `from` / `to` (either side may be null):
6
+ * - `tier.changed` — previous ladder step → current step
7
+ * - `achievement.granted` — `from: null`, `to: achievement`
8
+ * - `achievement.revoked` — `from: achievement`, `to: null`
9
+ */
10
+ export type CatalogRef = {
11
+ id: string;
12
+ slug?: string;
13
+ };
14
+ export type TransitionLogData = {
15
+ from: CatalogRef | null;
16
+ to: CatalogRef | null;
17
+ };
18
+ export declare function catalogRef(id: string, slug?: string | null): CatalogRef;
19
+ export declare function transitionLogData(args: {
20
+ from?: {
21
+ id: string;
22
+ slug?: string | null;
23
+ } | null;
24
+ to?: {
25
+ id: string;
26
+ slug?: string | null;
27
+ } | null;
28
+ }): TransitionLogData;
29
+ /** Read the “current” catalog id from a log `data` blob (new or legacy shapes). */
30
+ export declare function transitionToId(data: unknown): string | null;
31
+ //# sourceMappingURL=logData.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logData.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/logData.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,UAAU,GAAG,IAAI,CAAA;IACvB,EAAE,EAAE,UAAU,GAAG,IAAI,CAAA;CACtB,CAAA;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,UAAU,CAKvE;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,IAAI,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAA;IAClD,EAAE,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAA;CACjD,GAAG,iBAAiB,CAKpB;AAED,mFAAmF;AACnF,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAY3D"}
@@ -0,0 +1,30 @@
1
+ export function catalogRef(id, slug) {
2
+ return {
3
+ id,
4
+ ...(typeof slug === 'string' && slug.length > 0 ? { slug } : {}),
5
+ };
6
+ }
7
+ export function transitionLogData(args) {
8
+ return {
9
+ from: args.from ? catalogRef(args.from.id, args.from.slug) : null,
10
+ to: args.to ? catalogRef(args.to.id, args.to.slug) : null,
11
+ };
12
+ }
13
+ /** Read the “current” catalog id from a log `data` blob (new or legacy shapes). */
14
+ export function transitionToId(data) {
15
+ if (!data || typeof data !== 'object' || Array.isArray(data))
16
+ return null;
17
+ const record = data;
18
+ const to = record.to;
19
+ if (to && typeof to === 'object' && !Array.isArray(to)) {
20
+ const id = to.id;
21
+ if (typeof id === 'string' && id)
22
+ return id;
23
+ }
24
+ // Legacy tier.changed / achievement.* shapes
25
+ if (typeof record.tier === 'string' && record.tier)
26
+ return record.tier;
27
+ if (typeof record.achievement === 'string' && record.achievement)
28
+ return record.achievement;
29
+ return null;
30
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/reconcile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAS,MAAM,SAAS,CAAA;AAY7D,MAAM,MAAM,0BAA0B,GAAG;IACvC,YAAY,EAAE,MAAM,CAAA;IACpB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,iBAAiB,EAAE,MAAM,CAAA;IACzB,0BAA0B,EAAE,MAAM,CAAA;IAClC,mBAAmB,EAAE,MAAM,CAAA;IAC3B,eAAe,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,cAAc,CAAA;IACpB,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAsFD,wFAAwF;AACxF,wBAAsB,wBAAwB,CAAC,IAAI,EAAE;IACnD,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,cAAc,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB,GAAG,OAAO,CAAC,IAAI,CAAC,0BAA0B,EAAE,cAAc,CAAC,CAAC,CAoC5D;AA2LD;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,0BAA0B,CAAC,CA8CrC"}
1
+ {"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/reconcile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAS,MAAM,SAAS,CAAA;AAa7D,MAAM,MAAM,0BAA0B,GAAG;IACvC,YAAY,EAAE,MAAM,CAAA;IACpB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,iBAAiB,EAAE,MAAM,CAAA;IACzB,0BAA0B,EAAE,MAAM,CAAA;IAClC,mBAAmB,EAAE,MAAM,CAAA;IAC3B,eAAe,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,cAAc,CAAA;IACpB,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAwFD,wFAAwF;AACxF,wBAAsB,wBAAwB,CAAC,IAAI,EAAE;IACnD,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,cAAc,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB,GAAG,OAAO,CAAC,IAAI,CAAC,0BAA0B,EAAE,cAAc,CAAC,CAAC,CAoC5D;AA2LD;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,0BAA0B,CAAC,CA8CrC"}
@@ -1,5 +1,6 @@
1
1
  import { collectionOf } from '../../collections/helpers';
2
2
  import { syncCompositeProgression, } from './grantSideEffects';
3
+ import { transitionLogData, transitionToId } from './logData';
3
4
  import { recordLog, syncTierChangedLog } from './recordEvent';
4
5
  import { relationId } from './relationId';
5
6
  import { resolveCatalogId } from './resolveCatalog';
@@ -33,7 +34,7 @@ async function backfillGrantedLogs(args) {
33
34
  },
34
35
  });
35
36
  const logged = new Set(logs.docs
36
- .map((log) => relationId(log.data?.achievement))
37
+ .map((log) => transitionToId(log.data))
37
38
  .filter((id) => Boolean(id)));
38
39
  let written = 0;
39
40
  for (const grant of grants.docs) {
@@ -63,10 +64,12 @@ async function backfillGrantedLogs(args) {
63
64
  scopeId: args.scopeId,
64
65
  type: 'achievement.granted',
65
66
  data: {
66
- achievement: achievementId,
67
- ...(slug ? { achievementSlug: slug } : {}),
68
- backfilled: true,
67
+ ...transitionLogData({
68
+ from: null,
69
+ to: { id: achievementId, slug },
70
+ }),
69
71
  ...(completedAt ? { completedAt } : {}),
72
+ backfilled: true,
70
73
  },
71
74
  });
72
75
  logged.add(achievementId);
@@ -1 +1 @@
1
- {"version":3,"file":"recordEvent.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/recordEvent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAWtD,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,cAAc,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CACtC,CAAA;AAgBD,6DAA6D;AAC7D,wBAAsB,SAAS,CAAC,IAAI,EAAE,cAAc,wEAuCnD;AAED,4CAA4C;AAC5C,eAAO,MAAM,WAAW,kBAAY,CAAA;AAEpC;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,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,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB,wEAiCA;AAED,6FAA6F;AAC7F,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,cAAc,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB,iFAgDA"}
1
+ {"version":3,"file":"recordEvent.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/recordEvent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAWtD,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,cAAc,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CACtC,CAAA;AAgBD,6DAA6D;AAC7D,wBAAsB,SAAS,CAAC,IAAI,EAAE,cAAc,wEAuCnD;AAED,4CAA4C;AAC5C,eAAO,MAAM,WAAW,kBAAY,CAAA;AAEpC;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,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,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB,wEAiCA;AAED,6FAA6F;AAC7F,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,CAAC,EAAE,cAAc,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB,iFA0DA"}
@@ -2,8 +2,8 @@ import { APIError } from 'payload';
2
2
  import { DEFAULT_ACHIEVEMENT_METRIC_SLUG } from '../../catalog';
3
3
  import { collectionOf } from '../../collections/helpers';
4
4
  import { eventTypeRequiresActor } from './eventTypeRequiresActor';
5
+ import { transitionLogData, transitionToId } from './logData';
5
6
  import { resolveCatalogId } from './resolveCatalog';
6
- import { relationId } from './relationId';
7
7
  import { resolveCurrentTier } from './resolveCurrentTier';
8
8
  import { userScopeWhere } from './where';
9
9
  async function catalogId(args) {
@@ -113,22 +113,35 @@ export async function syncTierChangedLog(args) {
113
113
  and: [userScopeWhere(args.userId, scopeId), { type: { equals: typeId } }],
114
114
  },
115
115
  });
116
- const lastTierId = relationId(latest.docs[0]?.data?.tier);
116
+ const latestData = latest.docs[0]?.data;
117
+ const lastTierId = transitionToId(latestData);
117
118
  const currentId = current?.id ?? null;
118
119
  if (lastTierId === currentId)
119
120
  return null;
120
121
  if (!currentId)
121
122
  return null;
123
+ let fromSlug;
124
+ if (latestData && typeof latestData === 'object' && !Array.isArray(latestData)) {
125
+ const record = latestData;
126
+ const to = record.to;
127
+ if (to && typeof to === 'object' && !Array.isArray(to)) {
128
+ const slug = to.slug;
129
+ if (typeof slug === 'string')
130
+ fromSlug = slug;
131
+ }
132
+ else if (typeof record.tierSlug === 'string') {
133
+ fromSlug = record.tierSlug;
134
+ }
135
+ }
122
136
  return recordLog({
123
137
  payload: args.payload,
124
138
  req: args.req,
125
139
  userId: args.userId,
126
140
  scopeId,
127
141
  type: 'tier.changed',
128
- data: {
129
- tier: currentId,
130
- ...(current?.slug ? { tierSlug: current.slug } : {}),
131
- ...(lastTierId ? { previousTier: lastTierId } : {}),
132
- },
142
+ data: transitionLogData({
143
+ from: lastTierId ? { id: lastTierId, slug: fromSlug } : null,
144
+ to: { id: currentId, slug: current?.slug },
145
+ }),
133
146
  });
134
147
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dappermountain/payload-plugin-achievements",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "A robust, Admin-configured achievements engine for Payload: composable unlock rules, derived rank progress, request/review grants, metrics, and scoped multi-tenant support.",
5
5
  "license": "MIT",
6
6
  "type": "module",