@pixels-online/pixels-client-js-sdk 1.17.0 → 1.19.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/dist/index.esm.js CHANGED
@@ -628,25 +628,33 @@ class SSEConnection {
628
628
  }
629
629
 
630
630
  class OfferStore {
631
- constructor(config) {
631
+ constructor(config, client) {
632
+ this.client = client;
632
633
  this.offers = new Map();
633
- this.player = null;
634
+ this.players = new Map();
634
635
  this.logger = createLogger(config, 'OfferStore');
635
636
  }
636
- getPlayer() {
637
- return this.player || null;
637
+ getPlayer(targetId = this.client.getSelfId()) {
638
+ if (!targetId)
639
+ return null;
640
+ return this.players.get(targetId) || null;
638
641
  }
639
642
  setPlayer(player) {
640
- this.player = player;
643
+ this.players.set(player.snapshot.playerId, player);
641
644
  this.logger.log('Updated player:', player);
642
645
  }
643
646
  /**
644
647
  * Set all offers (replaces existing)
645
648
  */
646
- setOffers(offers) {
647
- this.offers.clear();
649
+ setOffers(offers, target) {
650
+ const targetPlayer = target || this.getPlayer();
651
+ if (!targetPlayer) {
652
+ this.logger.warn('No target player to set offers for');
653
+ return;
654
+ }
655
+ this.offers.set(targetPlayer.snapshot.playerId, new Map());
648
656
  offers.forEach((offer) => {
649
- this.offers.set(offer.instanceId, offer);
657
+ this.offers.get(targetPlayer.snapshot.playerId).set(offer.instanceId, offer);
650
658
  });
651
659
  this.logger.log(`Set ${offers.length} offers`);
652
660
  }
@@ -654,16 +662,23 @@ class OfferStore {
654
662
  * Add or update a single offer
655
663
  */
656
664
  upsertOffer(offer) {
657
- const previousOffer = this.offers.get(offer.instanceId);
658
- this.offers.set(offer.instanceId, offer);
665
+ let playerOffers = this.offers.get(offer.playerId);
666
+ if (!playerOffers) {
667
+ playerOffers = new Map();
668
+ this.offers.set(offer.playerId, playerOffers);
669
+ }
670
+ const previousOffer = playerOffers.get(offer.instanceId);
671
+ playerOffers.set(offer.instanceId, offer);
659
672
  this.logger.log(`${previousOffer ? 'Updated' : 'Added'} offer:`, offer.instanceId);
660
673
  return previousOffer;
661
674
  }
662
675
  /**
663
676
  * Remove an offer
664
677
  */
665
- removeOffer(offerId) {
666
- const removed = this.offers.delete(offerId);
678
+ removeOffer(offerId, targetId = this.client.getSelfId()) {
679
+ if (!targetId)
680
+ return false;
681
+ const removed = this.offers.get(targetId)?.delete(offerId) || false;
667
682
  if (removed) {
668
683
  this.logger.log(`Removed offer:`, offerId);
669
684
  }
@@ -672,26 +687,30 @@ class OfferStore {
672
687
  /**
673
688
  * Get a single offer
674
689
  */
675
- getOffer(offerId) {
676
- return this.offers.get(offerId);
690
+ getOffer(offerId, targetId = this.client.getSelfId()) {
691
+ if (!targetId)
692
+ return undefined;
693
+ return this.offers.get(targetId)?.get(offerId);
677
694
  }
678
695
  /**
679
696
  * Get all offers
680
697
  */
681
- getAllOffers() {
682
- return Array.from(this.offers.values());
698
+ getAllOffers(targetId = this.client.getSelfId()) {
699
+ if (!targetId)
700
+ return [];
701
+ return Array.from(this.offers.get(targetId)?.values() || []);
683
702
  }
684
703
  /**
685
704
  * Get offers filtered by status
686
705
  */
687
- getOffersByStatus(status) {
688
- return this.getAllOffers().filter((offer) => offer.status === status);
706
+ getOffersByStatus(status, targetId = this.client.getSelfId()) {
707
+ return this.getAllOffers(targetId).filter((offer) => offer.status === status);
689
708
  }
690
709
  /**
691
710
  * Get active offers (not expired, not claimed)
692
711
  */
693
- getActiveOffers() {
694
- return this.getAllOffers().filter((offer) => {
712
+ getActiveOffers(targetId = this.client.getSelfId()) {
713
+ return this.getAllOffers(targetId).filter((offer) => {
695
714
  if (!offer.status)
696
715
  return false; // Must have a status
697
716
  return (offer.status === 'surfaced' ||
@@ -702,14 +721,14 @@ class OfferStore {
702
721
  /**
703
722
  * Get claimable offers
704
723
  */
705
- getClaimableOffers() {
706
- return this.getOffersByStatus('claimable');
724
+ getClaimableOffers(targetId = this.client.getSelfId()) {
725
+ return this.getOffersByStatus('claimable', targetId);
707
726
  }
708
727
  /**
709
728
  * Check if an offer has expired
710
729
  */
711
- isOfferExpired(offerId) {
712
- const offer = this.getOffer(offerId);
730
+ isOfferExpired(offerId, targetId = this.client.getSelfId()) {
731
+ const offer = this.getOffer(offerId, targetId);
713
732
  if (!offer)
714
733
  return true;
715
734
  // Check status
@@ -722,17 +741,29 @@ class OfferStore {
722
741
  return false;
723
742
  }
724
743
  /**
725
- * Clear all offers
744
+ * Clear all offers for a specific player
726
745
  */
727
- clear() {
746
+ clear(targetId = this.client.getSelfId()) {
747
+ if (!targetId)
748
+ return;
749
+ this.offers.set(targetId, new Map());
750
+ this.logger.log('Cleared all offers for player:', targetId);
751
+ }
752
+ /**
753
+ * Clear all offers for all players
754
+ */
755
+ clearAll() {
728
756
  this.offers.clear();
729
- this.logger.log('Cleared all offers');
757
+ this.logger.log('Cleared all offers for all players');
730
758
  }
731
759
  /**
732
- * Get offer count
760
+ * Get offer count (for self player)
733
761
  */
734
762
  get size() {
735
- return this.offers.size;
763
+ const selfId = this.client.getSelfId();
764
+ if (!selfId)
765
+ return 0;
766
+ return this.offers.get(selfId)?.size || 0;
736
767
  }
737
768
  }
738
769
 
@@ -835,7 +866,9 @@ class AssetHelper {
835
866
  return null;
836
867
  }
837
868
  setCurrencyAssetContents(currencies) {
838
- this.currencies = currencies;
869
+ Object.keys(currencies).forEach((key) => {
870
+ this.currencies[key] = currencies[key];
871
+ });
839
872
  }
840
873
  resolveReward(reward) {
841
874
  if (reward.kind === 'loyalty_currency' && reward.rewardId) {
@@ -891,6 +924,7 @@ class OfferwallClient {
891
924
  constructor(config) {
892
925
  this.isInitializing = false;
893
926
  this.pendingStackedToken = null;
927
+ this.selfId = null;
894
928
  this.config = {
895
929
  autoConnect: config.autoConnect ?? false,
896
930
  reconnect: config.reconnect ?? true,
@@ -903,7 +937,7 @@ class OfferwallClient {
903
937
  this.hooks = this.config.hooks || {};
904
938
  this.logger = createLogger(this.config, 'OfferwallClient');
905
939
  this.eventEmitter = new EventEmitter(this.config);
906
- this.offerStore = new OfferStore(this.config);
940
+ this.offerStore = new OfferStore(this.config, this);
907
941
  this.tokenManager = new TokenManager(this.config);
908
942
  this.assetHelper = new AssetHelper(this.config);
909
943
  this.sseConnection = new SSEConnection(this.config, this.eventEmitter, this.tokenManager);
@@ -944,6 +978,9 @@ class OfferwallClient {
944
978
  get assets() {
945
979
  return this.assetHelper;
946
980
  }
981
+ getSelfId() {
982
+ return this.selfId;
983
+ }
947
984
  /**
948
985
  * Initialize the offerwall client and connect
949
986
  */
@@ -1006,8 +1043,9 @@ class OfferwallClient {
1006
1043
  if (this.sseConnection) {
1007
1044
  this.sseConnection.disconnect();
1008
1045
  }
1009
- this.offerStore.clear();
1046
+ this.offerStore.clearAll();
1010
1047
  this.tokenManager.clearToken();
1048
+ this.selfId = null;
1011
1049
  if (this.hooks.afterDisconnect) {
1012
1050
  await this.hooks.afterDisconnect();
1013
1051
  }
@@ -1015,8 +1053,8 @@ class OfferwallClient {
1015
1053
  /**
1016
1054
  * Claim rewards for an offer
1017
1055
  */
1018
- async claimReward(instanceId) {
1019
- const offer = this.offerStore.getOffer(instanceId);
1056
+ async claimReward(instanceId, targetId = this.getSelfId()) {
1057
+ const offer = this.offerStore.getOffer(instanceId, targetId);
1020
1058
  if (!offer) {
1021
1059
  throw new Error(`Offer ${instanceId} not found`);
1022
1060
  }
@@ -1031,7 +1069,7 @@ class OfferwallClient {
1031
1069
  }
1032
1070
  }
1033
1071
  try {
1034
- const response = await this.claimOfferAPI(instanceId);
1072
+ const response = await this.claimOfferAPI(instanceId, targetId);
1035
1073
  const updatedOffer = { ...offer, status: 'claimed' };
1036
1074
  this.offerStore.upsertOffer(updatedOffer);
1037
1075
  this.eventEmitter.emit(OfferEvent.OFFER_CLAIMED, {
@@ -1087,7 +1125,7 @@ class OfferwallClient {
1087
1125
  });
1088
1126
  */
1089
1127
  this.eventEmitter.on(OfferEvent.OFFER_SURFACED, ({ offer }) => {
1090
- this.offerStore.upsertOffer(offer);
1128
+ this.offerStore.upsertOffer(offer); // should always be selfId
1091
1129
  this.logger.log(`Surfaced offer: ${offer.instanceId}`);
1092
1130
  });
1093
1131
  }
@@ -1117,23 +1155,27 @@ class OfferwallClient {
1117
1155
  }
1118
1156
  return response.json();
1119
1157
  }
1120
- async claimOfferAPI(instanceId) {
1158
+ async claimOfferAPI(instanceId, targetId = null) {
1121
1159
  return this.postWithAuth('/v1/client/reward/claim', {
1122
1160
  instanceId,
1123
1161
  kind: 'offer',
1162
+ targetId: targetId || undefined,
1124
1163
  });
1125
1164
  }
1126
- getPlayer() {
1127
- return this.offerStore.getPlayer();
1165
+ getPlayer(targetId = this.getSelfId()) {
1166
+ return this.offerStore.getPlayer(targetId);
1128
1167
  }
1129
- getOffers() {
1130
- return this.offerStore.getAllOffers();
1168
+ getOffers(targetId = this.getSelfId()) {
1169
+ return this.offerStore.getAllOffers(targetId);
1131
1170
  }
1132
- async refreshOffersAndPlayer() {
1171
+ async refreshOffersAndPlayer(targetId = null) {
1133
1172
  try {
1134
- const { offers, player } = await this.getOffersAndPlayer();
1135
- this.offerStore.setOffers(offers);
1173
+ const { offers, player } = await this.getOffersAndPlayer(targetId);
1174
+ if (targetId == null) {
1175
+ this.selfId = player.snapshot.playerId;
1176
+ }
1136
1177
  this.offerStore.setPlayer(player);
1178
+ this.offerStore.setOffers(offers, player);
1137
1179
  this.eventEmitter.emit(OfferEvent.REFRESH, { offers, player: player });
1138
1180
  this.logger.log('Refreshed offers and player snapshot');
1139
1181
  }
@@ -1142,9 +1184,10 @@ class OfferwallClient {
1142
1184
  throw error;
1143
1185
  }
1144
1186
  }
1145
- async getOffersAndPlayer() {
1187
+ async getOffersAndPlayer(targetId = null) {
1146
1188
  const data = await this.postWithAuth('/v1/client/player/campaigns', {
1147
1189
  viewingCampaigns: true,
1190
+ targetId: targetId || undefined,
1148
1191
  });
1149
1192
  if (!data.offers || !Array.isArray(data.offers)) {
1150
1193
  throw new Error('No offers returned from offers endpoint');
@@ -1919,6 +1962,8 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
1919
1962
  const claimMultiplier = shouldScale ? (playerOffer.claimedCount || 0) + 1 : 1;
1920
1963
  const conditionData = [];
1921
1964
  let isValid = true;
1965
+ let maxTotalClaimsFromScaling = Infinity;
1966
+ const updateMax = (limit) => (maxTotalClaimsFromScaling = Math.min(maxTotalClaimsFromScaling, limit));
1922
1967
  if (completionConditions?.context?.id) {
1923
1968
  const hasTrackedContext = completionTrackers?.context &&
1924
1969
  completionConditions.context.id === completionTrackers.context;
@@ -1935,17 +1980,22 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
1935
1980
  }
1936
1981
  else {
1937
1982
  if (isDisqualify)
1938
- return { isValid: false };
1983
+ return { isValid: false, availableClaimsNow: 0 };
1939
1984
  }
1940
1985
  }
1941
1986
  if (conditions?.buyItem) {
1942
- const scaledAmount = (conditions.buyItem.amount || 1) * claimMultiplier;
1943
- const isDisqualify = (completionTrackers?.buyItem || 0) < scaledAmount;
1987
+ const baseAmount = conditions.buyItem.amount || 1;
1988
+ const scaledAmount = baseAmount * claimMultiplier;
1989
+ const trackerValue = completionTrackers?.buyItem || 0;
1990
+ const isDisqualify = trackerValue < scaledAmount;
1991
+ if (shouldScale && baseAmount > 0) {
1992
+ updateMax(Math.floor(trackerValue / baseAmount));
1993
+ }
1944
1994
  if (addDetails) {
1945
1995
  conditionData.push({
1946
1996
  isMet: !isDisqualify,
1947
1997
  kind: 'buyItem',
1948
- trackerAmount: completionTrackers?.buyItem || 0,
1998
+ trackerAmount: trackerValue,
1949
1999
  text: `Buy ${scaledAmount} ${conditions.buyItem.name}`,
1950
2000
  });
1951
2001
  if (isDisqualify)
@@ -1953,17 +2003,22 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
1953
2003
  }
1954
2004
  else {
1955
2005
  if (isDisqualify)
1956
- return { isValid: false };
2006
+ return { isValid: false, availableClaimsNow: 0 };
1957
2007
  }
1958
2008
  }
1959
2009
  if (conditions?.spendCurrency) {
1960
- const scaledAmount = (conditions.spendCurrency.amount || 1) * claimMultiplier;
1961
- const isDisqualify = (completionTrackers?.spendCurrency || 0) < scaledAmount;
2010
+ const baseAmount = conditions.spendCurrency.amount || 1;
2011
+ const scaledAmount = baseAmount * claimMultiplier;
2012
+ const trackerValue = completionTrackers?.spendCurrency || 0;
2013
+ const isDisqualify = trackerValue < scaledAmount;
2014
+ if (shouldScale && baseAmount > 0) {
2015
+ updateMax(Math.floor(trackerValue / baseAmount));
2016
+ }
1962
2017
  if (addDetails) {
1963
2018
  conditionData.push({
1964
2019
  isMet: !isDisqualify,
1965
2020
  kind: 'spendCurrency',
1966
- trackerAmount: completionTrackers?.spendCurrency || 0,
2021
+ trackerAmount: trackerValue,
1967
2022
  text: `Spend ${scaledAmount} ${conditions.spendCurrency.name}`,
1968
2023
  });
1969
2024
  if (isDisqualify)
@@ -1971,17 +2026,22 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
1971
2026
  }
1972
2027
  else {
1973
2028
  if (isDisqualify)
1974
- return { isValid: false };
2029
+ return { isValid: false, availableClaimsNow: 0 };
1975
2030
  }
1976
2031
  }
1977
2032
  if (conditions?.depositCurrency) {
1978
- const scaledAmount = (conditions.depositCurrency.amount || 1) * claimMultiplier;
1979
- const isDisqualify = (completionTrackers?.depositCurrency || 0) < scaledAmount;
2033
+ const baseAmount = conditions.depositCurrency.amount || 1;
2034
+ const scaledAmount = baseAmount * claimMultiplier;
2035
+ const trackerValue = completionTrackers?.depositCurrency || 0;
2036
+ const isDisqualify = trackerValue < scaledAmount;
2037
+ if (shouldScale && baseAmount > 0) {
2038
+ updateMax(Math.floor(trackerValue / baseAmount));
2039
+ }
1980
2040
  if (addDetails) {
1981
2041
  conditionData.push({
1982
2042
  isMet: !isDisqualify,
1983
2043
  kind: 'depositCurrency',
1984
- trackerAmount: completionTrackers?.depositCurrency || 0,
2044
+ trackerAmount: trackerValue,
1985
2045
  text: `Deposit ${scaledAmount} ${conditions.depositCurrency.name}`,
1986
2046
  });
1987
2047
  if (isDisqualify)
@@ -1989,7 +2049,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
1989
2049
  }
1990
2050
  else {
1991
2051
  if (isDisqualify)
1992
- return { isValid: false };
2052
+ return { isValid: false, availableClaimsNow: 0 };
1993
2053
  }
1994
2054
  }
1995
2055
  if (conditions?.login) {
@@ -2006,7 +2066,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
2006
2066
  }
2007
2067
  else {
2008
2068
  if (!isMet)
2009
- return { isValid: false };
2069
+ return { isValid: false, availableClaimsNow: 0 };
2010
2070
  }
2011
2071
  }
2012
2072
  if (conditions?.loginStreak) {
@@ -2026,7 +2086,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
2026
2086
  }
2027
2087
  else {
2028
2088
  if (isDisqualify)
2029
- return { isValid: false };
2089
+ return { isValid: false, availableClaimsNow: 0 };
2030
2090
  }
2031
2091
  }
2032
2092
  if (conditions?.social) {
@@ -2048,6 +2108,17 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
2048
2108
  if (likes < minLikes || views < minViews || comments < minComments) {
2049
2109
  isDisqualify = true;
2050
2110
  }
2111
+ if (shouldScale && mode === 'accumulate' && hasContent) {
2112
+ const baseLikes = cSocial?.minLikes || 0;
2113
+ const baseViews = cSocial?.minViews || 0;
2114
+ const baseComments = cSocial?.minComments || 0;
2115
+ if (baseLikes > 0)
2116
+ updateMax(Math.floor(likes / baseLikes));
2117
+ if (baseViews > 0)
2118
+ updateMax(Math.floor(views / baseViews));
2119
+ if (baseComments > 0)
2120
+ updateMax(Math.floor(comments / baseComments));
2121
+ }
2051
2122
  if (addDetails) {
2052
2123
  const platformMap = {
2053
2124
  tiktok: 'TikTok',
@@ -2119,14 +2190,18 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
2119
2190
  }
2120
2191
  else {
2121
2192
  if (isDisqualify)
2122
- return { isValid: false };
2193
+ return { isValid: false, availableClaimsNow: 0 };
2123
2194
  }
2124
2195
  }
2125
2196
  // Linked completions - wait for N linked entities to complete
2126
2197
  if (conditions?.linkedCompletions?.min) {
2198
+ const baseMin = conditions.linkedCompletions.min;
2127
2199
  const currentCount = completionTrackers?.linkedCompletions || 0;
2128
- const scaledMin = conditions.linkedCompletions.min * claimMultiplier;
2200
+ const scaledMin = baseMin * claimMultiplier;
2129
2201
  const isDisqualify = currentCount < scaledMin;
2202
+ if (shouldScale && baseMin > 0) {
2203
+ updateMax(Math.floor(currentCount / baseMin));
2204
+ }
2130
2205
  if (addDetails) {
2131
2206
  conditionData.push({
2132
2207
  isMet: !isDisqualify,
@@ -2139,7 +2214,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
2139
2214
  }
2140
2215
  else {
2141
2216
  if (isDisqualify)
2142
- return { isValid: false };
2217
+ return { isValid: false, availableClaimsNow: 0 };
2143
2218
  }
2144
2219
  }
2145
2220
  if (conditions?.dynamicTracker?.conditions?.length) {
@@ -2149,6 +2224,13 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
2149
2224
  ...conditions.dynamicTracker,
2150
2225
  conditions: resolvedConditions,
2151
2226
  }, claimMultiplier);
2227
+ if (shouldScale) {
2228
+ const dynamicMax = getMaxClaimsForDynamicGroup(completionTrackers?.dynamicTracker || {}, {
2229
+ ...conditions.dynamicTracker,
2230
+ conditions: resolvedConditions,
2231
+ }, playerOffer.claimedCount || 0);
2232
+ updateMax(dynamicMax);
2233
+ }
2152
2234
  if (addDetails) {
2153
2235
  conditionData.push({
2154
2236
  isMet: dynamicResult,
@@ -2160,7 +2242,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
2160
2242
  }
2161
2243
  else {
2162
2244
  if (!dynamicResult)
2163
- return { isValid: false };
2245
+ return { isValid: false, availableClaimsNow: 0 };
2164
2246
  }
2165
2247
  }
2166
2248
  const r = meetsBaseConditions({
@@ -2171,9 +2253,18 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
2171
2253
  });
2172
2254
  isValid = isValid && r.isValid;
2173
2255
  conditionData.push(...(r.conditionData || []));
2174
- return { isValid, conditionData };
2175
- }
2176
- return { isValid: true, conditionData: [] };
2256
+ if (maxClaimCount && maxClaimCount > 0) {
2257
+ updateMax(maxClaimCount);
2258
+ }
2259
+ const claimedCount = playerOffer.claimedCount || 0;
2260
+ let availableClaimsNow = !isValid
2261
+ ? 0
2262
+ : maxTotalClaimsFromScaling === Infinity
2263
+ ? -1
2264
+ : Math.max(0, maxTotalClaimsFromScaling - claimedCount);
2265
+ return { isValid, conditionData, availableClaimsNow };
2266
+ }
2267
+ return { isValid: true, conditionData: [], availableClaimsNow: -1 };
2177
2268
  };
2178
2269
  /**
2179
2270
  * Checks if completion conditions were met before a specific expiry time.
@@ -2338,26 +2429,30 @@ function evaluateDynamicCondition(dynamicObj, cond, claimMultiplier = 1) {
2338
2429
  }
2339
2430
  }
2340
2431
  const compareTo = isNumber ? Number(cond.compareTo) : String(cond.compareTo);
2341
- switch (cond.operator) {
2342
- case '==':
2343
- return val === compareTo;
2344
- case '!=':
2345
- return val !== compareTo;
2346
- }
2347
2432
  if (isNumber && typeof compareTo === 'number') {
2433
+ const skipMultiplier = cond.operator === '==' || cond.operator === '!=';
2434
+ const scaledCompareTo = skipMultiplier ? compareTo : compareTo * claimMultiplier;
2348
2435
  switch (cond.operator) {
2436
+ case '==':
2437
+ return val === scaledCompareTo;
2438
+ case '!=':
2439
+ return val !== scaledCompareTo;
2349
2440
  case '>':
2350
- return val > compareTo * claimMultiplier;
2441
+ return val > scaledCompareTo;
2351
2442
  case '>=':
2352
- return val >= compareTo * claimMultiplier;
2443
+ return val >= scaledCompareTo;
2353
2444
  case '<':
2354
- return val < compareTo * claimMultiplier;
2445
+ return val < scaledCompareTo;
2355
2446
  case '<=':
2356
- return val <= compareTo * claimMultiplier;
2447
+ return val <= scaledCompareTo;
2357
2448
  }
2358
2449
  }
2359
2450
  else if (!isNumber && typeof compareTo === 'string') {
2360
2451
  switch (cond.operator) {
2452
+ case '==':
2453
+ return val === compareTo;
2454
+ case '!=':
2455
+ return val !== compareTo;
2361
2456
  case 'has':
2362
2457
  return val.includes(compareTo);
2363
2458
  case 'not_has':
@@ -2366,6 +2461,98 @@ function evaluateDynamicCondition(dynamicObj, cond, claimMultiplier = 1) {
2366
2461
  }
2367
2462
  return false;
2368
2463
  }
2464
+ /**
2465
+ * Calculates the maximum number of claims supported by a single dynamic condition.
2466
+ */
2467
+ function getMaxClaimsForDynamicCondition(dynamicObj, cond) {
2468
+ if (!dynamicObj)
2469
+ return 0;
2470
+ const val = dynamicObj[cond.key];
2471
+ if (val === undefined)
2472
+ return 0;
2473
+ if (typeof val === 'number') {
2474
+ const base = Number(cond.compareTo);
2475
+ if (isNaN(base)) {
2476
+ return evaluateDynamicCondition(dynamicObj, cond, 1) ? Infinity : 0;
2477
+ }
2478
+ switch (cond.operator) {
2479
+ case '>=':
2480
+ if (base === 0)
2481
+ return val >= 0 ? Infinity : 0;
2482
+ if (base < 0)
2483
+ return val >= base ? Infinity : 0;
2484
+ return Math.max(0, Math.floor(val / base));
2485
+ case '>':
2486
+ if (base === 0)
2487
+ return val > 0 ? Infinity : 0;
2488
+ if (base < 0)
2489
+ return val > base ? Infinity : 0;
2490
+ if (val <= 0)
2491
+ return 0;
2492
+ return Math.max(0, Math.ceil(val / base) - 1);
2493
+ case '==':
2494
+ return val === base ? Infinity : 0;
2495
+ case '!=':
2496
+ return val !== base ? Infinity : 0;
2497
+ case '<=':
2498
+ if (base === 0)
2499
+ return val <= 0 ? Infinity : 0;
2500
+ if (base > 0)
2501
+ return evaluateDynamicCondition(dynamicObj, cond, 1) ? Infinity : 0;
2502
+ if (val >= 0)
2503
+ return 0;
2504
+ return Math.max(0, Math.floor(val / base));
2505
+ case '<':
2506
+ if (base === 0)
2507
+ return val < 0 ? Infinity : 0;
2508
+ if (base > 0)
2509
+ return evaluateDynamicCondition(dynamicObj, cond, 1) ? Infinity : 0;
2510
+ if (val >= 0)
2511
+ return 0;
2512
+ return Math.max(0, Math.ceil(val / base) - 1);
2513
+ }
2514
+ }
2515
+ // we don't scale the rest, they are always true or always false
2516
+ return evaluateDynamicCondition(dynamicObj, cond, 1) ? Infinity : 0;
2517
+ }
2518
+ /**
2519
+ * Calculates the maximum number of claims supported by a group of dynamic conditions.
2520
+ */
2521
+ function getMaxClaimsForDynamicGroup(dynamicObj, dynamicGroup, currentClaimCount = 0) {
2522
+ const { conditions, links } = dynamicGroup;
2523
+ if (!conditions || conditions.length === 0)
2524
+ return Infinity;
2525
+ // AND only
2526
+ if (!links || links.length === 0 || links.every((l) => l === 'AND')) {
2527
+ let minClaims = Infinity;
2528
+ for (const cond of conditions) {
2529
+ const max = getMaxClaimsForDynamicCondition(dynamicObj, cond);
2530
+ if (max === 0)
2531
+ return 0;
2532
+ minClaims = Math.min(minClaims, max);
2533
+ }
2534
+ return minClaims;
2535
+ }
2536
+ // OR only
2537
+ if (links.every((l) => l === 'OR')) {
2538
+ let maxClaims = 0;
2539
+ for (const cond of conditions) {
2540
+ const max = getMaxClaimsForDynamicCondition(dynamicObj, cond);
2541
+ if (max === Infinity)
2542
+ return Infinity;
2543
+ maxClaims = Math.max(maxClaims, max);
2544
+ }
2545
+ return maxClaims;
2546
+ }
2547
+ // mixed:
2548
+ const maxIterations = 100;
2549
+ for (let n = currentClaimCount + 1; n <= currentClaimCount + maxIterations; n++) {
2550
+ if (!meetsDynamicConditions(dynamicObj, dynamicGroup, n)) {
2551
+ return n - 1;
2552
+ }
2553
+ }
2554
+ return currentClaimCount + maxIterations;
2555
+ }
2369
2556
  /**
2370
2557
  * Evaluates a group of dynamic conditions with logical links (AND, OR, AND NOT).
2371
2558
  * @param dynamicObj - The player's dynamic object with any key and string or number value.
@@ -2468,5 +2655,5 @@ const rewardSchema = {
2468
2655
  image: String,
2469
2656
  };
2470
2657
 
2471
- export { AssetHelper, ConnectionState, EventEmitter, OfferEvent, OfferStore, OfferwallClient, PlayerOfferStatuses, SSEConnection, hasConditions, meetsBaseConditions, meetsClaimableConditions, meetsCompletionConditions, meetsCompletionConditionsBeforeExpiry, meetsDynamicConditions, meetsSurfacingConditions, offerListenerEvents, offerMeetsCompletionConditions, rewardKinds, rewardSchema };
2658
+ export { AssetHelper, ConnectionState, EventEmitter, OfferEvent, OfferStore, OfferwallClient, PlayerOfferStatuses, SSEConnection, getMaxClaimsForDynamicCondition, getMaxClaimsForDynamicGroup, hasConditions, meetsBaseConditions, meetsClaimableConditions, meetsCompletionConditions, meetsCompletionConditionsBeforeExpiry, meetsDynamicConditions, meetsSurfacingConditions, offerListenerEvents, offerMeetsCompletionConditions, rewardKinds, rewardSchema };
2472
2659
  //# sourceMappingURL=index.esm.js.map