@dappermountain/payload-plugin-achievements 0.1.0 → 0.2.1
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 +36 -6
- package/dist/admin/ReconcileProgressionButton.d.ts +11 -0
- package/dist/admin/ReconcileProgressionButton.d.ts.map +1 -0
- package/dist/admin/ReconcileProgressionButton.js +41 -0
- package/dist/catalog.d.ts +3 -0
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +1 -0
- package/dist/cli/reconcile.d.ts +18 -0
- package/dist/cli/reconcile.d.ts.map +1 -0
- package/dist/cli/reconcile.js +90 -0
- package/dist/collections/AchievementRequests/hooks/prepareAchievementRequest.d.ts.map +1 -1
- package/dist/collections/AchievementRequests/hooks/prepareAchievementRequest.js +35 -15
- package/dist/collections/Achievements/hooks/afterRequiresReviewDisabled.d.ts +7 -0
- package/dist/collections/Achievements/hooks/afterRequiresReviewDisabled.d.ts.map +1 -0
- package/dist/collections/Achievements/hooks/afterRequiresReviewDisabled.js +15 -0
- package/dist/collections/Achievements/hooks/index.d.ts.map +1 -1
- package/dist/collections/Achievements/hooks/index.js +2 -0
- package/dist/collections/Achievements/index.d.ts.map +1 -1
- package/dist/collections/Achievements/index.js +3 -0
- package/dist/collections/Grants/hooks/afterGrantMutation.d.ts +4 -0
- package/dist/collections/Grants/hooks/afterGrantMutation.d.ts.map +1 -0
- package/dist/collections/Grants/hooks/afterGrantMutation.js +56 -0
- package/dist/collections/Grants/hooks/index.d.ts.map +1 -1
- package/dist/collections/Grants/hooks/index.js +3 -0
- package/dist/collections/Grants/index.d.ts.map +1 -1
- package/dist/collections/Grants/index.js +18 -1
- package/dist/collections/Logs/index.js +1 -1
- package/dist/collections/TierRequests/access/index.d.ts +3 -0
- package/dist/collections/TierRequests/access/index.d.ts.map +1 -0
- package/dist/collections/TierRequests/access/index.js +8 -0
- package/dist/collections/TierRequests/hooks/index.d.ts +3 -0
- package/dist/collections/TierRequests/hooks/index.d.ts.map +1 -0
- package/dist/collections/TierRequests/hooks/index.js +5 -0
- package/dist/collections/TierRequests/hooks/prepareTierRequest.d.ts +8 -0
- package/dist/collections/TierRequests/hooks/prepareTierRequest.d.ts.map +1 -0
- package/dist/collections/TierRequests/hooks/prepareTierRequest.js +67 -0
- package/dist/collections/TierRequests/index.d.ts +3 -0
- package/dist/collections/TierRequests/index.d.ts.map +1 -0
- package/dist/collections/TierRequests/index.js +47 -0
- package/dist/collections/Tiers/hooks/afterRequiresReviewDisabled.d.ts +7 -0
- package/dist/collections/Tiers/hooks/afterRequiresReviewDisabled.d.ts.map +1 -0
- package/dist/collections/Tiers/hooks/afterRequiresReviewDisabled.js +15 -0
- package/dist/collections/Tiers/hooks/index.d.ts.map +1 -1
- package/dist/collections/Tiers/hooks/index.js +2 -0
- package/dist/collections/Tiers/index.d.ts.map +1 -1
- package/dist/collections/Tiers/index.js +11 -3
- package/dist/collections/index.d.ts.map +1 -1
- package/dist/collections/index.js +2 -0
- package/dist/collections/slugs.d.ts.map +1 -1
- package/dist/collections/slugs.js +1 -0
- package/dist/defaults.d.ts +3 -0
- package/dist/defaults.d.ts.map +1 -1
- package/dist/defaults.js +12 -3
- package/dist/endpoints/index.d.ts.map +1 -1
- package/dist/endpoints/index.js +9 -3
- package/dist/endpoints/reconcile.d.ts +8 -0
- package/dist/endpoints/reconcile.d.ts.map +1 -0
- package/dist/endpoints/reconcile.js +73 -0
- 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 +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/seed/index.d.ts +2 -0
- package/dist/seed/index.d.ts.map +1 -1
- package/dist/seed/index.js +1 -0
- package/dist/services/achievement/contextFlags.d.ts +5 -0
- package/dist/services/achievement/contextFlags.d.ts.map +1 -0
- package/dist/services/achievement/contextFlags.js +4 -0
- package/dist/services/achievement/grantSideEffects.d.ts +54 -0
- package/dist/services/achievement/grantSideEffects.d.ts.map +1 -0
- package/dist/services/achievement/grantSideEffects.js +269 -0
- package/dist/services/achievement/index.d.ts +6 -0
- package/dist/services/achievement/index.d.ts.map +1 -1
- package/dist/services/achievement/index.js +4 -0
- package/dist/services/achievement/reconcile.d.ts +36 -0
- package/dist/services/achievement/reconcile.d.ts.map +1 -0
- package/dist/services/achievement/reconcile.js +292 -0
- package/dist/services/achievement/recordEvent.d.ts.map +1 -1
- package/dist/services/achievement/recordEvent.js +8 -0
- package/dist/services/achievement/releaseReviewGate.d.ts +23 -0
- package/dist/services/achievement/releaseReviewGate.d.ts.map +1 -0
- package/dist/services/achievement/releaseReviewGate.js +71 -0
- package/dist/services/achievement/resolveCurrentTier.d.ts +1 -1
- package/dist/services/achievement/resolveCurrentTier.d.ts.map +1 -1
- package/dist/services/achievement/resolveCurrentTier.js +12 -2
- package/dist/services/achievement/submitAchievementRequest.d.ts +4 -2
- package/dist/services/achievement/submitAchievementRequest.d.ts.map +1 -1
- package/dist/services/achievement/submitAchievementRequest.js +8 -101
- package/dist/services/achievement/tierApproval.d.ts +8 -0
- package/dist/services/achievement/tierApproval.d.ts.map +1 -0
- package/dist/services/achievement/tierApproval.js +21 -0
- package/dist/services/achievement/tierProgression.d.ts +22 -0
- package/dist/services/achievement/tierProgression.d.ts.map +1 -0
- package/dist/services/achievement/tierProgression.js +100 -0
- package/dist/types.d.ts +8 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +11 -6
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { APIError } from 'payload';
|
|
2
|
+
import { collectionOf } from '../../../collections/helpers';
|
|
3
|
+
import { CTX_SYSTEM_REQUEST } from '../../../services/achievement/contextFlags';
|
|
4
|
+
import { syncTierChangedLog } from '../../../services/achievement/recordEvent';
|
|
5
|
+
import { relationId } from '../../../services/achievement/relationId';
|
|
6
|
+
function scopeIdOf(data) {
|
|
7
|
+
return relationId(data?.scope);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* System-created requests (reconcile / progression sync) may set user freely.
|
|
11
|
+
* Member creates are not used for tiers in v1 — reviewers/system own the flow.
|
|
12
|
+
*/
|
|
13
|
+
export const prepareTierRequest = async ({ data, operation, req, originalDoc, }) => {
|
|
14
|
+
if (!data)
|
|
15
|
+
return data;
|
|
16
|
+
if (operation === 'create') {
|
|
17
|
+
if (!req.context?.[CTX_SYSTEM_REQUEST]) {
|
|
18
|
+
const userId = req.user?.id;
|
|
19
|
+
if (typeof userId !== 'string')
|
|
20
|
+
throw new APIError('Unauthorized', 401);
|
|
21
|
+
data.user = userId;
|
|
22
|
+
}
|
|
23
|
+
const tierId = relationId(data.tier);
|
|
24
|
+
if (!tierId)
|
|
25
|
+
throw new APIError('tier required', 400);
|
|
26
|
+
const tier = await req.payload.findByID({
|
|
27
|
+
collection: collectionOf('tiers'),
|
|
28
|
+
id: tierId,
|
|
29
|
+
depth: 0,
|
|
30
|
+
overrideAccess: true,
|
|
31
|
+
req,
|
|
32
|
+
});
|
|
33
|
+
if (!tier)
|
|
34
|
+
throw new APIError(`Unknown tier: ${tierId}`, 400);
|
|
35
|
+
if (tier.requiresReview !== true) {
|
|
36
|
+
throw new APIError('This tier does not require review.', 400);
|
|
37
|
+
}
|
|
38
|
+
data.tier = tierId;
|
|
39
|
+
if (!data.status)
|
|
40
|
+
data.status = 'pending';
|
|
41
|
+
return data;
|
|
42
|
+
}
|
|
43
|
+
if (operation === 'update' && originalDoc) {
|
|
44
|
+
data.user = originalDoc.user;
|
|
45
|
+
data.tier = originalDoc.tier;
|
|
46
|
+
}
|
|
47
|
+
return data;
|
|
48
|
+
};
|
|
49
|
+
export const afterTierRequestChange = async ({ doc, operation, previousDoc, req, }) => {
|
|
50
|
+
const status = doc.status;
|
|
51
|
+
if (status !== 'approved')
|
|
52
|
+
return doc;
|
|
53
|
+
const previousStatus = operation === 'update' ? previousDoc?.status : undefined;
|
|
54
|
+
if (previousStatus === 'approved')
|
|
55
|
+
return doc;
|
|
56
|
+
const userId = relationId(doc.user);
|
|
57
|
+
if (!userId)
|
|
58
|
+
return doc;
|
|
59
|
+
const scopeId = scopeIdOf(doc);
|
|
60
|
+
await syncTierChangedLog({
|
|
61
|
+
payload: req.payload,
|
|
62
|
+
req: req,
|
|
63
|
+
userId,
|
|
64
|
+
scopeId,
|
|
65
|
+
});
|
|
66
|
+
return doc;
|
|
67
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/collections/TierRequests/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAS,MAAM,SAAS,CAAA;AAOtD,wBAAgB,2BAA2B,IAAI,gBAAgB,CA2C9D"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { collectionAdmin, collectionOf, optionalFields, slugOf } from '../helpers';
|
|
2
|
+
import { scopeField, userField } from '../fields';
|
|
3
|
+
import { access } from './access';
|
|
4
|
+
import { hooks } from './hooks';
|
|
5
|
+
export function buildTierRequestsCollection() {
|
|
6
|
+
return {
|
|
7
|
+
slug: slugOf('tierRequests'),
|
|
8
|
+
labels: {
|
|
9
|
+
singular: 'Tier Request',
|
|
10
|
+
plural: 'Tier Requests',
|
|
11
|
+
},
|
|
12
|
+
access: { ...access },
|
|
13
|
+
hooks,
|
|
14
|
+
admin: collectionAdmin({
|
|
15
|
+
useAsTitle: 'id',
|
|
16
|
+
defaultColumns: ['user', 'tier', 'status', 'createdAt'],
|
|
17
|
+
description: 'When a member has met a tier’s unlock rules but that tier requires review before it becomes their current tier.',
|
|
18
|
+
}),
|
|
19
|
+
fields: [
|
|
20
|
+
...optionalFields(scopeField()),
|
|
21
|
+
userField(),
|
|
22
|
+
{
|
|
23
|
+
name: 'tier',
|
|
24
|
+
type: 'relationship',
|
|
25
|
+
relationTo: collectionOf('tiers'),
|
|
26
|
+
required: true,
|
|
27
|
+
index: true,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'status',
|
|
31
|
+
type: 'select',
|
|
32
|
+
required: true,
|
|
33
|
+
defaultValue: 'pending',
|
|
34
|
+
options: [
|
|
35
|
+
{ label: 'Pending', value: 'pending' },
|
|
36
|
+
{ label: 'Approved', value: 'approved' },
|
|
37
|
+
{ label: 'Rejected', value: 'rejected' },
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'note',
|
|
42
|
+
type: 'textarea',
|
|
43
|
+
admin: { description: 'Optional note from the reviewer.' },
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CollectionAfterChangeHook } from 'payload';
|
|
2
|
+
/**
|
|
3
|
+
* When Requires review is turned off, approve pending tier requests for this step
|
|
4
|
+
* so each waiting user gets a tier.changed audit via existing request hooks.
|
|
5
|
+
*/
|
|
6
|
+
export declare const afterRequiresReviewDisabled: CollectionAfterChangeHook;
|
|
7
|
+
//# sourceMappingURL=afterRequiresReviewDisabled.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"afterRequiresReviewDisabled.d.ts","sourceRoot":"","sources":["../../../../src/collections/Tiers/hooks/afterRequiresReviewDisabled.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAA;AAOxD;;;GAGG;AACH,eAAO,MAAM,2BAA2B,EAAE,yBAmBzC,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { didDisableRequiresReview, releasePendingTierReviews, } from '../../../services/achievement/releaseReviewGate';
|
|
2
|
+
/**
|
|
3
|
+
* When Requires review is turned off, approve pending tier requests for this step
|
|
4
|
+
* so each waiting user gets a tier.changed audit via existing request hooks.
|
|
5
|
+
*/
|
|
6
|
+
export const afterRequiresReviewDisabled = async ({ doc, operation, previousDoc, req, }) => {
|
|
7
|
+
if (operation !== 'update')
|
|
8
|
+
return doc;
|
|
9
|
+
if (!didDisableRequiresReview(previousDoc, doc)) {
|
|
10
|
+
return doc;
|
|
11
|
+
}
|
|
12
|
+
const tierId = String(doc.id);
|
|
13
|
+
await releasePendingTierReviews({ req, tierId });
|
|
14
|
+
return doc;
|
|
15
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/collections/Tiers/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/collections/Tiers/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAK/C,eAAO,MAAM,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAG3C,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/collections/Tiers/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAQ/C,wBAAgB,oBAAoB,IAAI,gBAAgB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/collections/Tiers/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAQ/C,wBAAgB,oBAAoB,IAAI,gBAAgB,CAwFvD"}
|
|
@@ -11,8 +11,8 @@ export function buildTiersCollection() {
|
|
|
11
11
|
hooks,
|
|
12
12
|
admin: collectionAdmin({
|
|
13
13
|
useAsTitle: 'name',
|
|
14
|
-
defaultColumns: columnsWithOptionalScope(['name', 'slug', 'rank', 'scope']),
|
|
15
|
-
description: 'Unlock rules decide when someone reaches this step. Current tier is derived by walking the ladder (lowest rank first)
|
|
14
|
+
defaultColumns: columnsWithOptionalScope(['name', 'slug', 'rank', 'requiresReview', 'scope']),
|
|
15
|
+
description: 'Unlock rules decide when someone reaches this step. Current tier is derived by walking the ladder (lowest rank first). Tiers with Requires review also need an approved tier request.',
|
|
16
16
|
}),
|
|
17
17
|
fields: [
|
|
18
18
|
...optionalFields(scopeField()),
|
|
@@ -67,12 +67,20 @@ export function buildTiersCollection() {
|
|
|
67
67
|
: []),
|
|
68
68
|
],
|
|
69
69
|
},
|
|
70
|
+
{
|
|
71
|
+
name: 'requiresReview',
|
|
72
|
+
type: 'checkbox',
|
|
73
|
+
defaultValue: false,
|
|
74
|
+
admin: {
|
|
75
|
+
description: 'When enabled, unlock rules alone are not enough — an approved tier request is required before this step becomes the member’s current tier. Turning it off approves pending requests for this tier automatically.',
|
|
76
|
+
},
|
|
77
|
+
},
|
|
70
78
|
{
|
|
71
79
|
name: 'unlockRules',
|
|
72
80
|
type: 'group',
|
|
73
81
|
label: 'Unlock rules',
|
|
74
82
|
admin: {
|
|
75
|
-
description: 'Rules that must pass to unlock this tier (all-of or any-of). Empty rules mean this step is open.
|
|
83
|
+
description: 'Rules that must pass to unlock this tier (all-of or any-of). Empty rules mean this step is open. Current tier is derived by walking ranks; optional tier.changed logs record moves.',
|
|
76
84
|
},
|
|
77
85
|
fields: buildRuleTreeFields(),
|
|
78
86
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/collections/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/collections/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAyB/C,qEAAqE;AACrE,wBAAgB,2BAA2B,IAAI,gBAAgB,EAAE,CAMhE"}
|
|
@@ -6,6 +6,7 @@ import { buildEventTypesCollection } from './EventTypes/index';
|
|
|
6
6
|
import { buildGrantsCollection } from './Grants/index';
|
|
7
7
|
import { buildLogsCollection } from './Logs/index';
|
|
8
8
|
import { buildMetricsCollection } from './Metrics/index';
|
|
9
|
+
import { buildTierRequestsCollection } from './TierRequests/index';
|
|
9
10
|
import { buildTiersCollection } from './Tiers/index';
|
|
10
11
|
const builders = {
|
|
11
12
|
metrics: buildMetricsCollection,
|
|
@@ -14,6 +15,7 @@ const builders = {
|
|
|
14
15
|
achievements: buildAchievementsCollection,
|
|
15
16
|
grants: buildGrantsCollection,
|
|
16
17
|
achievementRequests: buildAchievementRequestsCollection,
|
|
18
|
+
tierRequests: buildTierRequestsCollection,
|
|
17
19
|
logs: buildLogsCollection,
|
|
18
20
|
};
|
|
19
21
|
/** Build achievement collections after plugin options are stored. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slugs.d.ts","sourceRoot":"","sources":["../../src/collections/slugs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAA;AAElF;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,gBAAgB,CAAA;AAEtD;;;GAGG;AACH,eAAO,MAAM,6BAA6B,EAAE,MAAM,CAAC,wBAAwB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"slugs.d.ts","sourceRoot":"","sources":["../../src/collections/slugs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAA;AAElF;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,gBAAgB,CAAA;AAEtD;;;GAGG;AACH,eAAO,MAAM,6BAA6B,EAAE,MAAM,CAAC,wBAAwB,EAAE,MAAM,CASlF,CAAA;AAED,eAAO,MAAM,mBAAmB,iBAAiB,CAAA;AAEjD;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,wBAA6B,GACrC,MAAM,CAAC,wBAAwB,EAAE,MAAM,CAAC,CAkB1C;AAED,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,wBAA6B,GAAG,MAAM,GAAG,KAAK,CAMxF"}
|
package/dist/defaults.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { AchievementPluginOptions } from './types';
|
|
|
2
2
|
import { resolveCollectionSlugs } from './collections/slugs';
|
|
3
3
|
export declare const DEFAULT_USERS_COLLECTION = "users";
|
|
4
4
|
export declare const DEFAULT_ME_ENDPOINT_PATH = "/achievements/me";
|
|
5
|
+
export declare const DEFAULT_RECONCILE_ENDPOINT_PATH = "/achievements/reconcile";
|
|
5
6
|
export type ResolvedAchievementOptions = Omit<AchievementPluginOptions, 'users' | 'endpoints'> & {
|
|
6
7
|
enabled: boolean;
|
|
7
8
|
usersCollectionSlug: string;
|
|
@@ -14,8 +15,10 @@ export type ResolvedAchievementOptions = Omit<AchievementPluginOptions, 'users'
|
|
|
14
15
|
adminGroup: string | false;
|
|
15
16
|
endpoints: {
|
|
16
17
|
me: string | false;
|
|
18
|
+
reconcile: string | false;
|
|
17
19
|
};
|
|
18
20
|
};
|
|
19
21
|
export declare function resolveMeEndpointPath(me?: string | false): string | false;
|
|
22
|
+
export declare function resolveReconcileEndpointPath(path?: string | false): string | false;
|
|
20
23
|
export declare function resolveOptions(options?: AchievementPluginOptions): ResolvedAchievementOptions;
|
|
21
24
|
//# sourceMappingURL=defaults.d.ts.map
|
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,SAAS,CAAA;AACvD,OAAO,EAEL,sBAAsB,EACvB,MAAM,qBAAqB,CAAA;AAE5B,eAAO,MAAM,wBAAwB,UAAU,CAAA;AAC/C,eAAO,MAAM,wBAAwB,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAA;AACvD,OAAO,EAEL,sBAAsB,EACvB,MAAM,qBAAqB,CAAA;AAE5B,eAAO,MAAM,wBAAwB,UAAU,CAAA;AAC/C,eAAO,MAAM,wBAAwB,qBAAqB,CAAA;AAC1D,eAAO,MAAM,+BAA+B,4BAA4B,CAAA;AAExE,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAAC,wBAAwB,EAAE,OAAO,GAAG,WAAW,CAAC,GAAG;IAC/F,OAAO,EAAE,OAAO,CAAA;IAChB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,KAAK,EAAE;QAAE,YAAY,EAAE,OAAO,CAAA;KAAE,CAAA;IAChC,+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,SAAS,EAAE,MAAM,GAAG,KAAK,CAAA;KAAE,CAAA;CAC7D,CAAA;AAQD,wBAAgB,qBAAqB,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,CAIzE;AAED,wBAAgB,4BAA4B,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,CAIlF;AAED,wBAAgB,cAAc,CAAC,OAAO,GAAE,wBAA6B,GAAG,0BAA0B,CAejG"}
|
package/dist/defaults.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { resolveAdminGroup, resolveCollectionSlugs, } from './collections/slugs';
|
|
2
2
|
export const DEFAULT_USERS_COLLECTION = 'users';
|
|
3
3
|
export const DEFAULT_ME_ENDPOINT_PATH = '/achievements/me';
|
|
4
|
-
|
|
4
|
+
export const DEFAULT_RECONCILE_ENDPOINT_PATH = '/achievements/reconcile';
|
|
5
|
+
function normalizeEndpointPath(path, fallback) {
|
|
5
6
|
const trimmed = path.trim();
|
|
6
7
|
if (!trimmed)
|
|
7
|
-
return
|
|
8
|
+
return fallback;
|
|
8
9
|
return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
|
|
9
10
|
}
|
|
10
11
|
export function resolveMeEndpointPath(me) {
|
|
@@ -12,7 +13,14 @@ export function resolveMeEndpointPath(me) {
|
|
|
12
13
|
return false;
|
|
13
14
|
if (me == null)
|
|
14
15
|
return DEFAULT_ME_ENDPOINT_PATH;
|
|
15
|
-
return normalizeEndpointPath(me);
|
|
16
|
+
return normalizeEndpointPath(me, DEFAULT_ME_ENDPOINT_PATH);
|
|
17
|
+
}
|
|
18
|
+
export function resolveReconcileEndpointPath(path) {
|
|
19
|
+
if (path === false)
|
|
20
|
+
return false;
|
|
21
|
+
if (path == null)
|
|
22
|
+
return DEFAULT_RECONCILE_ENDPOINT_PATH;
|
|
23
|
+
return normalizeEndpointPath(path, DEFAULT_RECONCILE_ENDPOINT_PATH);
|
|
16
24
|
}
|
|
17
25
|
export function resolveOptions(options = {}) {
|
|
18
26
|
return {
|
|
@@ -26,6 +34,7 @@ export function resolveOptions(options = {}) {
|
|
|
26
34
|
adminGroup: resolveAdminGroup(options),
|
|
27
35
|
endpoints: {
|
|
28
36
|
me: resolveMeEndpointPath(options.endpoints?.me),
|
|
37
|
+
reconcile: resolveReconcileEndpointPath(options.endpoints?.reconcile),
|
|
29
38
|
},
|
|
30
39
|
};
|
|
31
40
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/endpoints/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEvC,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/endpoints/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEvC,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAA;AAI7D,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,0BAA0B,GAAG,QAAQ,EAAE,CASzF"}
|
package/dist/endpoints/index.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { buildMeEndpoint } from './me';
|
|
2
|
+
import { buildReconcileEndpoint } from './reconcile';
|
|
2
3
|
export function buildAchievementEndpoints(options) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
const endpoints = [];
|
|
5
|
+
if (options.endpoints.me !== false) {
|
|
6
|
+
endpoints.push(buildMeEndpoint(options.endpoints.me));
|
|
7
|
+
}
|
|
8
|
+
if (options.endpoints.reconcile !== false) {
|
|
9
|
+
endpoints.push(buildReconcileEndpoint(options.endpoints.reconcile));
|
|
10
|
+
}
|
|
11
|
+
return endpoints;
|
|
6
12
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Endpoint } from 'payload';
|
|
2
|
+
/**
|
|
3
|
+
* `POST` repair progression from existing grants / requests.
|
|
4
|
+
* Body (optional): `{ userId?, scopeId?, achievementId?, achievementSlug?, tierId?, tierSlug?, limit? }`.
|
|
5
|
+
* Requires host `canReview`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildReconcileEndpoint(path: string): Endpoint;
|
|
8
|
+
//# sourceMappingURL=reconcile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../src/endpoints/reconcile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAkB,MAAM,SAAS,CAAA;AAoCvD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CA2C7D"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { getUserId } from '../access/helpers/index';
|
|
2
|
+
import { getAchievementOptions } from '../options-store';
|
|
3
|
+
import { reconcileProgression } from '../services/achievement/reconcile';
|
|
4
|
+
function requestUrl(req) {
|
|
5
|
+
return req.url ? new URL(req.url, 'http://local') : null;
|
|
6
|
+
}
|
|
7
|
+
async function readBody(req) {
|
|
8
|
+
try {
|
|
9
|
+
if (typeof req.json === 'function') {
|
|
10
|
+
const body = await req.json();
|
|
11
|
+
if (body && typeof body === 'object')
|
|
12
|
+
return body;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
// empty body
|
|
17
|
+
}
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
20
|
+
function stringParam(body, url, keys) {
|
|
21
|
+
for (const key of keys) {
|
|
22
|
+
const fromBody = body[key];
|
|
23
|
+
if (typeof fromBody === 'string' && fromBody.length > 0)
|
|
24
|
+
return fromBody;
|
|
25
|
+
const fromUrl = url?.searchParams.get(key);
|
|
26
|
+
if (fromUrl && fromUrl.length > 0)
|
|
27
|
+
return fromUrl;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* `POST` repair progression from existing grants / requests.
|
|
33
|
+
* Body (optional): `{ userId?, scopeId?, achievementId?, achievementSlug?, tierId?, tierSlug?, limit? }`.
|
|
34
|
+
* Requires host `canReview`.
|
|
35
|
+
*/
|
|
36
|
+
export function buildReconcileEndpoint(path) {
|
|
37
|
+
return {
|
|
38
|
+
path,
|
|
39
|
+
method: 'post',
|
|
40
|
+
handler: async (req) => {
|
|
41
|
+
const actorId = getUserId(req);
|
|
42
|
+
if (!actorId || !req.user) {
|
|
43
|
+
return Response.json({ errors: [{ message: 'Unauthorized' }] }, { status: 401 });
|
|
44
|
+
}
|
|
45
|
+
const { canReview } = getAchievementOptions();
|
|
46
|
+
if (!canReview) {
|
|
47
|
+
return Response.json({ errors: [{ message: 'Forbidden' }] }, { status: 403 });
|
|
48
|
+
}
|
|
49
|
+
const body = await readBody(req);
|
|
50
|
+
const url = requestUrl(req);
|
|
51
|
+
const scopeId = stringParam(body, url, ['scopeId', 'scope']);
|
|
52
|
+
if (!(await canReview(req.user, scopeId))) {
|
|
53
|
+
return Response.json({ errors: [{ message: 'Forbidden' }] }, { status: 403 });
|
|
54
|
+
}
|
|
55
|
+
const limitRaw = body.limit;
|
|
56
|
+
const limit = typeof limitRaw === 'number' && Number.isFinite(limitRaw)
|
|
57
|
+
? Math.floor(limitRaw)
|
|
58
|
+
: undefined;
|
|
59
|
+
const result = await reconcileProgression({
|
|
60
|
+
payload: req.payload,
|
|
61
|
+
req,
|
|
62
|
+
scopeId,
|
|
63
|
+
userId: stringParam(body, url, ['userId', 'user']),
|
|
64
|
+
achievementId: stringParam(body, url, ['achievementId']),
|
|
65
|
+
achievementSlug: stringParam(body, url, ['achievementSlug', 'achievement']),
|
|
66
|
+
tierId: stringParam(body, url, ['tierId']),
|
|
67
|
+
tierSlug: stringParam(body, url, ['tierSlug', 'tier']),
|
|
68
|
+
limit,
|
|
69
|
+
});
|
|
70
|
+
return Response.json({ ok: true, ...result });
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
package/dist/exports/client.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { ActorField } from '../admin/ActorField';
|
|
2
2
|
export { MetricChangeField } from '../admin/MetricChangeField';
|
|
3
3
|
export { MetricField } from '../admin/MetricField';
|
|
4
|
+
export { ReconcileProgressionButton } from '../admin/ReconcileProgressionButton';
|
|
4
5
|
//# 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,qBAAqB,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA"}
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/exports/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAClD,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAA"}
|
package/dist/exports/client.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -3,11 +3,11 @@ 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, resolveCurrentTier, resolveTierProgress, reviewAchievementRequest, submitAchievementRequest, syncTierChangedLog, validateRuleGroup, validateRuleNode, } from './services/achievement/index';
|
|
7
|
-
export type { ResolvedTier, RuleGroup, TierLadderProgress, CatalogProgressGroup, CatalogProgressItem } 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, validateRuleGroup, validateRuleNode, } from './services/achievement/index';
|
|
7
|
+
export type { ResolvedTier, RuleGroup, TierLadderProgress, CatalogProgressGroup, CatalogProgressItem, 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
|
-
export { DEFAULT_ME_ENDPOINT_PATH } from './defaults';
|
|
10
|
+
export { DEFAULT_ME_ENDPOINT_PATH, DEFAULT_RECONCILE_ENDPOINT_PATH } from './defaults';
|
|
11
11
|
export { collectionOf, slugOf } from './collections/helpers';
|
|
12
12
|
export { isAchievementPluginEnabled } from './enabled';
|
|
13
13
|
//# sourceMappingURL=index.d.ts.map
|
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,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,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,EACxB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,8BAA8B,CAAA;AACrC,YAAY,
|
|
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"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
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, resolveCurrentTier, resolveTierProgress, reviewAchievementRequest, submitAchievementRequest, syncTierChangedLog, 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, validateRuleGroup, validateRuleNode, } from './services/achievement/index';
|
|
5
5
|
export { DEFAULT_COLLECTION_BASE_SLUGS, DEFAULT_COLLECTION_PREFIX, DEFAULT_ADMIN_GROUP } from './collections/slugs';
|
|
6
|
-
export { DEFAULT_ME_ENDPOINT_PATH } from './defaults';
|
|
6
|
+
export { DEFAULT_ME_ENDPOINT_PATH, DEFAULT_RECONCILE_ENDPOINT_PATH } from './defaults';
|
|
7
7
|
export { collectionOf, slugOf } from './collections/helpers';
|
|
8
8
|
export { isAchievementPluginEnabled } from './enabled';
|
package/dist/seed/index.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export type AchievementSeedTier = {
|
|
|
14
14
|
/** Optional media upload id when the host configured `mediaCollection`. */
|
|
15
15
|
image?: string;
|
|
16
16
|
scope?: string;
|
|
17
|
+
/** When true, unlock rules are not enough — an approved tier request is required. */
|
|
18
|
+
requiresReview?: boolean;
|
|
17
19
|
/** Bare rule array (AND) or `{ combinator, rules }`. Seed slugs resolved to relationships. */
|
|
18
20
|
unlockRules?: Record<string, unknown>[] | {
|
|
19
21
|
combinator?: string;
|
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;AAMtC,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,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,yEAAyE;IACzE,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,gFAAgF;IAChF,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,UAAU,CAAC,EAAE,yBAAyB,EAAE,CAAA;IACxC,OAAO,CAAC,EAAE,yBAAyB,EAAE,CAAA;IACrC,KAAK,CAAC,EAAE,mBAAmB,EAAE,CAAA;IAC7B,YAAY,CAAC,EAAE,0BAA0B,EAAE,CAAA;IAC3C,gEAAgE;IAChE,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAA;CAClC,CAAA;AAmMD,2FAA2F;AAC3F,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB5F;AAiCD;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/seed/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAMtC,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,yEAAyE;IACzE,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,gFAAgF;IAChF,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,UAAU,CAAC,EAAE,yBAAyB,EAAE,CAAA;IACxC,OAAO,CAAC,EAAE,yBAAyB,EAAE,CAAA;IACrC,KAAK,CAAC,EAAE,mBAAmB,EAAE,CAAA;IAC7B,YAAY,CAAC,EAAE,0BAA0B,EAAE,CAAA;IAC3C,gEAAgE;IAChE,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAA;CAClC,CAAA;AAmMD,2FAA2F;AAC3F,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB5F;AAiCD;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmLnG"}
|
package/dist/seed/index.js
CHANGED
|
@@ -221,6 +221,7 @@ export async function seedAchievements(payload, input) {
|
|
|
221
221
|
slug: tier.slug,
|
|
222
222
|
rank: tier.rank,
|
|
223
223
|
scope: tier.scope,
|
|
224
|
+
requiresReview: tier.requiresReview ?? false,
|
|
224
225
|
...(tier.icon ? { icon: tier.icon } : {}),
|
|
225
226
|
...(tier.image ? { image: tier.image } : {}),
|
|
226
227
|
unlockRules: { combinator: 'and', rules: [] },
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Shared req.context keys for grant / request side-effect guards. */
|
|
2
|
+
export declare const CTX_SKIP_GRANT_SIDE_EFFECTS = "achievementSkipGrantSideEffects";
|
|
3
|
+
export declare const CTX_CASCADING_REVOKE = "achievementCascadingRevoke";
|
|
4
|
+
export declare const CTX_SYSTEM_REQUEST = "achievementSystemRequest";
|
|
5
|
+
//# sourceMappingURL=contextFlags.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextFlags.d.ts","sourceRoot":"","sources":["../../../src/services/achievement/contextFlags.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,eAAO,MAAM,2BAA2B,oCAAoC,CAAA;AAC5E,eAAO,MAAM,oBAAoB,+BAA+B,CAAA;AAChE,eAAO,MAAM,kBAAkB,6BAA6B,CAAA"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Shared req.context keys for grant / request side-effect guards. */
|
|
2
|
+
export const CTX_SKIP_GRANT_SIDE_EFFECTS = 'achievementSkipGrantSideEffects';
|
|
3
|
+
export const CTX_CASCADING_REVOKE = 'achievementCascadingRevoke';
|
|
4
|
+
export const CTX_SYSTEM_REQUEST = 'achievementSystemRequest';
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { PayloadRequest } from 'payload';
|
|
2
|
+
export { CTX_CASCADING_REVOKE, CTX_SKIP_GRANT_SIDE_EFFECTS, CTX_SYSTEM_REQUEST } from './contextFlags';
|
|
3
|
+
/** Create a grant row only — side effects run in Grants afterChange. */
|
|
4
|
+
export declare function createGrantRow(args: {
|
|
5
|
+
req: PayloadRequest;
|
|
6
|
+
userId: string;
|
|
7
|
+
achievementId: string;
|
|
8
|
+
scopeId?: string | null;
|
|
9
|
+
note?: string | null;
|
|
10
|
+
completedAt?: string;
|
|
11
|
+
}): Promise<Record<string, unknown>>;
|
|
12
|
+
/** Ensure a pending achievement request exists (system / reconcile path). */
|
|
13
|
+
export declare function ensureAchievementRequest(args: {
|
|
14
|
+
req: PayloadRequest;
|
|
15
|
+
userId: string;
|
|
16
|
+
achievementId: string;
|
|
17
|
+
scopeId?: string | null;
|
|
18
|
+
}): Promise<{
|
|
19
|
+
doc: Record<string, unknown>;
|
|
20
|
+
created: boolean;
|
|
21
|
+
}>;
|
|
22
|
+
/**
|
|
23
|
+
* Auto-grant or open a request for composed achievements whose completion rules pass.
|
|
24
|
+
*/
|
|
25
|
+
export declare function syncCompositeProgression(args: {
|
|
26
|
+
req: PayloadRequest;
|
|
27
|
+
userId: string;
|
|
28
|
+
scopeId: string | null;
|
|
29
|
+
}): Promise<{
|
|
30
|
+
granted: number;
|
|
31
|
+
requested: number;
|
|
32
|
+
}>;
|
|
33
|
+
/** Delete grants for composed achievements that no longer pass completion rules. */
|
|
34
|
+
export declare function revokeIncompleteComposites(args: {
|
|
35
|
+
req: PayloadRequest;
|
|
36
|
+
userId: string;
|
|
37
|
+
scopeId: string | null;
|
|
38
|
+
}): Promise<number>;
|
|
39
|
+
export declare function runAfterGrantCreated(args: {
|
|
40
|
+
req: PayloadRequest;
|
|
41
|
+
userId: string;
|
|
42
|
+
achievementId: string;
|
|
43
|
+
scopeId: string | null;
|
|
44
|
+
achievementSlug?: string;
|
|
45
|
+
}): Promise<void>;
|
|
46
|
+
export declare function runAfterGrantDeleted(args: {
|
|
47
|
+
req: PayloadRequest;
|
|
48
|
+
userId: string;
|
|
49
|
+
achievementId: string;
|
|
50
|
+
scopeId: string | null;
|
|
51
|
+
achievementSlug?: string;
|
|
52
|
+
cascading?: boolean;
|
|
53
|
+
}): Promise<void>;
|
|
54
|
+
//# sourceMappingURL=grantSideEffects.d.ts.map
|
|
@@ -0,0 +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"}
|