@stackedapp/utils 1.23.2 → 1.23.4

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.
@@ -0,0 +1,11 @@
1
+ import { IDynamicCondition, IDynamicGroup, StackedBaseUserExtra } from '@stackedapp/types';
2
+ export declare const calculatePercent: (amount: number | undefined, goal: number | undefined, isMet: boolean) => number;
3
+ export declare const formatList: (items: string[]) => string;
4
+ export declare const calculateDynamicConditionPercent: (dynamicObj: Record<string, string | number | boolean> | undefined, cond: IDynamicCondition) => number;
5
+ export declare const calculateDynamicGroupPercent: (dynamicObj: Record<string, string | number | boolean> | undefined, group: IDynamicGroup | undefined) => number;
6
+ export declare function evaluateDynamicCondition(dynamicObj: Record<string, string | number | boolean>, cond: IDynamicCondition, claimMultiplier?: number): boolean;
7
+ export declare function meetsDynamicConditions(dynamicObj: Record<string, string | number | boolean> | undefined | null, dynamicGroup: IDynamicGroup, claimMultiplier?: number): boolean;
8
+ export declare function getMaxClaimsForDynamicCondition(dynamicObj: Record<string, string | number | boolean>, cond: IDynamicCondition): number;
9
+ export declare function getMaxClaimsForDynamicGroup(dynamicObj: Record<string, string | number | boolean>, dynamicGroup: IDynamicGroup, currentClaimCount?: number): number;
10
+ export declare function aggregateTokenBalances(data?: StackedBaseUserExtra): Record<string, number>;
11
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1,256 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.calculateDynamicGroupPercent = exports.calculateDynamicConditionPercent = exports.formatList = exports.calculatePercent = void 0;
4
+ exports.evaluateDynamicCondition = evaluateDynamicCondition;
5
+ exports.meetsDynamicConditions = meetsDynamicConditions;
6
+ exports.getMaxClaimsForDynamicCondition = getMaxClaimsForDynamicCondition;
7
+ exports.getMaxClaimsForDynamicGroup = getMaxClaimsForDynamicGroup;
8
+ exports.aggregateTokenBalances = aggregateTokenBalances;
9
+ const calculatePercent = (amount, goal, isMet) => {
10
+ if (goal && goal > 0 && amount !== undefined) {
11
+ return Math.min(100, (amount / goal) * 100);
12
+ }
13
+ return isMet ? 100 : 0;
14
+ };
15
+ exports.calculatePercent = calculatePercent;
16
+ const formatList = (items) => items.length === 2 ? items.join(' and ') : `${items.slice(0, -1).join(', ')}, and ${items.at(-1)}`;
17
+ exports.formatList = formatList;
18
+ const calculateDynamicConditionPercent = (dynamicObj, cond) => {
19
+ if (!dynamicObj)
20
+ return 0;
21
+ if (cond.operator === 'is_truthy')
22
+ return dynamicObj[cond.key] ? 100 : 0;
23
+ if (cond.operator === 'is_falsy')
24
+ return !dynamicObj[cond.key] ? 100 : 0;
25
+ if (cond.operator === 'is_defined')
26
+ return dynamicObj[cond.key] != null ? 100 : 0;
27
+ const val = dynamicObj[cond.key];
28
+ if (typeof val !== 'number')
29
+ return 0;
30
+ const compareTo = typeof cond.compareTo === 'number' ? cond.compareTo : 0;
31
+ switch (cond.operator) {
32
+ case '>=':
33
+ case '>':
34
+ return compareTo > 0 ? Math.min(100, (val / compareTo) * 100) : val >= compareTo ? 100 : 0;
35
+ case '==':
36
+ return val === compareTo ? 100 : 0;
37
+ case '!=':
38
+ return val !== compareTo ? 100 : 0;
39
+ case '<=':
40
+ case '<':
41
+ return val <= compareTo ? 100 : 0;
42
+ default:
43
+ return 0;
44
+ }
45
+ };
46
+ exports.calculateDynamicConditionPercent = calculateDynamicConditionPercent;
47
+ const calculateDynamicGroupPercent = (dynamicObj, group) => {
48
+ if (!group?.conditions?.length)
49
+ return 100;
50
+ const percentages = group.conditions.map((c) => (0, exports.calculateDynamicConditionPercent)(dynamicObj, c));
51
+ if (!group.links?.length) {
52
+ return percentages.reduce((sum, p) => sum + p, 0) / percentages.length;
53
+ }
54
+ let result = percentages[0];
55
+ for (let i = 0; i < group.links.length; i++) {
56
+ const nextPct = percentages[i + 1];
57
+ switch (group.links[i]) {
58
+ case 'AND':
59
+ result = (result + nextPct) / 2;
60
+ break;
61
+ case 'OR':
62
+ result = Math.max(result, nextPct);
63
+ break;
64
+ case 'AND NOT':
65
+ result = (result + (100 - nextPct)) / 2;
66
+ break;
67
+ }
68
+ }
69
+ return result;
70
+ };
71
+ exports.calculateDynamicGroupPercent = calculateDynamicGroupPercent;
72
+ function evaluateDynamicCondition(dynamicObj, cond, claimMultiplier = 1) {
73
+ if (!dynamicObj)
74
+ return false;
75
+ const val = dynamicObj[cond.key];
76
+ if (cond.operator === 'is_truthy')
77
+ return !!val;
78
+ if (cond.operator === 'is_falsy')
79
+ return !val;
80
+ if (cond.operator === 'is_defined')
81
+ return val != null;
82
+ if (val == undefined)
83
+ return false;
84
+ const isNumber = typeof val === 'number';
85
+ const isBoolean = typeof val === 'boolean';
86
+ if (isBoolean) {
87
+ switch (cond.operator) {
88
+ case '==':
89
+ return val === Boolean(cond.compareTo);
90
+ case '!=':
91
+ return val !== Boolean(cond.compareTo);
92
+ default:
93
+ return false;
94
+ }
95
+ }
96
+ const compareTo = isNumber ? Number(cond.compareTo) : String(cond.compareTo);
97
+ if (isNumber && typeof compareTo === 'number') {
98
+ const skipMultiplier = cond.operator === '==' || cond.operator === '!=';
99
+ const scaledCompareTo = skipMultiplier ? compareTo : compareTo * claimMultiplier;
100
+ switch (cond.operator) {
101
+ case '==':
102
+ return val === scaledCompareTo;
103
+ case '!=':
104
+ return val !== scaledCompareTo;
105
+ case '>':
106
+ return val > scaledCompareTo;
107
+ case '>=':
108
+ return val >= scaledCompareTo;
109
+ case '<':
110
+ return val < scaledCompareTo;
111
+ case '<=':
112
+ return val <= scaledCompareTo;
113
+ }
114
+ }
115
+ else if (!isNumber && typeof compareTo === 'string') {
116
+ switch (cond.operator) {
117
+ case '==':
118
+ return val === compareTo;
119
+ case '!=':
120
+ return val !== compareTo;
121
+ case 'has':
122
+ return val.includes(compareTo);
123
+ case 'not_has':
124
+ return !val.includes(compareTo);
125
+ }
126
+ }
127
+ return false;
128
+ }
129
+ function meetsDynamicConditions(dynamicObj, dynamicGroup, claimMultiplier = 1) {
130
+ const { conditions, links } = dynamicGroup;
131
+ if (!conditions || conditions.length === 0)
132
+ return true;
133
+ if (!dynamicObj)
134
+ return false;
135
+ if (!links || links.length === 0) {
136
+ return conditions.every((cond) => evaluateDynamicCondition(dynamicObj, cond, claimMultiplier));
137
+ }
138
+ let result = evaluateDynamicCondition(dynamicObj, conditions[0], claimMultiplier);
139
+ for (let i = 0; i < links.length; i++) {
140
+ const nextCond = evaluateDynamicCondition(dynamicObj, conditions[i + 1], claimMultiplier);
141
+ const link = links[i];
142
+ if (link === 'AND') {
143
+ result = result && nextCond;
144
+ }
145
+ else if (link === 'OR') {
146
+ result = result || nextCond;
147
+ }
148
+ else if (link === 'AND NOT') {
149
+ result = result && !nextCond;
150
+ }
151
+ }
152
+ return result;
153
+ }
154
+ function getMaxClaimsForDynamicCondition(dynamicObj, cond) {
155
+ if (!dynamicObj)
156
+ return 0;
157
+ if (cond.operator === 'is_truthy')
158
+ return dynamicObj[cond.key] ? Infinity : 0;
159
+ if (cond.operator === 'is_falsy')
160
+ return !dynamicObj[cond.key] ? Infinity : 0;
161
+ if (cond.operator === 'is_defined')
162
+ return dynamicObj[cond.key] != null ? Infinity : 0;
163
+ const val = dynamicObj[cond.key];
164
+ if (val === undefined)
165
+ return 0;
166
+ if (typeof val === 'number') {
167
+ const base = Number(cond.compareTo);
168
+ if (isNaN(base)) {
169
+ return evaluateDynamicCondition(dynamicObj, cond, 1) ? Infinity : 0;
170
+ }
171
+ switch (cond.operator) {
172
+ case '>=':
173
+ if (base === 0)
174
+ return val >= 0 ? Infinity : 0;
175
+ if (base < 0)
176
+ return val >= base ? Infinity : 0;
177
+ return Math.max(0, Math.floor(val / base));
178
+ case '>':
179
+ if (base === 0)
180
+ return val > 0 ? Infinity : 0;
181
+ if (base < 0)
182
+ return val > base ? Infinity : 0;
183
+ if (val <= 0)
184
+ return 0;
185
+ return Math.max(0, Math.ceil(val / base) - 1);
186
+ case '==':
187
+ return val === base ? Infinity : 0;
188
+ case '!=':
189
+ return val !== base ? Infinity : 0;
190
+ case '<=':
191
+ if (base === 0)
192
+ return val <= 0 ? Infinity : 0;
193
+ if (base > 0)
194
+ return evaluateDynamicCondition(dynamicObj, cond, 1) ? Infinity : 0;
195
+ if (val >= 0)
196
+ return 0;
197
+ return Math.max(0, Math.floor(val / base));
198
+ case '<':
199
+ if (base === 0)
200
+ return val < 0 ? Infinity : 0;
201
+ if (base > 0)
202
+ return evaluateDynamicCondition(dynamicObj, cond, 1) ? Infinity : 0;
203
+ if (val >= 0)
204
+ return 0;
205
+ return Math.max(0, Math.ceil(val / base) - 1);
206
+ }
207
+ }
208
+ return evaluateDynamicCondition(dynamicObj, cond, 1) ? Infinity : 0;
209
+ }
210
+ function getMaxClaimsForDynamicGroup(dynamicObj, dynamicGroup, currentClaimCount = 0) {
211
+ const { conditions, links } = dynamicGroup;
212
+ if (!conditions || conditions.length === 0)
213
+ return Infinity;
214
+ if (!links || links.length === 0 || links.every((l) => l === 'AND')) {
215
+ let minClaims = Infinity;
216
+ for (const cond of conditions) {
217
+ const max = getMaxClaimsForDynamicCondition(dynamicObj, cond);
218
+ if (max === 0)
219
+ return 0;
220
+ minClaims = Math.min(minClaims, max);
221
+ }
222
+ return minClaims;
223
+ }
224
+ if (links.every((l) => l === 'OR')) {
225
+ let maxClaims = 0;
226
+ for (const cond of conditions) {
227
+ const max = getMaxClaimsForDynamicCondition(dynamicObj, cond);
228
+ if (max === Infinity)
229
+ return Infinity;
230
+ maxClaims = Math.max(maxClaims, max);
231
+ }
232
+ return maxClaims;
233
+ }
234
+ const maxIterations = 100;
235
+ for (let n = currentClaimCount + 1; n <= currentClaimCount + maxIterations; n++) {
236
+ if (!meetsDynamicConditions(dynamicObj, dynamicGroup, n)) {
237
+ return n - 1;
238
+ }
239
+ }
240
+ return currentClaimCount + maxIterations;
241
+ }
242
+ function aggregateTokenBalances(data) {
243
+ const aggregatedBalances = {};
244
+ for (const { balances } of data?.cryptoWallets || []) {
245
+ if (!balances)
246
+ continue;
247
+ for (const [key, balance] of Object.entries(balances)) {
248
+ if (!aggregatedBalances[key]) {
249
+ aggregatedBalances[key] = 0;
250
+ }
251
+ aggregatedBalances[key] += balance;
252
+ }
253
+ }
254
+ return aggregatedBalances;
255
+ }
256
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1,97 @@
1
+ import { ICompletionCondition, ISurfacingCondition, ICompletionTrackers, IClaimableTrackers, StackedOffer, StackedSnapshot, StackedBaseUserExtra, StackedOfferTrackers, IClaimableCondition, ConditionDetail } from '@stackedapp/types';
2
+ export { meetsDynamicConditions, getMaxClaimsForDynamicCondition, getMaxClaimsForDynamicGroup } from './helpers';
3
+ export declare const meetsBaseConditions: ({ conditions, playerSnap, addDetails, playerOffer, additionalData, }: {
4
+ conditions: ISurfacingCondition[] | ICompletionCondition[];
5
+ playerSnap: StackedSnapshot;
6
+ addDetails?: boolean;
7
+ playerOffer?: {
8
+ createdAt?: Date | number;
9
+ claimedCount?: number;
10
+ trackers?: Record<string, any>;
11
+ };
12
+ additionalData?: StackedBaseUserExtra;
13
+ }) => {
14
+ isValid: boolean;
15
+ isComplete: boolean;
16
+ percentCompleted: number;
17
+ conditionData?: undefined;
18
+ } | {
19
+ isValid: boolean;
20
+ isComplete: boolean;
21
+ percentCompleted: number;
22
+ conditionData: ConditionDetail[] | undefined;
23
+ };
24
+ export declare const hasCompletionConditions: (conditions: ICompletionCondition[] | undefined | null) => boolean;
25
+ export declare const meetsLinkedEntityOffersCondition: ({ linkedEntityOffers, matchingLinks, linkedPOfferMap, }: {
26
+ linkedEntityOffers: {
27
+ kind?: string;
28
+ offer_id?: string;
29
+ offerId?: string;
30
+ link?: boolean;
31
+ };
32
+ matchingLinks: Array<{
33
+ playerId: string;
34
+ kind?: string;
35
+ }>;
36
+ linkedPOfferMap: Map<string, StackedOffer>;
37
+ }) => {
38
+ isValid: boolean;
39
+ linkedPlayerOffer_ids?: string[];
40
+ };
41
+ export declare const offerMeetsCompletionConditions: (offer: StackedOffer, snapshot: StackedSnapshot, additionalData?: StackedBaseUserExtra) => {
42
+ isValid: boolean;
43
+ isComplete: boolean;
44
+ percentCompleted: number;
45
+ conditionData: ConditionDetail[];
46
+ availableClaimsNow: number;
47
+ } | {
48
+ isValid: boolean;
49
+ isComplete: boolean;
50
+ percentCompleted: number;
51
+ availableClaimsNow: number;
52
+ conditionData?: undefined;
53
+ };
54
+ export declare const meetsCompletionConditions: ({ completionConditions, completionTrackers, playerSnap, playerOffer, addDetails, maxClaimCount, additionalData, }: {
55
+ completionConditions: ICompletionCondition[];
56
+ completionTrackers?: ICompletionTrackers;
57
+ playerSnap: StackedSnapshot;
58
+ playerOffer: {
59
+ createdAt?: Date | number;
60
+ trackers?: StackedOfferTrackers;
61
+ };
62
+ addDetails?: boolean;
63
+ maxClaimCount?: number;
64
+ additionalData?: StackedBaseUserExtra;
65
+ }) => {
66
+ isValid: boolean;
67
+ isComplete: boolean;
68
+ percentCompleted: number;
69
+ conditionData: ConditionDetail[];
70
+ availableClaimsNow: number;
71
+ } | {
72
+ isValid: boolean;
73
+ isComplete: boolean;
74
+ percentCompleted: number;
75
+ availableClaimsNow: number;
76
+ conditionData?: undefined;
77
+ };
78
+ export declare const meetsCompletionConditionsBeforeExpiry: ({ completionConditions, completionTrackers, playerSnap, playerOffer, maxClaimCount, }: {
79
+ completionConditions: ICompletionCondition[];
80
+ completionTrackers?: ICompletionTrackers;
81
+ playerSnap: StackedSnapshot;
82
+ playerOffer: {
83
+ createdAt?: Date | number;
84
+ claimedCount?: number;
85
+ trackers?: Record<string, any>;
86
+ expiresAt?: Date | number | string;
87
+ };
88
+ maxClaimCount?: number;
89
+ }) => boolean;
90
+ export declare function meetsClaimableConditions({ claimableConditions, playerOfferTrackers, claimableTrackers, }: {
91
+ claimableConditions?: IClaimableCondition[];
92
+ playerOfferTrackers?: StackedOfferTrackers;
93
+ claimableTrackers?: IClaimableTrackers;
94
+ }): {
95
+ isValid: boolean;
96
+ };
97
+ //# sourceMappingURL=index.d.ts.map