@pixels-online/pixels-client-js-sdk 1.17.0 → 1.18.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.
@@ -1925,6 +1925,8 @@
1925
1925
  const claimMultiplier = shouldScale ? (playerOffer.claimedCount || 0) + 1 : 1;
1926
1926
  const conditionData = [];
1927
1927
  let isValid = true;
1928
+ let maxTotalClaimsFromScaling = Infinity;
1929
+ const updateMax = (limit) => (maxTotalClaimsFromScaling = Math.min(maxTotalClaimsFromScaling, limit));
1928
1930
  if (completionConditions?.context?.id) {
1929
1931
  const hasTrackedContext = completionTrackers?.context &&
1930
1932
  completionConditions.context.id === completionTrackers.context;
@@ -1941,17 +1943,22 @@
1941
1943
  }
1942
1944
  else {
1943
1945
  if (isDisqualify)
1944
- return { isValid: false };
1946
+ return { isValid: false, availableClaimsNow: 0 };
1945
1947
  }
1946
1948
  }
1947
1949
  if (conditions?.buyItem) {
1948
- const scaledAmount = (conditions.buyItem.amount || 1) * claimMultiplier;
1949
- const isDisqualify = (completionTrackers?.buyItem || 0) < scaledAmount;
1950
+ const baseAmount = conditions.buyItem.amount || 1;
1951
+ const scaledAmount = baseAmount * claimMultiplier;
1952
+ const trackerValue = completionTrackers?.buyItem || 0;
1953
+ const isDisqualify = trackerValue < scaledAmount;
1954
+ if (shouldScale && baseAmount > 0) {
1955
+ updateMax(Math.floor(trackerValue / baseAmount));
1956
+ }
1950
1957
  if (addDetails) {
1951
1958
  conditionData.push({
1952
1959
  isMet: !isDisqualify,
1953
1960
  kind: 'buyItem',
1954
- trackerAmount: completionTrackers?.buyItem || 0,
1961
+ trackerAmount: trackerValue,
1955
1962
  text: `Buy ${scaledAmount} ${conditions.buyItem.name}`,
1956
1963
  });
1957
1964
  if (isDisqualify)
@@ -1959,17 +1966,22 @@
1959
1966
  }
1960
1967
  else {
1961
1968
  if (isDisqualify)
1962
- return { isValid: false };
1969
+ return { isValid: false, availableClaimsNow: 0 };
1963
1970
  }
1964
1971
  }
1965
1972
  if (conditions?.spendCurrency) {
1966
- const scaledAmount = (conditions.spendCurrency.amount || 1) * claimMultiplier;
1967
- const isDisqualify = (completionTrackers?.spendCurrency || 0) < scaledAmount;
1973
+ const baseAmount = conditions.spendCurrency.amount || 1;
1974
+ const scaledAmount = baseAmount * claimMultiplier;
1975
+ const trackerValue = completionTrackers?.spendCurrency || 0;
1976
+ const isDisqualify = trackerValue < scaledAmount;
1977
+ if (shouldScale && baseAmount > 0) {
1978
+ updateMax(Math.floor(trackerValue / baseAmount));
1979
+ }
1968
1980
  if (addDetails) {
1969
1981
  conditionData.push({
1970
1982
  isMet: !isDisqualify,
1971
1983
  kind: 'spendCurrency',
1972
- trackerAmount: completionTrackers?.spendCurrency || 0,
1984
+ trackerAmount: trackerValue,
1973
1985
  text: `Spend ${scaledAmount} ${conditions.spendCurrency.name}`,
1974
1986
  });
1975
1987
  if (isDisqualify)
@@ -1977,17 +1989,22 @@
1977
1989
  }
1978
1990
  else {
1979
1991
  if (isDisqualify)
1980
- return { isValid: false };
1992
+ return { isValid: false, availableClaimsNow: 0 };
1981
1993
  }
1982
1994
  }
1983
1995
  if (conditions?.depositCurrency) {
1984
- const scaledAmount = (conditions.depositCurrency.amount || 1) * claimMultiplier;
1985
- const isDisqualify = (completionTrackers?.depositCurrency || 0) < scaledAmount;
1996
+ const baseAmount = conditions.depositCurrency.amount || 1;
1997
+ const scaledAmount = baseAmount * claimMultiplier;
1998
+ const trackerValue = completionTrackers?.depositCurrency || 0;
1999
+ const isDisqualify = trackerValue < scaledAmount;
2000
+ if (shouldScale && baseAmount > 0) {
2001
+ updateMax(Math.floor(trackerValue / baseAmount));
2002
+ }
1986
2003
  if (addDetails) {
1987
2004
  conditionData.push({
1988
2005
  isMet: !isDisqualify,
1989
2006
  kind: 'depositCurrency',
1990
- trackerAmount: completionTrackers?.depositCurrency || 0,
2007
+ trackerAmount: trackerValue,
1991
2008
  text: `Deposit ${scaledAmount} ${conditions.depositCurrency.name}`,
1992
2009
  });
1993
2010
  if (isDisqualify)
@@ -1995,7 +2012,7 @@
1995
2012
  }
1996
2013
  else {
1997
2014
  if (isDisqualify)
1998
- return { isValid: false };
2015
+ return { isValid: false, availableClaimsNow: 0 };
1999
2016
  }
2000
2017
  }
2001
2018
  if (conditions?.login) {
@@ -2012,7 +2029,7 @@
2012
2029
  }
2013
2030
  else {
2014
2031
  if (!isMet)
2015
- return { isValid: false };
2032
+ return { isValid: false, availableClaimsNow: 0 };
2016
2033
  }
2017
2034
  }
2018
2035
  if (conditions?.loginStreak) {
@@ -2032,7 +2049,7 @@
2032
2049
  }
2033
2050
  else {
2034
2051
  if (isDisqualify)
2035
- return { isValid: false };
2052
+ return { isValid: false, availableClaimsNow: 0 };
2036
2053
  }
2037
2054
  }
2038
2055
  if (conditions?.social) {
@@ -2054,6 +2071,17 @@
2054
2071
  if (likes < minLikes || views < minViews || comments < minComments) {
2055
2072
  isDisqualify = true;
2056
2073
  }
2074
+ if (shouldScale && mode === 'accumulate' && hasContent) {
2075
+ const baseLikes = cSocial?.minLikes || 0;
2076
+ const baseViews = cSocial?.minViews || 0;
2077
+ const baseComments = cSocial?.minComments || 0;
2078
+ if (baseLikes > 0)
2079
+ updateMax(Math.floor(likes / baseLikes));
2080
+ if (baseViews > 0)
2081
+ updateMax(Math.floor(views / baseViews));
2082
+ if (baseComments > 0)
2083
+ updateMax(Math.floor(comments / baseComments));
2084
+ }
2057
2085
  if (addDetails) {
2058
2086
  const platformMap = {
2059
2087
  tiktok: 'TikTok',
@@ -2125,14 +2153,18 @@
2125
2153
  }
2126
2154
  else {
2127
2155
  if (isDisqualify)
2128
- return { isValid: false };
2156
+ return { isValid: false, availableClaimsNow: 0 };
2129
2157
  }
2130
2158
  }
2131
2159
  // Linked completions - wait for N linked entities to complete
2132
2160
  if (conditions?.linkedCompletions?.min) {
2161
+ const baseMin = conditions.linkedCompletions.min;
2133
2162
  const currentCount = completionTrackers?.linkedCompletions || 0;
2134
- const scaledMin = conditions.linkedCompletions.min * claimMultiplier;
2163
+ const scaledMin = baseMin * claimMultiplier;
2135
2164
  const isDisqualify = currentCount < scaledMin;
2165
+ if (shouldScale && baseMin > 0) {
2166
+ updateMax(Math.floor(currentCount / baseMin));
2167
+ }
2136
2168
  if (addDetails) {
2137
2169
  conditionData.push({
2138
2170
  isMet: !isDisqualify,
@@ -2145,7 +2177,7 @@
2145
2177
  }
2146
2178
  else {
2147
2179
  if (isDisqualify)
2148
- return { isValid: false };
2180
+ return { isValid: false, availableClaimsNow: 0 };
2149
2181
  }
2150
2182
  }
2151
2183
  if (conditions?.dynamicTracker?.conditions?.length) {
@@ -2155,6 +2187,13 @@
2155
2187
  ...conditions.dynamicTracker,
2156
2188
  conditions: resolvedConditions,
2157
2189
  }, claimMultiplier);
2190
+ if (shouldScale) {
2191
+ const dynamicMax = getMaxClaimsForDynamicGroup(completionTrackers?.dynamicTracker || {}, {
2192
+ ...conditions.dynamicTracker,
2193
+ conditions: resolvedConditions,
2194
+ }, playerOffer.claimedCount || 0);
2195
+ updateMax(dynamicMax);
2196
+ }
2158
2197
  if (addDetails) {
2159
2198
  conditionData.push({
2160
2199
  isMet: dynamicResult,
@@ -2166,7 +2205,7 @@
2166
2205
  }
2167
2206
  else {
2168
2207
  if (!dynamicResult)
2169
- return { isValid: false };
2208
+ return { isValid: false, availableClaimsNow: 0 };
2170
2209
  }
2171
2210
  }
2172
2211
  const r = meetsBaseConditions({
@@ -2177,9 +2216,18 @@
2177
2216
  });
2178
2217
  isValid = isValid && r.isValid;
2179
2218
  conditionData.push(...(r.conditionData || []));
2180
- return { isValid, conditionData };
2181
- }
2182
- return { isValid: true, conditionData: [] };
2219
+ if (maxClaimCount && maxClaimCount > 0) {
2220
+ updateMax(maxClaimCount);
2221
+ }
2222
+ const claimedCount = playerOffer.claimedCount || 0;
2223
+ let availableClaimsNow = !isValid
2224
+ ? 0
2225
+ : maxTotalClaimsFromScaling === Infinity
2226
+ ? -1
2227
+ : Math.max(0, maxTotalClaimsFromScaling - claimedCount);
2228
+ return { isValid, conditionData, availableClaimsNow };
2229
+ }
2230
+ return { isValid: true, conditionData: [], availableClaimsNow: -1 };
2183
2231
  };
2184
2232
  /**
2185
2233
  * Checks if completion conditions were met before a specific expiry time.
@@ -2344,26 +2392,30 @@
2344
2392
  }
2345
2393
  }
2346
2394
  const compareTo = isNumber ? Number(cond.compareTo) : String(cond.compareTo);
2347
- switch (cond.operator) {
2348
- case '==':
2349
- return val === compareTo;
2350
- case '!=':
2351
- return val !== compareTo;
2352
- }
2353
2395
  if (isNumber && typeof compareTo === 'number') {
2396
+ const skipMultiplier = cond.operator === '==' || cond.operator === '!=';
2397
+ const scaledCompareTo = skipMultiplier ? compareTo : compareTo * claimMultiplier;
2354
2398
  switch (cond.operator) {
2399
+ case '==':
2400
+ return val === scaledCompareTo;
2401
+ case '!=':
2402
+ return val !== scaledCompareTo;
2355
2403
  case '>':
2356
- return val > compareTo * claimMultiplier;
2404
+ return val > scaledCompareTo;
2357
2405
  case '>=':
2358
- return val >= compareTo * claimMultiplier;
2406
+ return val >= scaledCompareTo;
2359
2407
  case '<':
2360
- return val < compareTo * claimMultiplier;
2408
+ return val < scaledCompareTo;
2361
2409
  case '<=':
2362
- return val <= compareTo * claimMultiplier;
2410
+ return val <= scaledCompareTo;
2363
2411
  }
2364
2412
  }
2365
2413
  else if (!isNumber && typeof compareTo === 'string') {
2366
2414
  switch (cond.operator) {
2415
+ case '==':
2416
+ return val === compareTo;
2417
+ case '!=':
2418
+ return val !== compareTo;
2367
2419
  case 'has':
2368
2420
  return val.includes(compareTo);
2369
2421
  case 'not_has':
@@ -2372,6 +2424,98 @@
2372
2424
  }
2373
2425
  return false;
2374
2426
  }
2427
+ /**
2428
+ * Calculates the maximum number of claims supported by a single dynamic condition.
2429
+ */
2430
+ function getMaxClaimsForDynamicCondition(dynamicObj, cond) {
2431
+ if (!dynamicObj)
2432
+ return 0;
2433
+ const val = dynamicObj[cond.key];
2434
+ if (val === undefined)
2435
+ return 0;
2436
+ if (typeof val === 'number') {
2437
+ const base = Number(cond.compareTo);
2438
+ if (isNaN(base)) {
2439
+ return evaluateDynamicCondition(dynamicObj, cond, 1) ? Infinity : 0;
2440
+ }
2441
+ switch (cond.operator) {
2442
+ case '>=':
2443
+ if (base === 0)
2444
+ return val >= 0 ? Infinity : 0;
2445
+ if (base < 0)
2446
+ return val >= base ? Infinity : 0;
2447
+ return Math.max(0, Math.floor(val / base));
2448
+ case '>':
2449
+ if (base === 0)
2450
+ return val > 0 ? Infinity : 0;
2451
+ if (base < 0)
2452
+ return val > base ? Infinity : 0;
2453
+ if (val <= 0)
2454
+ return 0;
2455
+ return Math.max(0, Math.ceil(val / base) - 1);
2456
+ case '==':
2457
+ return val === base ? Infinity : 0;
2458
+ case '!=':
2459
+ return val !== base ? Infinity : 0;
2460
+ case '<=':
2461
+ if (base === 0)
2462
+ return val <= 0 ? Infinity : 0;
2463
+ if (base > 0)
2464
+ return evaluateDynamicCondition(dynamicObj, cond, 1) ? Infinity : 0;
2465
+ if (val >= 0)
2466
+ return 0;
2467
+ return Math.max(0, Math.floor(val / base));
2468
+ case '<':
2469
+ if (base === 0)
2470
+ return val < 0 ? Infinity : 0;
2471
+ if (base > 0)
2472
+ return evaluateDynamicCondition(dynamicObj, cond, 1) ? Infinity : 0;
2473
+ if (val >= 0)
2474
+ return 0;
2475
+ return Math.max(0, Math.ceil(val / base) - 1);
2476
+ }
2477
+ }
2478
+ // we don't scale the rest, they are always true or always false
2479
+ return evaluateDynamicCondition(dynamicObj, cond, 1) ? Infinity : 0;
2480
+ }
2481
+ /**
2482
+ * Calculates the maximum number of claims supported by a group of dynamic conditions.
2483
+ */
2484
+ function getMaxClaimsForDynamicGroup(dynamicObj, dynamicGroup, currentClaimCount = 0) {
2485
+ const { conditions, links } = dynamicGroup;
2486
+ if (!conditions || conditions.length === 0)
2487
+ return Infinity;
2488
+ // AND only
2489
+ if (!links || links.length === 0 || links.every((l) => l === 'AND')) {
2490
+ let minClaims = Infinity;
2491
+ for (const cond of conditions) {
2492
+ const max = getMaxClaimsForDynamicCondition(dynamicObj, cond);
2493
+ if (max === 0)
2494
+ return 0;
2495
+ minClaims = Math.min(minClaims, max);
2496
+ }
2497
+ return minClaims;
2498
+ }
2499
+ // OR only
2500
+ if (links.every((l) => l === 'OR')) {
2501
+ let maxClaims = 0;
2502
+ for (const cond of conditions) {
2503
+ const max = getMaxClaimsForDynamicCondition(dynamicObj, cond);
2504
+ if (max === Infinity)
2505
+ return Infinity;
2506
+ maxClaims = Math.max(maxClaims, max);
2507
+ }
2508
+ return maxClaims;
2509
+ }
2510
+ // mixed:
2511
+ const maxIterations = 100;
2512
+ for (let n = currentClaimCount + 1; n <= currentClaimCount + maxIterations; n++) {
2513
+ if (!meetsDynamicConditions(dynamicObj, dynamicGroup, n)) {
2514
+ return n - 1;
2515
+ }
2516
+ }
2517
+ return currentClaimCount + maxIterations;
2518
+ }
2375
2519
  /**
2376
2520
  * Evaluates a group of dynamic conditions with logical links (AND, OR, AND NOT).
2377
2521
  * @param dynamicObj - The player's dynamic object with any key and string or number value.
@@ -2480,6 +2624,8 @@
2480
2624
  exports.OfferwallClient = OfferwallClient;
2481
2625
  exports.PlayerOfferStatuses = PlayerOfferStatuses;
2482
2626
  exports.SSEConnection = SSEConnection;
2627
+ exports.getMaxClaimsForDynamicCondition = getMaxClaimsForDynamicCondition;
2628
+ exports.getMaxClaimsForDynamicGroup = getMaxClaimsForDynamicGroup;
2483
2629
  exports.hasConditions = hasConditions;
2484
2630
  exports.meetsBaseConditions = meetsBaseConditions;
2485
2631
  exports.meetsClaimableConditions = meetsClaimableConditions;