@dappermountain/payload-plugin-achievements 0.3.1 → 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 +71 -7
- 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/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 +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -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/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/index.d.ts +2 -0
- package/dist/services/achievement/index.d.ts.map +1 -1
- package/dist/services/achievement/index.js +1 -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/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# `@dappermountain/payload-plugin-achievements`
|
|
2
2
|
|
|
3
|
-
A Payload CMS plugin for a **rule-based achievements and ranking system** — tiers, badges, points, grants, review workflows, and an append-only activity log — configured in Admin
|
|
3
|
+
A Payload CMS plugin for a **rule-based achievements and ranking system** — tiers, badges, points, grants, review workflows, and an append-only activity log — configured in Admin.
|
|
4
4
|
|
|
5
5
|
Most gamification features end up as one-off collections and scattered hooks. This plugin gives you a reusable engine: define *what* can be earned and *how* it unlocks in Payload Admin, then record events from your application code. Current tier and progress are **derived** from rules and history, so you don’t maintain a separate progress table that drifts out of sync.
|
|
6
6
|
|
|
@@ -16,8 +16,9 @@ Most gamification features end up as one-off collections and scattered hooks. Th
|
|
|
16
16
|
|
|
17
17
|
## Requirements
|
|
18
18
|
|
|
19
|
-
- Payload `^3.
|
|
20
|
-
- `@payloadcms/ui` `^3.
|
|
19
|
+
- Payload `^3.89`
|
|
20
|
+
- `@payloadcms/ui` `^3.89`
|
|
21
|
+
- `@payloadcms/richtext-lexical` `^3.89`
|
|
21
22
|
- React `^19`
|
|
22
23
|
|
|
23
24
|
## Install
|
|
@@ -120,7 +121,7 @@ Unlock (tiers), eligibility, and completion (achievements) share the same rule t
|
|
|
120
121
|
| `event-count` / `metric-minimum` | Fraction of the target |
|
|
121
122
|
| `achievement-complete` | `1` if granted; otherwise rolls up that achievement’s completion (or eligibility) rules |
|
|
122
123
|
|
|
123
|
-
**Computed elapsed metrics** — `kind: computed` + `compute: elapsed` + `unit` (`seconds` | `minutes` | `hours` | `days` | `years`) + `since` (built-in `user-created-at` / `first-event`, or a host `metricAnchors` key). Gate tenure with `metric-minimum` against that metric. Years use a fixed 365-day length
|
|
124
|
+
**Computed elapsed metrics** — `kind: computed` + `compute: elapsed` + `unit` (`seconds` | `minutes` | `hours` | `days` | `years`) + `since` (built-in `user-created-at` / `first-event`, or a host `metricAnchors` key). Gate tenure with `metric-minimum` against that metric. Years use a fixed 365-day length. System seed only creates stored `points` — product computed metrics (e.g. `days`) are host-seeded after registering anchors.
|
|
124
125
|
|
|
125
126
|
**Stored metrics** — totals live in `achievement-metric-balances`, updated by `recordMetricChange` (log + balance dual-write). Logs remain the audit/rebuild source; reconcile backfills balances.
|
|
126
127
|
|
|
@@ -230,6 +231,50 @@ achievementPlugin({
|
|
|
230
231
|
| --- | --- | --- |
|
|
231
232
|
| `includeJoins` | `true` | Injects an `achievements` group on the users collection with joins to grants and requests. Set `false` if you own that UI yourself. |
|
|
232
233
|
|
|
234
|
+
### `subjects`
|
|
235
|
+
|
|
236
|
+
Host documents that logs may point at via a polymorphic `subject` field (same idea as `requiresActor` / `requiresMetric`).
|
|
237
|
+
|
|
238
|
+
| Option | Default | Notes |
|
|
239
|
+
| --- | --- | --- |
|
|
240
|
+
| `collections` | `[]` | Allowlist of collection slugs (e.g. `posts`, `comments`). Empty / omit → subject feature is off (no log field, no event-type subject flags). |
|
|
241
|
+
|
|
242
|
+
When configured:
|
|
243
|
+
|
|
244
|
+
- Event types gain **Requires a subject** + optional **Subject collections** (`subjectRelationTo`).
|
|
245
|
+
- Logs gain a polymorphic **Subject** relationship (Admin links to the host doc).
|
|
246
|
+
- `recordLog({ subject: { relationTo, value } })` (or `logSubject(relationTo, value)`) writes the ref.
|
|
247
|
+
- Prefer `subject` over stuffing ids into `data` JSON.
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
achievementPlugin({
|
|
251
|
+
canReview,
|
|
252
|
+
subjects: {
|
|
253
|
+
collections: ['posts', 'comments', 'endorsements'],
|
|
254
|
+
},
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
await seedAchievements(payload, {
|
|
258
|
+
eventTypes: [
|
|
259
|
+
{
|
|
260
|
+
name: 'Comment created',
|
|
261
|
+
slug: 'comment.created',
|
|
262
|
+
requiresSubject: true,
|
|
263
|
+
subjectRelationTo: ['comments'],
|
|
264
|
+
},
|
|
265
|
+
],
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
await recordLog({
|
|
269
|
+
payload,
|
|
270
|
+
userId,
|
|
271
|
+
scopeId,
|
|
272
|
+
type: 'comment.created',
|
|
273
|
+
actorId: userId,
|
|
274
|
+
subject: logSubject('comments', commentId),
|
|
275
|
+
})
|
|
276
|
+
```
|
|
277
|
+
|
|
233
278
|
### `endpoints`
|
|
234
279
|
|
|
235
280
|
| Option | Default | Notes |
|
|
@@ -250,7 +295,7 @@ achievementPlugin({
|
|
|
250
295
|
extensions: {
|
|
251
296
|
metricAnchors: {
|
|
252
297
|
'membership-granted': {
|
|
253
|
-
// Admin UI i18n (Payload label
|
|
298
|
+
// Admin UI i18n (Payload label / OptionLabel)
|
|
254
299
|
label: ({ t }) => t('custom:achievements:anchors:membershipGranted'),
|
|
255
300
|
resolve: async ({ payload, userId, scopeId }) => {
|
|
256
301
|
// return a Date from host data, or null
|
|
@@ -275,7 +320,7 @@ achievementPlugin({
|
|
|
275
320
|
|
|
276
321
|
## Server helpers
|
|
277
322
|
|
|
278
|
-
Use these from hooks, jobs, and trusted endpoints
|
|
323
|
+
Use these from hooks, jobs, and trusted endpoints:
|
|
279
324
|
|
|
280
325
|
```ts
|
|
281
326
|
import {
|
|
@@ -293,9 +338,18 @@ import {
|
|
|
293
338
|
buildUnlockRequirementLeaves,
|
|
294
339
|
submitAchievementRequest,
|
|
295
340
|
reviewAchievementRequest,
|
|
341
|
+
logSubject,
|
|
296
342
|
} from '@dappermountain/payload-plugin-achievements'
|
|
297
343
|
|
|
298
344
|
await recordLog({ payload, userId, scopeId, type: 'host.custom-kind', actorId })
|
|
345
|
+
await recordLog({
|
|
346
|
+
payload,
|
|
347
|
+
userId,
|
|
348
|
+
scopeId,
|
|
349
|
+
type: 'comment.created',
|
|
350
|
+
actorId: userId,
|
|
351
|
+
subject: logSubject('comments', commentId),
|
|
352
|
+
})
|
|
299
353
|
await recordMetricChange({ payload, userId, scopeId, metric: 'points', change: 10 })
|
|
300
354
|
const points = await resolveMetricValue({ payload, userId, scopeId, metric: 'points' })
|
|
301
355
|
const board = await getMetricLeaderboard({ payload, metric: 'points', scopeId, limit: 20 })
|
|
@@ -341,6 +395,8 @@ Engine log `data` for those system events uses a shared transition envelope:
|
|
|
341
395
|
| `achievement.granted` | `null` | achievement |
|
|
342
396
|
| `achievement.revoked` | achievement | `null` |
|
|
343
397
|
|
|
398
|
+
Host document links belong on polymorphic **`subject`** (when `subjects.collections` is set), not inside `data`.
|
|
399
|
+
|
|
344
400
|
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).
|
|
345
401
|
|
|
346
402
|
**Admin:** Grants list includes **Repair progression**, which `POST`s the reconcile endpoint (requires `canReview`).
|
|
@@ -372,7 +428,15 @@ import {
|
|
|
372
428
|
await seedAchievementCatalog(payload) // system metrics + event types (English)
|
|
373
429
|
|
|
374
430
|
await seedAchievements(payload, {
|
|
375
|
-
eventTypes: [
|
|
431
|
+
eventTypes: [
|
|
432
|
+
{ name: 'Custom kind', slug: 'host.custom-kind', requiresActor: true },
|
|
433
|
+
{
|
|
434
|
+
name: 'Comment created',
|
|
435
|
+
slug: 'comment.created',
|
|
436
|
+
requiresSubject: true,
|
|
437
|
+
subjectRelationTo: ['comments'],
|
|
438
|
+
},
|
|
439
|
+
],
|
|
376
440
|
metrics: [{ name: 'Streak days', slug: 'streak-days' }],
|
|
377
441
|
tiers: [
|
|
378
442
|
{
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RelationshipFieldClientProps } from 'payload';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
type SubjectFieldProps = RelationshipFieldClientProps & {
|
|
4
|
+
eventTypesSlug: string;
|
|
5
|
+
/** Plugin allowlist — used when the event type leaves `subjectRelationTo` empty. */
|
|
6
|
+
subjectCollections: string[];
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Polymorphic subject — only when the selected event type requires a host document.
|
|
10
|
+
* Clears the value when the type does not require a subject.
|
|
11
|
+
*/
|
|
12
|
+
export declare function SubjectField(props: SubjectFieldProps): React.JSX.Element | null;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=SubjectField.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SubjectField.d.ts","sourceRoot":"","sources":["../../src/admin/SubjectField.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,SAAS,CAAA;AAE3D,OAAO,KAA6B,MAAM,OAAO,CAAA;AAIjD,KAAK,iBAAiB,GAAG,4BAA4B,GAAG;IACtD,cAAc,EAAE,MAAM,CAAA;IACtB,oFAAoF;IACpF,kBAAkB,EAAE,MAAM,EAAE,CAAA;CAC7B,CAAA;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,4BAoCpD"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { RelationshipField, useField } from '@payloadcms/ui';
|
|
4
|
+
import { useEffect, useMemo } from 'react';
|
|
5
|
+
import { useEventTypeFlags } from './useEventTypeFlags.js';
|
|
6
|
+
/**
|
|
7
|
+
* Polymorphic subject — only when the selected event type requires a host document.
|
|
8
|
+
* Clears the value when the type does not require a subject.
|
|
9
|
+
*/
|
|
10
|
+
export function SubjectField(props) {
|
|
11
|
+
const { eventTypesSlug, subjectCollections, ...fieldArgs } = props;
|
|
12
|
+
const { requiresSubject, subjectRelationTo } = useEventTypeFlags(eventTypesSlug);
|
|
13
|
+
const { setValue, value } = useField({ path: fieldArgs.path });
|
|
14
|
+
const allowedCollections = useMemo(() => {
|
|
15
|
+
if (subjectRelationTo.length > 0) {
|
|
16
|
+
return subjectRelationTo.filter((slug) => subjectCollections.includes(slug));
|
|
17
|
+
}
|
|
18
|
+
return subjectCollections;
|
|
19
|
+
}, [subjectCollections, subjectRelationTo]);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!requiresSubject && value != null && value !== '') {
|
|
22
|
+
setValue(null);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (!requiresSubject || !value || typeof value !== 'object')
|
|
26
|
+
return;
|
|
27
|
+
const relationTo = value.relationTo;
|
|
28
|
+
if (typeof relationTo === 'string' && allowedCollections.length > 0 && !allowedCollections.includes(relationTo)) {
|
|
29
|
+
setValue(null);
|
|
30
|
+
}
|
|
31
|
+
}, [requiresSubject, setValue, value, allowedCollections]);
|
|
32
|
+
if (!requiresSubject || allowedCollections.length === 0)
|
|
33
|
+
return null;
|
|
34
|
+
return (_jsx(RelationshipField, { ...fieldArgs, field: {
|
|
35
|
+
...fieldArgs.field,
|
|
36
|
+
relationTo: allowedCollections,
|
|
37
|
+
} }));
|
|
38
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export type EventTypeFlags = {
|
|
2
2
|
requiresActor: boolean;
|
|
3
3
|
requiresMetric: boolean;
|
|
4
|
+
requiresSubject: boolean;
|
|
5
|
+
subjectRelationTo: string[];
|
|
4
6
|
};
|
|
5
7
|
/** Resolve event-type flags for the selected `type` field (fetch when only an id is present). */
|
|
6
8
|
export declare function useEventTypeFlags(eventTypesSlug: string): EventTypeFlags;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEventTypeFlags.d.ts","sourceRoot":"","sources":["../../src/admin/useEventTypeFlags.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,cAAc,GAAG;IAC3B,aAAa,EAAE,OAAO,CAAA;IACtB,cAAc,EAAE,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"useEventTypeFlags.d.ts","sourceRoot":"","sources":["../../src/admin/useEventTypeFlags.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,cAAc,GAAG;IAC3B,aAAa,EAAE,OAAO,CAAA;IACtB,cAAc,EAAE,OAAO,CAAA;IACvB,eAAe,EAAE,OAAO,CAAA;IACxB,iBAAiB,EAAE,MAAM,EAAE,CAAA;CAC5B,CAAA;AAqDD,iGAAiG;AACjG,wBAAgB,iBAAiB,CAAC,cAAc,EAAE,MAAM,GAAG,cAAc,CA4CxE"}
|
|
@@ -15,19 +15,35 @@ function relationId(value) {
|
|
|
15
15
|
}
|
|
16
16
|
return null;
|
|
17
17
|
}
|
|
18
|
+
function readSubjectRelationTo(value) {
|
|
19
|
+
if (!Array.isArray(value))
|
|
20
|
+
return [];
|
|
21
|
+
return value.filter((entry) => typeof entry === 'string' && entry.trim().length > 0);
|
|
22
|
+
}
|
|
18
23
|
function flagsFromValue(typeValue) {
|
|
19
24
|
if (!typeValue || typeof typeValue !== 'object')
|
|
20
25
|
return null;
|
|
21
|
-
if (!('requiresActor' in typeValue ||
|
|
26
|
+
if (!('requiresActor' in typeValue ||
|
|
27
|
+
'requiresMetric' in typeValue ||
|
|
28
|
+
'requiresSubject' in typeValue ||
|
|
29
|
+
'subjectRelationTo' in typeValue ||
|
|
30
|
+
'slug' in typeValue)) {
|
|
22
31
|
return null;
|
|
23
32
|
}
|
|
24
33
|
const row = typeValue;
|
|
25
34
|
return {
|
|
26
35
|
requiresActor: Boolean(row.requiresActor),
|
|
27
36
|
requiresMetric: Boolean(row.requiresMetric) || row.slug === 'metric.delta',
|
|
37
|
+
requiresSubject: Boolean(row.requiresSubject),
|
|
38
|
+
subjectRelationTo: readSubjectRelationTo(row.subjectRelationTo),
|
|
28
39
|
};
|
|
29
40
|
}
|
|
30
|
-
const empty = {
|
|
41
|
+
const empty = {
|
|
42
|
+
requiresActor: false,
|
|
43
|
+
requiresMetric: false,
|
|
44
|
+
requiresSubject: false,
|
|
45
|
+
subjectRelationTo: [],
|
|
46
|
+
};
|
|
31
47
|
/** Resolve event-type flags for the selected `type` field (fetch when only an id is present). */
|
|
32
48
|
export function useEventTypeFlags(eventTypesSlug) {
|
|
33
49
|
const { config } = useConfig();
|
|
@@ -47,7 +63,9 @@ export function useEventTypeFlags(eventTypesSlug) {
|
|
|
47
63
|
const controller = new AbortController();
|
|
48
64
|
const api = config.routes?.api ?? '/api';
|
|
49
65
|
const serverURL = config.serverURL ?? '';
|
|
50
|
-
const url = `${serverURL}${api}/${eventTypesSlug}/${typeId}
|
|
66
|
+
const url = `${serverURL}${api}/${eventTypesSlug}/${typeId}` +
|
|
67
|
+
`?depth=0&select[requiresActor]=true&select[requiresMetric]=true` +
|
|
68
|
+
`&select[requiresSubject]=true&select[subjectRelationTo]=true&select[slug]=true`;
|
|
51
69
|
void fetch(url, { credentials: 'include', signal: controller.signal })
|
|
52
70
|
.then(async (res) => {
|
|
53
71
|
if (!res.ok) {
|
package/dist/cli/reconcile.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export declare function runReconcileCli(args: {
|
|
|
12
12
|
config: SanitizedConfig | Promise<SanitizedConfig>;
|
|
13
13
|
argv?: string[];
|
|
14
14
|
filters?: ReconcileCliFilters;
|
|
15
|
-
/** When false, return the result
|
|
15
|
+
/** When false, return the result and leave the process running. Default true. */
|
|
16
16
|
exit?: boolean;
|
|
17
17
|
}): Promise<ReconcileProgressionResult>;
|
|
18
18
|
//# sourceMappingURL=reconcile.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../src/cli/reconcile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,SAAS,CAAA;AAE1D,OAAO,EAEL,KAAK,wBAAwB,EAC7B,KAAK,0BAA0B,EAChC,MAAM,sCAAsC,CAAA;AAE7C,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,wBAAwB,EAAE,SAAS,GAAG,KAAK,CAAC,CAAA;AA2BnF,sFAAsF;AACtF,wBAAgB,qBAAqB,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,mBAAmB,CAUjG;AAED,wBAAgB,qBAAqB,IAAI,IAAI,CAc5C;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;IAClD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,OAAO,CAAC,EAAE,mBAAmB,CAAA;IAC7B,
|
|
1
|
+
{"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../src/cli/reconcile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,SAAS,CAAA;AAE1D,OAAO,EAEL,KAAK,wBAAwB,EAC7B,KAAK,0BAA0B,EAChC,MAAM,sCAAsC,CAAA;AAE7C,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,wBAAwB,EAAE,SAAS,GAAG,KAAK,CAAC,CAAA;AA2BnF,sFAAsF;AACtF,wBAAgB,qBAAqB,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,mBAAmB,CAUjG;AAED,wBAAgB,qBAAqB,IAAI,IAAI,CAc5C;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;IAClD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,OAAO,CAAC,EAAE,mBAAmB,CAAA;IAC7B,iFAAiF;IACjF,IAAI,CAAC,EAAE,OAAO,CAAA;CACf,GAAG,OAAO,CAAC,0BAA0B,CAAC,CA4BtC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/collections/EventTypes/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAS,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/collections/EventTypes/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAS,MAAM,SAAS,CAAA;AAqCtD,wBAAgB,yBAAyB,IAAI,gBAAgB,CAoE5D"}
|
|
@@ -1,8 +1,36 @@
|
|
|
1
|
+
import { getAchievementOptions } from '../../options-store.js';
|
|
1
2
|
import { collectionAdmin, collectionOf, columnsWithOptionalScope, optionalFields, slugOf } from '../helpers.js';
|
|
2
3
|
import { localizedNameField, scopeField } from '../fields/index.js';
|
|
3
4
|
import { access } from './access/index.js';
|
|
4
5
|
import { hooks } from './hooks/index.js';
|
|
6
|
+
function subjectCatalogFields() {
|
|
7
|
+
const collections = getAchievementOptions().subjectCollections;
|
|
8
|
+
if (collections.length === 0)
|
|
9
|
+
return [];
|
|
10
|
+
return [
|
|
11
|
+
{
|
|
12
|
+
name: 'requiresSubject',
|
|
13
|
+
type: 'checkbox',
|
|
14
|
+
defaultValue: false,
|
|
15
|
+
label: 'Requires a subject',
|
|
16
|
+
admin: {
|
|
17
|
+
description: 'Turn this on when the log points at a host document (for example a post or comment). We’ll ask for that document when it’s logged.',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'subjectRelationTo',
|
|
22
|
+
type: 'select',
|
|
23
|
+
hasMany: true,
|
|
24
|
+
options: collections.map((slug) => ({ label: slug, value: slug })),
|
|
25
|
+
admin: {
|
|
26
|
+
condition: (_, siblingData) => Boolean(siblingData?.requiresSubject),
|
|
27
|
+
description: 'Which host collections this type may reference. Leave empty to allow any collection from the plugin subjects allowlist.',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
}
|
|
5
32
|
export function buildEventTypesCollection() {
|
|
33
|
+
const subjectFields = subjectCatalogFields();
|
|
6
34
|
return {
|
|
7
35
|
slug: slugOf('eventTypes'),
|
|
8
36
|
access: { ...access },
|
|
@@ -14,6 +42,7 @@ export function buildEventTypesCollection() {
|
|
|
14
42
|
'slug',
|
|
15
43
|
'requiresActor',
|
|
16
44
|
'requiresMetric',
|
|
45
|
+
...(subjectFields.length > 0 ? ['requiresSubject'] : []),
|
|
17
46
|
'system',
|
|
18
47
|
'scope',
|
|
19
48
|
]),
|
|
@@ -46,6 +75,7 @@ export function buildEventTypesCollection() {
|
|
|
46
75
|
description: 'Turn this on when the entry changes a score (like points). We’ll ask which metric and by how much.',
|
|
47
76
|
},
|
|
48
77
|
},
|
|
78
|
+
...subjectFields,
|
|
49
79
|
{
|
|
50
80
|
name: 'system',
|
|
51
81
|
type: 'checkbox',
|
|
@@ -2,4 +2,5 @@ import type { Validate } from 'payload';
|
|
|
2
2
|
export declare const validateActor: Validate;
|
|
3
3
|
export declare const validateMetric: Validate;
|
|
4
4
|
export declare const validateChange: Validate;
|
|
5
|
+
export declare const validateSubject: Validate;
|
|
5
6
|
//# sourceMappingURL=validators.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../../../src/collections/Logs/fields/validators.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../../../src/collections/Logs/fields/validators.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAavC,eAAO,MAAM,aAAa,EAAE,QAW3B,CAAA;AAED,eAAO,MAAM,cAAc,EAAE,QA0B5B,CAAA;AAED,eAAO,MAAM,cAAc,EAAE,QAW5B,CAAA;AAED,eAAO,MAAM,eAAe,EAAE,QAqC7B,CAAA"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { getAchievementOptions } from '../../../options-store.js';
|
|
1
2
|
import { collectionOf } from '../../../collections/helpers.js';
|
|
2
|
-
import { eventTypeRequiresActor, eventTypeRequiresMetric, } from '../../../services/achievement/eventTypeRequiresActor.js';
|
|
3
|
+
import { eventTypeRequiresActor, eventTypeRequiresMetric, eventTypeSubjectRelationTo, getEventTypeFlags, } from '../../../services/achievement/eventTypeRequiresActor.js';
|
|
4
|
+
import { readLogSubject } from '../../../services/achievement/logSubject.js';
|
|
3
5
|
import { relationId } from '../../../services/achievement/relationId.js';
|
|
4
6
|
export const validateActor = async (value, { data, req }) => {
|
|
5
7
|
if (!req?.payload)
|
|
@@ -55,3 +57,37 @@ export const validateChange = async (value, { data, req }) => {
|
|
|
55
57
|
}
|
|
56
58
|
return true;
|
|
57
59
|
};
|
|
60
|
+
export const validateSubject = async (value, { data, req }) => {
|
|
61
|
+
if (!req?.payload)
|
|
62
|
+
return true;
|
|
63
|
+
const allowlist = getAchievementOptions().subjectCollections;
|
|
64
|
+
if (allowlist.length === 0)
|
|
65
|
+
return true;
|
|
66
|
+
const typeId = relationId(data?.type);
|
|
67
|
+
if (!typeId)
|
|
68
|
+
return true;
|
|
69
|
+
const flags = await getEventTypeFlags({
|
|
70
|
+
payload: req.payload,
|
|
71
|
+
req,
|
|
72
|
+
typeIdOrDoc: typeId,
|
|
73
|
+
});
|
|
74
|
+
const subject = readLogSubject(value);
|
|
75
|
+
if (!flags.requiresSubject) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
if (!subject) {
|
|
79
|
+
return 'This log type needs a subject (the host document it is about).';
|
|
80
|
+
}
|
|
81
|
+
if (!allowlist.includes(subject.relationTo)) {
|
|
82
|
+
return `Subject collection "${subject.relationTo}" is not in the plugin subjects allowlist.`;
|
|
83
|
+
}
|
|
84
|
+
const allowed = await eventTypeSubjectRelationTo({
|
|
85
|
+
payload: req.payload,
|
|
86
|
+
req,
|
|
87
|
+
typeIdOrDoc: typeId,
|
|
88
|
+
});
|
|
89
|
+
if (allowed.length > 0 && !allowed.includes(subject.relationTo)) {
|
|
90
|
+
return `This log type only allows subjects from: ${allowed.join(', ')}.`;
|
|
91
|
+
}
|
|
92
|
+
return true;
|
|
93
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ensureTypeRequirements.d.ts","sourceRoot":"","sources":["../../../../src/collections/Logs/hooks/ensureTypeRequirements.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"ensureTypeRequirements.d.ts","sourceRoot":"","sources":["../../../../src/collections/Logs/hooks/ensureTypeRequirements.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,SAAS,CAAA;AAc3D,eAAO,MAAM,sBAAsB,EAAE,4BA+DpC,CAAA"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { APIError } from 'payload';
|
|
2
|
+
import { getAchievementOptions } from '../../../options-store.js';
|
|
2
3
|
import { collectionOf } from '../../../collections/helpers.js';
|
|
3
|
-
import {
|
|
4
|
+
import { eventTypeSubjectRelationTo, getEventTypeFlags, } from '../../../services/achievement/eventTypeRequiresActor.js';
|
|
5
|
+
import { readLogSubject } from '../../../services/achievement/logSubject.js';
|
|
4
6
|
import { relationId } from '../../../services/achievement/relationId.js';
|
|
5
7
|
export const ensureTypeRequirements = async ({ data, req }) => {
|
|
6
8
|
if (!data)
|
|
@@ -8,18 +10,11 @@ export const ensureTypeRequirements = async ({ data, req }) => {
|
|
|
8
10
|
const typeId = relationId(data.type);
|
|
9
11
|
if (!typeId)
|
|
10
12
|
return data;
|
|
11
|
-
const flags = {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}),
|
|
17
|
-
requiresMetric: await eventTypeRequiresMetric({
|
|
18
|
-
payload: req.payload,
|
|
19
|
-
req,
|
|
20
|
-
typeIdOrDoc: typeId,
|
|
21
|
-
}),
|
|
22
|
-
};
|
|
13
|
+
const flags = await getEventTypeFlags({
|
|
14
|
+
payload: req.payload,
|
|
15
|
+
req,
|
|
16
|
+
typeIdOrDoc: typeId,
|
|
17
|
+
});
|
|
23
18
|
if (flags.requiresActor && !relationId(data.actor)) {
|
|
24
19
|
throw new APIError('This log type needs an actor (who caused it).', 400);
|
|
25
20
|
}
|
|
@@ -44,5 +39,19 @@ export const ensureTypeRequirements = async ({ data, req }) => {
|
|
|
44
39
|
throw new APIError('Computed metrics cannot be changed with a metric delta. Use a stored metric such as Points.', 400);
|
|
45
40
|
}
|
|
46
41
|
}
|
|
42
|
+
if (flags.requiresSubject && getAchievementOptions().subjectCollections.length > 0) {
|
|
43
|
+
const subject = readLogSubject(data.subject);
|
|
44
|
+
if (!subject) {
|
|
45
|
+
throw new APIError('This log type needs a subject (the host document it is about).', 400);
|
|
46
|
+
}
|
|
47
|
+
const allowed = await eventTypeSubjectRelationTo({
|
|
48
|
+
payload: req.payload,
|
|
49
|
+
req,
|
|
50
|
+
typeIdOrDoc: typeId,
|
|
51
|
+
});
|
|
52
|
+
if (allowed.length > 0 && !allowed.includes(subject.relationTo)) {
|
|
53
|
+
throw new APIError(`This log type only allows subjects from: ${allowed.join(', ')}.`, 400);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
47
56
|
return data;
|
|
48
57
|
};
|
|
@@ -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;
|
|
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;AAqCtD,+EAA+E;AAC/E,wBAAgB,mBAAmB,IAAI,gBAAgB,CAuHtD"}
|
|
@@ -3,12 +3,34 @@ import { collectionAdmin, collectionOf, optionalFields, slugOf } from '../helper
|
|
|
3
3
|
import { getAchievementOptions } from '../../options-store.js';
|
|
4
4
|
import { scopeField, userField } from '../fields/index.js';
|
|
5
5
|
import { access } from './access/index.js';
|
|
6
|
-
import { validateActor, validateChange, validateMetric } from './fields/validators.js';
|
|
6
|
+
import { validateActor, validateChange, validateMetric, validateSubject, } from './fields/validators.js';
|
|
7
7
|
import { hooks } from './hooks/index.js';
|
|
8
|
+
function subjectLogField(eventTypesSlug) {
|
|
9
|
+
const collections = getAchievementOptions().subjectCollections;
|
|
10
|
+
if (collections.length === 0)
|
|
11
|
+
return null;
|
|
12
|
+
return {
|
|
13
|
+
name: 'subject',
|
|
14
|
+
type: 'relationship',
|
|
15
|
+
relationTo: collections,
|
|
16
|
+
index: true,
|
|
17
|
+
validate: validateSubject,
|
|
18
|
+
admin: {
|
|
19
|
+
description: 'Host document this entry is about (post, comment, …).',
|
|
20
|
+
components: {
|
|
21
|
+
Field: {
|
|
22
|
+
path: '@dappermountain/payload-plugin-achievements/client#SubjectField',
|
|
23
|
+
clientProps: { eventTypesSlug, subjectCollections: collections },
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
8
29
|
/** Append-only occurrence log. `type` / `metric` are catalog relationships. */
|
|
9
30
|
export function buildLogsCollection() {
|
|
10
31
|
const users = getAchievementOptions().usersCollectionSlug ?? 'users';
|
|
11
32
|
const eventTypesSlug = collectionOf('eventTypes');
|
|
33
|
+
const subjectField = subjectLogField(eventTypesSlug);
|
|
12
34
|
return {
|
|
13
35
|
slug: slugOf('logs'),
|
|
14
36
|
labels: {
|
|
@@ -19,7 +41,15 @@ export function buildLogsCollection() {
|
|
|
19
41
|
hooks,
|
|
20
42
|
admin: collectionAdmin({
|
|
21
43
|
useAsTitle: 'id',
|
|
22
|
-
defaultColumns: [
|
|
44
|
+
defaultColumns: [
|
|
45
|
+
'type',
|
|
46
|
+
'user',
|
|
47
|
+
'actor',
|
|
48
|
+
...(subjectField ? ['subject'] : []),
|
|
49
|
+
'metric',
|
|
50
|
+
'change',
|
|
51
|
+
'createdAt',
|
|
52
|
+
],
|
|
23
53
|
description: 'Member activity history — grants, revokes, score changes, tier moves, and host custom events. Not a second grant store.',
|
|
24
54
|
}),
|
|
25
55
|
fields: [
|
|
@@ -88,6 +118,7 @@ export function buildLogsCollection() {
|
|
|
88
118
|
},
|
|
89
119
|
],
|
|
90
120
|
},
|
|
121
|
+
...(subjectField ? [subjectField] : []),
|
|
91
122
|
{
|
|
92
123
|
type: 'collapsible',
|
|
93
124
|
label: 'Details',
|
|
@@ -105,7 +136,7 @@ export function buildLogsCollection() {
|
|
|
105
136
|
name: 'data',
|
|
106
137
|
type: 'json',
|
|
107
138
|
admin: {
|
|
108
|
-
description: 'Engine transition envelope: `{ from, to }` each `{ id, slug? }` or null (tier.changed, achievement.granted/revoked).
|
|
139
|
+
description: 'Engine transition envelope: `{ from, to }` each `{ id, slug? }` or null (tier.changed, achievement.granted/revoked). Prefer `subject` for host document links.',
|
|
109
140
|
},
|
|
110
141
|
},
|
|
111
142
|
],
|
package/dist/defaults.d.ts
CHANGED
|
@@ -4,13 +4,15 @@ export declare const DEFAULT_USERS_COLLECTION = "users";
|
|
|
4
4
|
export declare const DEFAULT_ME_ENDPOINT_PATH = "/achievements/me";
|
|
5
5
|
export declare const DEFAULT_LEADERBOARD_ENDPOINT_PATH = "/achievements/leaderboard";
|
|
6
6
|
export declare const DEFAULT_RECONCILE_ENDPOINT_PATH = "/achievements/reconcile";
|
|
7
|
-
export type ResolvedAchievementOptions = Omit<AchievementPluginOptions, 'users' | 'endpoints'> & {
|
|
7
|
+
export type ResolvedAchievementOptions = Omit<AchievementPluginOptions, 'users' | 'endpoints' | 'subjects'> & {
|
|
8
8
|
enabled: boolean;
|
|
9
9
|
seedSystemCatalog: boolean;
|
|
10
10
|
usersCollectionSlug: string;
|
|
11
11
|
users: {
|
|
12
12
|
includeJoins: boolean;
|
|
13
13
|
};
|
|
14
|
+
/** Host collections allowed as polymorphic log subjects (empty = feature off). */
|
|
15
|
+
subjectCollections: string[];
|
|
14
16
|
/** Resolved collection slug map (after prefix + overrides). */
|
|
15
17
|
collectionSlugs: ReturnType<typeof resolveCollectionSlugs>;
|
|
16
18
|
/** Admin nav group for plugin collections (`false` = ungrouped). */
|
|
@@ -21,6 +23,8 @@ export type ResolvedAchievementOptions = Omit<AchievementPluginOptions, 'users'
|
|
|
21
23
|
reconcile: string | false;
|
|
22
24
|
};
|
|
23
25
|
};
|
|
26
|
+
/** Unique, non-empty collection slugs from plugin `subjects.collections`. */
|
|
27
|
+
export declare function resolveSubjectCollections(options?: AchievementPluginOptions): string[];
|
|
24
28
|
export declare function resolveMeEndpointPath(me?: string | false): string | false;
|
|
25
29
|
export declare function resolveLeaderboardEndpointPath(path?: string | false): string | false;
|
|
26
30
|
export declare function resolveReconcileEndpointPath(path?: string | false): string | false;
|
package/dist/defaults.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAA;AAC1D,OAAO,EAEL,sBAAsB,EACvB,MAAM,wBAAwB,CAAA;AAE/B,eAAO,MAAM,wBAAwB,UAAU,CAAA;AAC/C,eAAO,MAAM,wBAAwB,qBAAqB,CAAA;AAC1D,eAAO,MAAM,iCAAiC,8BAA8B,CAAA;AAC5E,eAAO,MAAM,+BAA+B,4BAA4B,CAAA;AAExE,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAAC,wBAAwB,EAAE,OAAO,GAAG,WAAW,CAAC,GAAG;
|
|
1
|
+
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAA;AAC1D,OAAO,EAEL,sBAAsB,EACvB,MAAM,wBAAwB,CAAA;AAE/B,eAAO,MAAM,wBAAwB,UAAU,CAAA;AAC/C,eAAO,MAAM,wBAAwB,qBAAqB,CAAA;AAC1D,eAAO,MAAM,iCAAiC,8BAA8B,CAAA;AAC5E,eAAO,MAAM,+BAA+B,4BAA4B,CAAA;AAExE,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAAC,wBAAwB,EAAE,OAAO,GAAG,WAAW,GAAG,UAAU,CAAC,GAAG;IAC5G,OAAO,EAAE,OAAO,CAAA;IAChB,iBAAiB,EAAE,OAAO,CAAA;IAC1B,mBAAmB,EAAE,MAAM,CAAA;IAC3B,KAAK,EAAE;QAAE,YAAY,EAAE,OAAO,CAAA;KAAE,CAAA;IAChC,kFAAkF;IAClF,kBAAkB,EAAE,MAAM,EAAE,CAAA;IAC5B,+DAA+D;IAC/D,eAAe,EAAE,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAA;IAC1D,oEAAoE;IACpE,UAAU,EAAE,MAAM,GAAG,KAAK,CAAA;IAC1B,SAAS,EAAE;QAAE,EAAE,EAAE,MAAM,GAAG,KAAK,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,KAAK,CAAC;QAAC,SAAS,EAAE,MAAM,GAAG,KAAK,CAAA;KAAE,CAAA;CAC1F,CAAA;AAED,6EAA6E;AAC7E,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,MAAM,EAAE,CAWtF;AAQD,wBAAgB,qBAAqB,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,CAIzE;AAED,wBAAgB,8BAA8B,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,CAIpF;AAED,wBAAgB,4BAA4B,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,CAIlF;AAED,wBAAgB,cAAc,CAAC,OAAO,GAAE,wBAA6B,GAAG,0BAA0B,CAmBjG"}
|
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/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,11 +3,11 @@ 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, 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, validateRuleGroup, validateRuleNode, } from './services/achievement/index.js';
|
|
7
|
-
export type { ResolvedTier, RuleGroup, TierLadderProgress, CatalogProgressGroup, CatalogProgressItem, UnlockRequirementLeaf, UnlockRequirementRelation, GetUserProgressInclude, UserProgressMeResponse, UserProgressReviews, UserProgressTierRequest, 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
12
|
export { eachRuleLeaf } from './collections/hooks/eachRuleLeaf.js';
|
|
13
13
|
export { isAchievementPluginEnabled } from './enabled.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,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,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,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,9 +1,9 @@
|
|
|
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, 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, 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
8
|
export { eachRuleLeaf } from './collections/hooks/eachRuleLeaf.js';
|
|
9
9
|
export { isAchievementPluginEnabled } from './enabled.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
|
};
|
|
@@ -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
|
+
}
|
|
@@ -14,6 +14,8 @@ export { applyMetricBalanceDelta, findMetricBalance, metricBalanceKey, rebuildMe
|
|
|
14
14
|
export { resolveMetricValue, loadMetricDoc } from './resolveMetricValue.js';
|
|
15
15
|
export { resolveElapsedAnchor } from './resolveElapsedAnchor.js';
|
|
16
16
|
export { recordEvent, recordLog, recordMetricChange, syncTierChangedLog } from './recordEvent.js';
|
|
17
|
+
export { logSubject, readLogSubject } from './logSubject.js';
|
|
18
|
+
export type { LogSubjectRef } from './logSubject.js';
|
|
17
19
|
export { resolveCurrentTier } from './resolveCurrentTier.js';
|
|
18
20
|
export type { ResolvedTier } from './resolveCurrentTier.js';
|
|
19
21
|
export { resolveTierProgress } from './resolveTierProgress.js';
|
|
@@ -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,oBAAoB,CAAA;AAC5H,YAAY,EAAE,SAAS,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAC3E,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAA;AAC5E,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAA;AAChG,OAAO,EAAE,4BAA4B,EAAE,MAAM,mCAAmC,CAAA;AAChF,YAAY,EACV,8BAA8B,EAC9B,qBAAqB,EACrB,yBAAyB,GAC1B,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,YAAY,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAA;AACjH,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AACtE,YAAY,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AAChG,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAChE,YAAY,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAC9F,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,EACpB,4BAA4B,GAC7B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAChE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACjG,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAC5D,YAAY,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAC9D,YAAY,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAClE,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAA;AAC1F,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAA;AACxE,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACxE,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA;AAC/E,YAAY,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAA;AAC1F,OAAO,EACL,wBAAwB,EACxB,gCAAgC,EAChC,yBAAyB,GAC1B,MAAM,wBAAwB,CAAA;AAC/B,YAAY,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAA;AACrE,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,cAAc,GACf,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,cAAc,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,oBAAoB,CAAA;AAC5H,YAAY,EAAE,SAAS,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAC3E,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAA;AAC5E,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAA;AAChG,OAAO,EAAE,4BAA4B,EAAE,MAAM,mCAAmC,CAAA;AAChF,YAAY,EACV,8BAA8B,EAC9B,qBAAqB,EACrB,yBAAyB,GAC1B,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,YAAY,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAA;AACjH,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AACtE,YAAY,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AAChG,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAChE,YAAY,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAC9F,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,EACpB,4BAA4B,GAC7B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAChE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACjG,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAC5D,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAC5D,YAAY,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAC9D,YAAY,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAClE,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAA;AAC1F,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAA;AACxE,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACxE,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA;AAC/E,YAAY,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAA;AAC1F,OAAO,EACL,wBAAwB,EACxB,gCAAgC,EAChC,yBAAyB,GAC1B,MAAM,wBAAwB,CAAA;AAC/B,YAAY,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAA;AACrE,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,cAAc,GACf,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA"}
|
|
@@ -8,6 +8,7 @@ export { applyMetricBalanceDelta, findMetricBalance, metricBalanceKey, rebuildMe
|
|
|
8
8
|
export { resolveMetricValue, loadMetricDoc } from './resolveMetricValue.js';
|
|
9
9
|
export { resolveElapsedAnchor } from './resolveElapsedAnchor.js';
|
|
10
10
|
export { recordEvent, recordLog, recordMetricChange, syncTierChangedLog } from './recordEvent.js';
|
|
11
|
+
export { logSubject, readLogSubject } from './logSubject.js';
|
|
11
12
|
export { resolveCurrentTier } from './resolveCurrentTier.js';
|
|
12
13
|
export { resolveTierProgress } from './resolveTierProgress.js';
|
|
13
14
|
export { grantAchievement, submitAchievementRequest } from './submitAchievementRequest.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Polymorphic relationship value (Payload `relationTo` + `value`). */
|
|
2
|
+
export type LogSubjectRef = {
|
|
3
|
+
relationTo: string;
|
|
4
|
+
value: string;
|
|
5
|
+
};
|
|
6
|
+
/** Build a log subject ref for `recordLog({ subject })`. */
|
|
7
|
+
export declare function logSubject(relationTo: string, value: string): LogSubjectRef;
|
|
8
|
+
/**
|
|
9
|
+
* Normalize a polymorphic relationship (Admin / Local API shape) to `{ relationTo, value }`.
|
|
10
|
+
* Accepts nested docs (`value: { id }`) and plain ids under `value`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function readLogSubject(value: unknown): LogSubjectRef | null;
|
|
13
|
+
//# sourceMappingURL=logSubject.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logSubject.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/logSubject.ts"],"names":[],"mappings":"AAEA,uEAAuE;AACvE,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,4DAA4D;AAC5D,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,CAE3E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,GAAG,IAAI,CAOnE"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { relationId } from './relationId.js';
|
|
2
|
+
/** Build a log subject ref for `recordLog({ subject })`. */
|
|
3
|
+
export function logSubject(relationTo, value) {
|
|
4
|
+
return { relationTo, value };
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Normalize a polymorphic relationship (Admin / Local API shape) to `{ relationTo, value }`.
|
|
8
|
+
* Accepts nested docs (`value: { id }`) and plain ids under `value`.
|
|
9
|
+
*/
|
|
10
|
+
export function readLogSubject(value) {
|
|
11
|
+
if (!value || typeof value !== 'object')
|
|
12
|
+
return null;
|
|
13
|
+
const row = value;
|
|
14
|
+
if (typeof row.relationTo !== 'string' || !row.relationTo.trim())
|
|
15
|
+
return null;
|
|
16
|
+
const id = relationId(row.value);
|
|
17
|
+
if (!id)
|
|
18
|
+
return null;
|
|
19
|
+
return { relationTo: row.relationTo.trim(), value: id };
|
|
20
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Payload, PayloadRequest } from 'payload';
|
|
2
|
+
import { type LogSubjectRef } from './logSubject.js';
|
|
2
3
|
export type RecordLogInput = {
|
|
3
4
|
payload: Payload;
|
|
4
5
|
req?: PayloadRequest;
|
|
@@ -12,6 +13,11 @@ export type RecordLogInput = {
|
|
|
12
13
|
/** Signed amount the metric moved (for metric.delta logs). */
|
|
13
14
|
change?: number;
|
|
14
15
|
reason?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Host document this entry is about (polymorphic).
|
|
18
|
+
* Preferred over stuffing ids into `data` when the host configured `subjects.collections`.
|
|
19
|
+
*/
|
|
20
|
+
subject?: LogSubjectRef | null;
|
|
15
21
|
data?: Record<string, unknown> | null;
|
|
16
22
|
};
|
|
17
23
|
/** Append a catalogued occurrence to the achievement log. */
|
|
@@ -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;
|
|
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;AAUtD,OAAO,EAAE,KAAK,aAAa,EAAkB,MAAM,iBAAiB,CAAA;AAQpE,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;;;OAGG;IACH,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI,CAAA;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CACtC,CAAA;AAgBD,6DAA6D;AAC7D,wBAAsB,SAAS,CAAC,IAAI,EAAE,cAAc,wEA0EnD;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,wEAsDA;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"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { APIError } from 'payload';
|
|
2
2
|
import { DEFAULT_ACHIEVEMENT_METRIC_SLUG } from '../../catalog.js';
|
|
3
3
|
import { collectionOf } from '../../collections/helpers.js';
|
|
4
|
-
import {
|
|
4
|
+
import { getAchievementOptions } from '../../options-store.js';
|
|
5
|
+
import { eventTypeSubjectRelationTo, getEventTypeFlags, } from './eventTypeRequiresActor.js';
|
|
6
|
+
import { readLogSubject } from './logSubject.js';
|
|
5
7
|
import { applyMetricBalanceDelta } from './metricBalances.js';
|
|
6
8
|
import { transitionLogData, transitionToId } from './logData.js';
|
|
7
9
|
import { loadMetricDoc } from './resolveMetricValue.js';
|
|
@@ -33,9 +35,34 @@ export async function recordLog(args) {
|
|
|
33
35
|
value: args.metric,
|
|
34
36
|
})
|
|
35
37
|
: undefined;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
const flags = await getEventTypeFlags({
|
|
39
|
+
payload: args.payload,
|
|
40
|
+
req: args.req,
|
|
41
|
+
typeIdOrDoc: typeId,
|
|
42
|
+
});
|
|
43
|
+
if (flags.requiresActor && !args.actorId) {
|
|
44
|
+
throw new APIError('This log type needs an actor (who caused it).', 400);
|
|
45
|
+
}
|
|
46
|
+
const allowlist = getAchievementOptions().subjectCollections;
|
|
47
|
+
let subject;
|
|
48
|
+
if (args.subject) {
|
|
49
|
+
const parsed = readLogSubject(args.subject) ?? args.subject;
|
|
50
|
+
subject = parsed;
|
|
51
|
+
}
|
|
52
|
+
if (flags.requiresSubject && allowlist.length > 0) {
|
|
53
|
+
if (!subject) {
|
|
54
|
+
throw new APIError('This log type needs a subject (the host document it is about).', 400);
|
|
55
|
+
}
|
|
56
|
+
if (!allowlist.includes(subject.relationTo)) {
|
|
57
|
+
throw new APIError(`Subject collection "${subject.relationTo}" is not in the plugin subjects allowlist.`, 400);
|
|
58
|
+
}
|
|
59
|
+
const allowed = await eventTypeSubjectRelationTo({
|
|
60
|
+
payload: args.payload,
|
|
61
|
+
req: args.req,
|
|
62
|
+
typeIdOrDoc: typeId,
|
|
63
|
+
});
|
|
64
|
+
if (allowed.length > 0 && !allowed.includes(subject.relationTo)) {
|
|
65
|
+
throw new APIError(`This log type only allows subjects from: ${allowed.join(', ')}.`, 400);
|
|
39
66
|
}
|
|
40
67
|
}
|
|
41
68
|
return args.payload.create({
|
|
@@ -48,6 +75,7 @@ export async function recordLog(args) {
|
|
|
48
75
|
metric: metricId,
|
|
49
76
|
change: args.change,
|
|
50
77
|
reason: args.reason,
|
|
78
|
+
...(subject ? { subject } : {}),
|
|
51
79
|
data: args.data ?? undefined,
|
|
52
80
|
},
|
|
53
81
|
overrideAccess: true,
|
package/dist/types.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export type AchievementMetricAnchor = (args: {
|
|
|
9
9
|
/**
|
|
10
10
|
* Anchor with an Admin label.
|
|
11
11
|
* `label` is a Payload Admin UI string ({@link OptionLabel}): plain string, `{ en, es, … }`,
|
|
12
|
-
* or `({ t }) => t('custom:…')`
|
|
12
|
+
* or `({ t }) => t('custom:…')` (Admin UI / OptionLabel).
|
|
13
13
|
*/
|
|
14
14
|
export type AchievementMetricAnchorConfig = {
|
|
15
15
|
label: OptionLabel;
|
|
@@ -40,11 +40,11 @@ export type AchievementRuleType = {
|
|
|
40
40
|
/**
|
|
41
41
|
* Optional fractional progress toward this leaf (0..1).
|
|
42
42
|
* When omitted, progress falls back to `evaluate` → 0 or 1.
|
|
43
|
-
* Used for next-tier ladder fill and host UIs
|
|
43
|
+
* Used for next-tier ladder fill and host UIs (progress display).
|
|
44
44
|
*/
|
|
45
45
|
progress?: (args: AchievementRuleEvalArgs) => Promise<number> | number;
|
|
46
46
|
/**
|
|
47
|
-
* `gate` rules (e.g. `tier-at-least`) are prerequisites
|
|
47
|
+
* `gate` rules (e.g. `tier-at-least`) are prerequisites that must already hold.
|
|
48
48
|
* They are omitted from AND averages. Default: `requirement`.
|
|
49
49
|
*/
|
|
50
50
|
progressRole?: 'requirement' | 'gate';
|
|
@@ -110,6 +110,17 @@ export type AchievementEndpointsOptions = {
|
|
|
110
110
|
*/
|
|
111
111
|
reconcile?: string | false;
|
|
112
112
|
};
|
|
113
|
+
/**
|
|
114
|
+
* Host documents that achievement logs may point at (polymorphic `subject`).
|
|
115
|
+
* Empty / omitted → subject field is not registered (engine stays host-agnostic).
|
|
116
|
+
*/
|
|
117
|
+
export type AchievementSubjectsOptions = {
|
|
118
|
+
/**
|
|
119
|
+
* Collection slugs allowed on log `subject.relationTo` (e.g. `posts`, `comments`).
|
|
120
|
+
* Event types pick a subset via `subjectRelationTo`; validation rejects anything outside this list.
|
|
121
|
+
*/
|
|
122
|
+
collections?: string[];
|
|
123
|
+
};
|
|
113
124
|
export type AchievementPluginOptions = {
|
|
114
125
|
/**
|
|
115
126
|
* Whether the user may review requests and manage achievement catalogs for a scope.
|
|
@@ -121,6 +132,11 @@ export type AchievementPluginOptions = {
|
|
|
121
132
|
enabled?: boolean;
|
|
122
133
|
/** Custom root API routes registered by the plugin. */
|
|
123
134
|
endpoints?: AchievementEndpointsOptions;
|
|
135
|
+
/**
|
|
136
|
+
* Polymorphic log subjects — host allowlist of collections.
|
|
137
|
+
* When set, event types gain `requiresSubject` / `subjectRelationTo` and logs gain `subject`.
|
|
138
|
+
*/
|
|
139
|
+
subjects?: AchievementSubjectsOptions;
|
|
124
140
|
extensions?: {
|
|
125
141
|
/** Custom rule evaluators merged with built-ins. */
|
|
126
142
|
ruleTypes?: AchievementRuleType[];
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dappermountain/payload-plugin-achievements",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
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",
|
|
@@ -69,21 +69,21 @@
|
|
|
69
69
|
"skills:check": "bunx skills check -p"
|
|
70
70
|
},
|
|
71
71
|
"peerDependencies": {
|
|
72
|
-
"@payloadcms/richtext-lexical": "^3.
|
|
73
|
-
"@payloadcms/ui": "^3.
|
|
72
|
+
"@payloadcms/richtext-lexical": "^3.89.0",
|
|
73
|
+
"@payloadcms/ui": "^3.89.0",
|
|
74
74
|
"lucide-react": "^0.543.0",
|
|
75
|
-
"payload": "^3.
|
|
75
|
+
"payload": "^3.89.0",
|
|
76
76
|
"react": "^19.0.0"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
79
|
"@dappermountain/agent-payload": "^0.1.5",
|
|
80
80
|
"@dappermountain/agent-practices": "^0.1.2",
|
|
81
|
-
"@payloadcms/richtext-lexical": "3.
|
|
82
|
-
"@payloadcms/ui": "3.
|
|
81
|
+
"@payloadcms/richtext-lexical": "3.89.0",
|
|
82
|
+
"@payloadcms/ui": "3.89.0",
|
|
83
83
|
"@types/bun": "1.4.0",
|
|
84
84
|
"@types/react": "^19.0.0",
|
|
85
85
|
"lucide-react": "0.543.0",
|
|
86
|
-
"payload": "3.
|
|
86
|
+
"payload": "3.89.0",
|
|
87
87
|
"prettier": "^3.6.2",
|
|
88
88
|
"react": "19.2.4",
|
|
89
89
|
"typescript": "5.9.2"
|