@dappermountain/payload-plugin-achievements 0.2.13 → 0.2.15
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/dist/services/achievement/tierProgression.d.ts +7 -1
- package/dist/services/achievement/tierProgression.d.ts.map +1 -1
- package/dist/services/achievement/tierProgression.js +34 -11
- package/dist/services/achievement/validateRules.d.ts.map +1 -1
- package/dist/services/achievement/validateRules.js +4 -1
- package/package.json +1 -1
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { PayloadRequest } from 'payload';
|
|
2
|
+
/**
|
|
3
|
+
* Ensure a tier request exists when unlock rules pass.
|
|
4
|
+
* Prefer open (pending/approved) rows. If only a **rejected** request exists,
|
|
5
|
+
* leave it alone — do not auto-queue a new pending after a denial.
|
|
6
|
+
*/
|
|
2
7
|
export declare function ensureTierRequest(args: {
|
|
3
8
|
req: PayloadRequest;
|
|
4
9
|
userId: string;
|
|
@@ -9,7 +14,8 @@ export declare function ensureTierRequest(args: {
|
|
|
9
14
|
created: boolean;
|
|
10
15
|
}>;
|
|
11
16
|
/**
|
|
12
|
-
* When unlock rules pass and the tier requires review, ensure a
|
|
17
|
+
* When unlock rules pass and the tier requires review, ensure a request exists.
|
|
18
|
+
* Does not recreate a request after rejection — reopen by updating that row or creating manually.
|
|
13
19
|
*/
|
|
14
20
|
export declare function syncTierProgression(args: {
|
|
15
21
|
req: PayloadRequest;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tierProgression.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/tierProgression.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAS,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"tierProgression.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/tierProgression.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAS,MAAM,SAAS,CAAA;AAqBpD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,cAAc,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB,GAAG,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAqD9D;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,GAAG,EAAE,cAAc,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB,GAAG,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CA0DjC;AAED,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA"}
|
|
@@ -2,26 +2,48 @@ import { collectionOf } from '../../collections/helpers.js';
|
|
|
2
2
|
import { CTX_SYSTEM_REQUEST } from './contextFlags.js';
|
|
3
3
|
import { evaluateRules } from './evaluateRules.js';
|
|
4
4
|
import { hasApprovedTierRequest } from './tierApproval.js';
|
|
5
|
+
function tierRequestScopeClauses(scopeId) {
|
|
6
|
+
if (scopeId)
|
|
7
|
+
return [{ scope: { equals: scopeId } }];
|
|
8
|
+
return [{ scope: { exists: false } }];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Ensure a tier request exists when unlock rules pass.
|
|
12
|
+
* Prefer open (pending/approved) rows. If only a **rejected** request exists,
|
|
13
|
+
* leave it alone — do not auto-queue a new pending after a denial.
|
|
14
|
+
*/
|
|
5
15
|
export async function ensureTierRequest(args) {
|
|
6
|
-
const
|
|
16
|
+
const base = [
|
|
7
17
|
{ user: { equals: args.userId } },
|
|
8
18
|
{ tier: { equals: args.tierId } },
|
|
9
|
-
|
|
19
|
+
...tierRequestScopeClauses(args.scopeId),
|
|
10
20
|
];
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
21
|
+
const open = await args.req.payload.find({
|
|
22
|
+
collection: collectionOf('tierRequests'),
|
|
23
|
+
depth: 0,
|
|
24
|
+
limit: 1,
|
|
25
|
+
overrideAccess: true,
|
|
26
|
+
req: args.req,
|
|
27
|
+
where: {
|
|
28
|
+
and: [...base, { status: { in: ['pending', 'approved'] } }],
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
if (open.docs[0]) {
|
|
32
|
+
return { doc: open.docs[0], created: false };
|
|
33
|
+
}
|
|
34
|
+
const rejected = await args.req.payload.find({
|
|
16
35
|
collection: collectionOf('tierRequests'),
|
|
17
36
|
depth: 0,
|
|
18
37
|
limit: 1,
|
|
19
38
|
overrideAccess: true,
|
|
20
39
|
req: args.req,
|
|
21
|
-
|
|
40
|
+
sort: '-updatedAt',
|
|
41
|
+
where: {
|
|
42
|
+
and: [...base, { status: { equals: 'rejected' } }],
|
|
43
|
+
},
|
|
22
44
|
});
|
|
23
|
-
if (
|
|
24
|
-
return { doc:
|
|
45
|
+
if (rejected.docs[0]) {
|
|
46
|
+
return { doc: rejected.docs[0], created: false };
|
|
25
47
|
}
|
|
26
48
|
args.req.context[CTX_SYSTEM_REQUEST] = true;
|
|
27
49
|
try {
|
|
@@ -43,7 +65,8 @@ export async function ensureTierRequest(args) {
|
|
|
43
65
|
}
|
|
44
66
|
}
|
|
45
67
|
/**
|
|
46
|
-
* When unlock rules pass and the tier requires review, ensure a
|
|
68
|
+
* When unlock rules pass and the tier requires review, ensure a request exists.
|
|
69
|
+
* Does not recreate a request after rejection — reopen by updating that row or creating manually.
|
|
47
70
|
*/
|
|
48
71
|
export async function syncTierProgression(args) {
|
|
49
72
|
const scopeId = args.scopeId;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validateRules.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/validateRules.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validateRules.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/validateRules.ts"],"names":[],"mappings":"AAKA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAEvE;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,IAAI,CA2E7E;AAED,sFAAsF;AACtF,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,GAC9D,MAAM,GAAG,IAAI,CAOf;AAED,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,EAC/D,KAAK,EAAE,MAAM,GACZ,IAAI,CAKN"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { APIError } from 'payload';
|
|
2
|
+
import { isBuiltinElapsedSince } from '../../extensions/elapsedSinceOptions.js';
|
|
3
|
+
import { getMetricAnchorResolver } from '../../extensions/metricAnchors.js';
|
|
2
4
|
import { relationId } from './relationId.js';
|
|
3
5
|
import { normalizeRuleGroup } from './evaluateRules.js';
|
|
4
6
|
/**
|
|
@@ -56,7 +58,8 @@ export function validateRuleNode(rule) {
|
|
|
56
58
|
return 'Elapsed since (first event) needs an event type.';
|
|
57
59
|
}
|
|
58
60
|
}
|
|
59
|
-
else if (since
|
|
61
|
+
else if (!isBuiltinElapsedSince(since) && !getMetricAnchorResolver(since)) {
|
|
62
|
+
// Host anchors (e.g. membership-granted) are registered via extensions.metricAnchors.
|
|
60
63
|
return 'Elapsed since needs a valid since source.';
|
|
61
64
|
}
|
|
62
65
|
if (typeof rule.amount !== 'number' || Number.isNaN(rule.amount) || rule.amount < 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dappermountain/payload-plugin-achievements",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
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",
|