@rebuy/rebuy 3.3.0 → 3.3.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.
@@ -23,6 +23,7 @@ __export(gwp_exports, {
23
23
  getValidGwpProductIds: () => getValidGwpProductIds,
24
24
  hasLostDiscount: () => hasLostDiscount,
25
25
  hasUnsupportedRuleTypes: () => hasUnsupportedRuleTypes,
26
+ isConfirmedRuleMiss: () => isConfirmedRuleMiss,
26
27
  isDefinitiveRuleAnswer: () => isDefinitiveRuleAnswer,
27
28
  shouldRemoveGift: () => shouldRemoveGift
28
29
  });
@@ -40,6 +41,9 @@ var ruleHasUnsupportedType = (rule) => (rule.logic ?? []).some(
40
41
  );
41
42
  var hasUnsupportedRuleTypes = (metadata) => [...metadata?.matchedRules ?? [], ...metadata?.unmatchedRules ?? []].some(ruleHasUnsupportedType);
42
43
 
44
+ // src/gwp/isConfirmedRuleMiss.ts
45
+ var isConfirmedRuleMiss = (metadata) => (metadata.unmatchedRules?.length ?? 0) > 0 && (metadata.matchedRules?.length ?? 0) === 0;
46
+
43
47
  // src/gwp/isDefinitiveRuleAnswer.ts
44
48
  var isDefinitiveRuleAnswer = (metadata) => metadata != null && (metadata.matchedRules != null || metadata.unmatchedRules != null);
45
49
 
@@ -1,5 +1,6 @@
1
1
  export * from '../gwp/getValidGwpProductIds';
2
2
  export * from '../gwp/hasUnsupportedRuleTypes';
3
+ export * from '../gwp/isConfirmedRuleMiss';
3
4
  export * from '../gwp/isDefinitiveRuleAnswer';
4
5
  export * from '../gwp/shouldRemoveGift';
5
6
  export * from '../gwp/types';
@@ -10,6 +10,9 @@ var ruleHasUnsupportedType = (rule) => (rule.logic ?? []).some(
10
10
  );
11
11
  var hasUnsupportedRuleTypes = (metadata) => [...metadata?.matchedRules ?? [], ...metadata?.unmatchedRules ?? []].some(ruleHasUnsupportedType);
12
12
 
13
+ // src/gwp/isConfirmedRuleMiss.ts
14
+ var isConfirmedRuleMiss = (metadata) => (metadata.unmatchedRules?.length ?? 0) > 0 && (metadata.matchedRules?.length ?? 0) === 0;
15
+
13
16
  // src/gwp/isDefinitiveRuleAnswer.ts
14
17
  var isDefinitiveRuleAnswer = (metadata) => metadata != null && (metadata.matchedRules != null || metadata.unmatchedRules != null);
15
18
 
@@ -20,6 +23,7 @@ export {
20
23
  getValidGwpProductIds,
21
24
  hasLostDiscount,
22
25
  hasUnsupportedRuleTypes,
26
+ isConfirmedRuleMiss,
23
27
  isDefinitiveRuleAnswer,
24
28
  shouldRemoveGift
25
29
  };
@@ -0,0 +1,12 @@
1
+ import { type GwpMetadata } from '../gwp/types';
2
+ /**
3
+ * Whether a definitive answer affirmatively evaluated the ruleset and NOTHING matched:
4
+ * `unmatchedRules` carries the evaluated-and-failed rules while `matchedRules` is empty. This is
5
+ * the only empty-valid-set shape that may remove gifts on qualification. An empty valid set is
6
+ * otherwise ambiguous — a MATCHED rule whose output enumerates no product ids (collection /
7
+ * endpoint / tag outputs carry none) or a zero-rule ruleset proves nothing, and removing on it
8
+ * would confiscate a still-qualified gift permanently (the watcher never re-adds). Mirrors the
9
+ * React extension's `isConfirmedRuleMiss` (REB-23842); the discount-lost pass bounds the
10
+ * fail-open cost on every path.
11
+ */
12
+ export declare const isConfirmedRuleMiss: (metadata: GwpMetadata) => boolean;
@@ -8,8 +8,11 @@ import { type GiftValidationInput, type ServerContext } from '../server/shared';
8
8
  * the rules) needs the engine's answer, so it is **fail-safe**: any error, a missing endpoint, a
9
9
  * non-definitive rule answer (no metadata, or metadata with neither rule array — the tolerated-empty-2xx
10
10
  * path), or unsupported rule types (`url`/`order_tag`) → don't drop on qualification (never remove a
11
- * genuinely-qualified gift on uncertainty). A definitive answer that omits a gift even `matchedRules: []`
12
- * removes it. **Discount loss** ({@link hasLostDiscount}: `!discounted && cost > 0`) is cart-observable
11
+ * genuinely-qualified gift on uncertainty). A definitive answer removes a gift its matched-rule product
12
+ * output omits; when that output yields NO ids at all, only a confirmed miss ({@link isConfirmedRuleMiss}:
13
+ * `unmatchedRules` populated, `matchedRules` empty) removes — a matched rule with a non-product output
14
+ * (collection/endpoint/tag) or a zero-rule ruleset proves nothing, so those keep the gifts (REB-23842,
15
+ * matching the React extension). **Discount loss** ({@link hasLostDiscount}: `!discounted && cost > 0`) is cart-observable
13
16
  * and needs NO engine answer, so it removes the gift on EVERY path, degraded or not — a buyer must not keep
14
17
  * paying for a "gift" because the rule engine was degraded. (React removed on any successful fetch; this
15
18
  * splits the observable discount-loss out of the qualification fail-safe rather than gating both behind it.)
@@ -61,6 +61,9 @@ var ruleHasUnsupportedType = (rule) => (rule.logic ?? []).some(
61
61
  );
62
62
  var hasUnsupportedRuleTypes = (metadata) => [...metadata?.matchedRules ?? [], ...metadata?.unmatchedRules ?? []].some(ruleHasUnsupportedType);
63
63
 
64
+ // src/gwp/isConfirmedRuleMiss.ts
65
+ var isConfirmedRuleMiss = (metadata) => (metadata.unmatchedRules?.length ?? 0) > 0 && (metadata.matchedRules?.length ?? 0) === 0;
66
+
64
67
  // src/gwp/isDefinitiveRuleAnswer.ts
65
68
  var isDefinitiveRuleAnswer = (metadata) => metadata != null && (metadata.matchedRules != null || metadata.unmatchedRules != null);
66
69
 
@@ -3882,6 +3885,7 @@ var validateGifts = async (input, ctx) => {
3882
3885
  const meta = metadata ?? void 0;
3883
3886
  if (!isDefinitiveRuleAnswer(meta) || hasUnsupportedRuleTypes(meta)) return discountLost;
3884
3887
  const validIds = getValidGwpProductIds(meta.matchedRules);
3888
+ if (validIds.size === 0 && !isConfirmedRuleMiss(meta)) return discountLost;
3885
3889
  return gifts.filter((gift) => shouldRemoveGift(gift, validIds)).map((gift) => gift.lineId);
3886
3890
  } catch {
3887
3891
  return discountLost;
@@ -13,6 +13,9 @@ var ruleHasUnsupportedType = (rule) => (rule.logic ?? []).some(
13
13
  );
14
14
  var hasUnsupportedRuleTypes = (metadata) => [...metadata?.matchedRules ?? [], ...metadata?.unmatchedRules ?? []].some(ruleHasUnsupportedType);
15
15
 
16
+ // src/gwp/isConfirmedRuleMiss.ts
17
+ var isConfirmedRuleMiss = (metadata) => (metadata.unmatchedRules?.length ?? 0) > 0 && (metadata.matchedRules?.length ?? 0) === 0;
18
+
16
19
  // src/gwp/isDefinitiveRuleAnswer.ts
17
20
  var isDefinitiveRuleAnswer = (metadata) => metadata != null && (metadata.matchedRules != null || metadata.unmatchedRules != null);
18
21
 
@@ -3834,6 +3837,7 @@ var validateGifts = async (input, ctx) => {
3834
3837
  const meta = metadata ?? void 0;
3835
3838
  if (!isDefinitiveRuleAnswer(meta) || hasUnsupportedRuleTypes(meta)) return discountLost;
3836
3839
  const validIds = getValidGwpProductIds(meta.matchedRules);
3840
+ if (validIds.size === 0 && !isConfirmedRuleMiss(meta)) return discountLost;
3837
3841
  return gifts.filter((gift) => shouldRemoveGift(gift, validIds)).map((gift) => gift.lineId);
3838
3842
  } catch {
3839
3843
  return discountLost;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebuy/rebuy",
3
- "version": "3.3.0",
3
+ "version": "3.3.1",
4
4
  "description": "Shared zod schemas, legacy-to-CAB widget transforms, and server orchestrators for Rebuy's Shopify consumers (rebuy-api, admin-nextjs, rebuy-shopify-extensions)",
5
5
  "license": "MIT",
6
6
  "author": "Rebuy, Inc.",