@fire-spark/core 0.3.5 → 0.4.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/dist/index.d.ts +22 -17
- package/dist/index.js +522 -501
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -408,7 +408,7 @@ type ApplyRedeemInput = {
|
|
|
408
408
|
reward_id: string;
|
|
409
409
|
/**
|
|
410
410
|
* Reward units to redeem (integer ≥ 1). With `payment_id` it is
|
|
411
|
-
* informational: `amount`
|
|
411
|
+
* informational: `amount` already covers every unit.
|
|
412
412
|
*/
|
|
413
413
|
quantity: number;
|
|
414
414
|
/** 0–1, compounded per unit over the remaining value. */
|
|
@@ -423,19 +423,17 @@ type ApplyRedeemInput = {
|
|
|
423
423
|
/** Root line to discount; omit to discount the whole order (prorated). */
|
|
424
424
|
line_uid?: string;
|
|
425
425
|
/**
|
|
426
|
-
*
|
|
427
|
-
*
|
|
428
|
-
* with the same `payment_id` share one tender
|
|
426
|
+
* Reward paid as a tender (voucher, balance…, not a discount): the
|
|
427
|
+
* merchant `REDEMPTION` payment that pays `amount`. Requires `amount`.
|
|
428
|
+
* Redeems with the same `payment_id` share one tender
|
|
429
|
+
* (`details.rewards[]`).
|
|
429
430
|
*/
|
|
430
431
|
payment_id?: string;
|
|
431
432
|
/**
|
|
432
|
-
* Total money the
|
|
433
|
+
* Total money the reward covers for the whole entry (all `quantity`
|
|
433
434
|
* units, not per unit); trimmed to total − tip − delivery.
|
|
434
435
|
*/
|
|
435
436
|
amount?: number;
|
|
436
|
-
/** Total points spent for the whole entry (not per unit). */
|
|
437
|
-
points?: number;
|
|
438
|
-
program?: string;
|
|
439
437
|
/** Provider of the REDEMPTION payment (default `firespark`). */
|
|
440
438
|
provider?: PaymentProvider;
|
|
441
439
|
/** ISO datetime with offset; validation fails once it has passed. */
|
|
@@ -517,9 +515,9 @@ declare class Order {
|
|
|
517
515
|
emptyOrder(): void;
|
|
518
516
|
/**
|
|
519
517
|
* Appends a redeem to `line_uid` (root line) or to the order. Steps keep
|
|
520
|
-
* the order in which redeems were applied. With `payment_id` (
|
|
521
|
-
* balance) it is not a discount: `amount`
|
|
522
|
-
*
|
|
518
|
+
* the order in which redeems were applied. With `payment_id` (voucher,
|
|
519
|
+
* balance…) it is not a discount: `amount` is the total of the whole
|
|
520
|
+
* entry (every unit, not per unit) and `quantity` is informational.
|
|
523
521
|
* Every redeem with the same `payment_id` is paid by one REDEMPTION
|
|
524
522
|
* tender whose `details.rewards[]` lists them; their summed amount is
|
|
525
523
|
* capped at total − tip − delivery (D15, D18, D19).
|
|
@@ -528,9 +526,8 @@ declare class Order {
|
|
|
528
526
|
/**
|
|
529
527
|
* Removes `quantity` units of `reward_id` from `line_uid` or the order,
|
|
530
528
|
* latest applied first. Removing more units than applied clears them all
|
|
531
|
-
* (and the REDEMPTION tender of a
|
|
532
|
-
*
|
|
533
|
-
* `amount` and `points`.
|
|
529
|
+
* (and the REDEMPTION tender of a payment redeem). A payment redeem that
|
|
530
|
+
* keeps some units keeps the same share of its `amount`.
|
|
534
531
|
*/
|
|
535
532
|
removeRedeem(input: RemoveRedeemInput): void;
|
|
536
533
|
private rememberLogScope;
|
|
@@ -544,7 +541,7 @@ declare class Order {
|
|
|
544
541
|
cancelOrder(): Promise<void>;
|
|
545
542
|
/**
|
|
546
543
|
* Reopens this placed, unpaid order: the server voids every payment
|
|
547
|
-
* (REDEMPTION
|
|
544
|
+
* (REDEMPTION rewards released), cancels it as `VOIDED` and opens a new
|
|
548
545
|
* `OPEN` order with the same content. This instance is closed afterwards;
|
|
549
546
|
* replace it with the returned `Order` (same HTTP options and listener).
|
|
550
547
|
*
|
|
@@ -786,6 +783,12 @@ declare const isDeviceCharged: (tender: {
|
|
|
786
783
|
}) => boolean;
|
|
787
784
|
/** Most a REDEMPTION can cover: order total − tip − delivery (D18). */
|
|
788
785
|
declare const redemptionCap: (order: Pick<Order$1, "lines" | "totals">) => number;
|
|
786
|
+
/**
|
|
787
|
+
* Trims payment redeems in application order so their sum fits the cap.
|
|
788
|
+
* Discount redeems are left untouched. The last trimmed amount is whatever
|
|
789
|
+
* room remains, so the kept amounts sum to at most `cap`.
|
|
790
|
+
*/
|
|
791
|
+
declare const clampPaymentRedeems: (redeem: readonly OrderRedeem[], cap: number, _currency: string) => OrderRedeem[];
|
|
789
792
|
type SimpleTenderPayment = {
|
|
790
793
|
id?: string;
|
|
791
794
|
type?: PaymentTenderInput["method"];
|
|
@@ -856,7 +859,7 @@ declare const listMenusStream: (scope?: StorefrontMenuScope, options?: ListMenus
|
|
|
856
859
|
//#endregion
|
|
857
860
|
//#region src/services/payments.d.ts
|
|
858
861
|
type ListPaymentsOptions = ServiceHttpOptions & {
|
|
859
|
-
/** Keep `REDEMPTION` payments (
|
|
862
|
+
/** Keep `REDEMPTION` payments (vouchers, balance…) for redeem flows. */
|
|
860
863
|
includeRedemptions?: boolean;
|
|
861
864
|
};
|
|
862
865
|
/**
|
|
@@ -1145,6 +1148,8 @@ declare class RulesEngine {
|
|
|
1145
1148
|
private assertBoolean;
|
|
1146
1149
|
private assertCurrency;
|
|
1147
1150
|
private assertEnum;
|
|
1151
|
+
private customerAge;
|
|
1152
|
+
private customerDate;
|
|
1148
1153
|
private assertAbsoluteDate;
|
|
1149
1154
|
private assertRelativeDate;
|
|
1150
1155
|
private assertRecurringDate;
|
|
@@ -1319,4 +1324,4 @@ declare const validateCardholderName: (value: string) => CardholderNameError | n
|
|
|
1319
1324
|
*/
|
|
1320
1325
|
declare const cardPreviewGroups: (value: string) => CardPreviewSlot[][];
|
|
1321
1326
|
//#endregion
|
|
1322
|
-
export { type ApplyRedeemInput, type AuthConfigureOptions, CARD_BRANDS, type CardBrand, type CardBrandSpec, type CardCvcError, type CardExpiry, type CardExpiryError, type CardNumberError, type CardPreviewSlot, type CardholderNameError, type CreateOrderOptions, type CreateSessionOptions, type CustomizationPath, type CustomizationSession, DEFAULT_RECOMMENDATION_TYPES, type ExchangeOptions, type FailedCharge, type FlatMenuCategory, type GroupDisplayType, type IdBuilder, type ListMenusStreamOptions, type ListPaymentsOptions, type Menu, type MenuCategoryNode, type MenuLookup, type MilestoneSteps, type MilestoneTrackItem, type ModifierControlType, type ModifierCtx, type ModifierGroupCtx, type NestedMenuCategory, Order, type OrderCustomerSource, type OrderDraft, type OrderIssue, type OrderIssueCode, type OrderLineDiff, type OrderLineIssue, type OrderLineIssueCode, type OrderScope, OrderSyncPendingError, OrderValidationError, type OrderValidationResult, type PayIntentOptions, type PayIntentResult, type PayOptions, type PaymentCardBrand, type PaymentProgressEvent, type PaymentProgressListener, type PaymentRunOptions, type PaymentSessionOptions, type PricingModifier, type Product, type ProductCtx, type QuantitiesByPath, type RecommendationType, RecurringDateCron, type RemoveRedeemInput, type ReorderOrderOptions, type ResetOptions, type ReversalOutcome, type ReverseAllOptions, RulesEngine, type ScheduleWindow, type SelectionStatus, type SelectionsByPath, type SerializedError, type ServiceHttpOptions, type SimpleTenderPayment, type Snapshot, type StorefrontAccessToken, type StorefrontErrorDetails, StorefrontHttpError, type TrackWorkflow, type ValidateOptions, type ValidateOrderAgainstMenuInput, type Variant, type VariantGroup, type VariantGroupCtx, type VariantOptionCtx, type VariantSelection, type VerifyWebhookSignatureInput, type VisitedState, addLines, auth, buildCustomerSnapshot, cardBrandSpec, cardDigits, cardNumberGaps, cardNumberMaxLength, cardPreviewGroups, caretAfterDigits, collectOrderLineDiffs, computeStartingPrice, countCartProducts, coveredAmount, createSession, currentNodes, cvcMaxLength, defaultConditionError, detectCardBrand, flattenMenuCategories, flushLogs, formatCardNumber, formatCvc, formatExpiry, fromOrderLines, genId, genOrderId, getLogContext, getScheduleWindow, indexMenu, isCardNumberComplete, isCardNumberDefinitelyInvalid, isCheckoutFieldFilled, isCvcComplete, isDeviceCharged, isDeviceNotLinked, isExpiryComplete, isKioskUnauthorized, isMilestoneStepDone, isOnSchedule, isOrderValidationError, isPosDeviceNotLinked, isPosUnauthorized, isRealPaymentMethod, isStorefrontNotFound, isVariantBase, listMenusStream, localizePosError, log, logError, luhnCheck, matchAbsoluteDateOperator, matchBooleanOperator, matchCurrencyOperator, matchEnumOperator, matchNumberOperator, matchRecurringDateOperator, matchRelativeDateOperator, matchStringOperator, milestoneSteps, milestoneTrack, nestMenuCategories, normalizeCardholderName, orderPaymentRedeems, parseExpiry, paymentMatchesClientConditions, recomputeRedemptions, recomputeTotals, redemptionCap, remainingAmount, removeLine, replaceLine, resolveCheckout, reversePayment, runPaymentAttempt, selectablePayments, serializeError, services, setLineQuantity, storefrontJson, stripRedemptionLines, toOrderLines, toPaymentCardBrand, validateCardNumber, validateCardholderName, validateCvc, validateExpiry, validateOrderAgainstMenu, verifyWebhookSignature, visitedStates };
|
|
1327
|
+
export { type ApplyRedeemInput, type AuthConfigureOptions, CARD_BRANDS, type CardBrand, type CardBrandSpec, type CardCvcError, type CardExpiry, type CardExpiryError, type CardNumberError, type CardPreviewSlot, type CardholderNameError, type CreateOrderOptions, type CreateSessionOptions, type CustomizationPath, type CustomizationSession, DEFAULT_RECOMMENDATION_TYPES, type ExchangeOptions, type FailedCharge, type FlatMenuCategory, type GroupDisplayType, type IdBuilder, type ListMenusStreamOptions, type ListPaymentsOptions, type Menu, type MenuCategoryNode, type MenuLookup, type MilestoneSteps, type MilestoneTrackItem, type ModifierControlType, type ModifierCtx, type ModifierGroupCtx, type NestedMenuCategory, Order, type OrderCustomerSource, type OrderDraft, type OrderIssue, type OrderIssueCode, type OrderLineDiff, type OrderLineIssue, type OrderLineIssueCode, type OrderScope, OrderSyncPendingError, OrderValidationError, type OrderValidationResult, type PayIntentOptions, type PayIntentResult, type PayOptions, type PaymentCardBrand, type PaymentProgressEvent, type PaymentProgressListener, type PaymentRunOptions, type PaymentSessionOptions, type PricingModifier, type Product, type ProductCtx, type QuantitiesByPath, type RecommendationType, RecurringDateCron, type RemoveRedeemInput, type ReorderOrderOptions, type ResetOptions, type ReversalOutcome, type ReverseAllOptions, RulesEngine, type ScheduleWindow, type SelectionStatus, type SelectionsByPath, type SerializedError, type ServiceHttpOptions, type SimpleTenderPayment, type Snapshot, type StorefrontAccessToken, type StorefrontErrorDetails, StorefrontHttpError, type TrackWorkflow, type ValidateOptions, type ValidateOrderAgainstMenuInput, type Variant, type VariantGroup, type VariantGroupCtx, type VariantOptionCtx, type VariantSelection, type VerifyWebhookSignatureInput, type VisitedState, addLines, auth, buildCustomerSnapshot, cardBrandSpec, cardDigits, cardNumberGaps, cardNumberMaxLength, cardPreviewGroups, caretAfterDigits, clampPaymentRedeems, collectOrderLineDiffs, computeStartingPrice, countCartProducts, coveredAmount, createSession, currentNodes, cvcMaxLength, defaultConditionError, detectCardBrand, flattenMenuCategories, flushLogs, formatCardNumber, formatCvc, formatExpiry, fromOrderLines, genId, genOrderId, getLogContext, getScheduleWindow, indexMenu, isCardNumberComplete, isCardNumberDefinitelyInvalid, isCheckoutFieldFilled, isCvcComplete, isDeviceCharged, isDeviceNotLinked, isExpiryComplete, isKioskUnauthorized, isMilestoneStepDone, isOnSchedule, isOrderValidationError, isPosDeviceNotLinked, isPosUnauthorized, isRealPaymentMethod, isStorefrontNotFound, isVariantBase, listMenusStream, localizePosError, log, logError, luhnCheck, matchAbsoluteDateOperator, matchBooleanOperator, matchCurrencyOperator, matchEnumOperator, matchNumberOperator, matchRecurringDateOperator, matchRelativeDateOperator, matchStringOperator, milestoneSteps, milestoneTrack, nestMenuCategories, normalizeCardholderName, orderPaymentRedeems, parseExpiry, paymentMatchesClientConditions, recomputeRedemptions, recomputeTotals, redemptionCap, remainingAmount, removeLine, replaceLine, resolveCheckout, reversePayment, runPaymentAttempt, selectablePayments, serializeError, services, setLineQuantity, storefrontJson, stripRedemptionLines, toOrderLines, toPaymentCardBrand, validateCardNumber, validateCardholderName, validateCvc, validateExpiry, validateOrderAgainstMenu, verifyWebhookSignature, visitedStates };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DIGITAL_COLLECT_AT, PARTIAL_APPROVAL_ERROR, activeIntent, amountTolerance, createAttemptReference, createPaymentError,
|
|
1
|
+
import { DIGITAL_COLLECT_AT, PARTIAL_APPROVAL_ERROR, activeIntent, amountTolerance, createAttemptReference, createPaymentError, currentIntent, defaultReversalFor, discountRedeems, groupPaymentRedeems, isDeliveryFee, isPaymentError, isPaymentRedeem, isRealPaymentMethod, isRealPaymentMethod as isRealPaymentMethod$1, isTenderSettled, isTipFee, liveTenders, memoryPaymentStorage, missingDeviceFields, money, moneyNumber, moneyZero, orderPaymentRedeems, orderPaymentRedeems as orderPaymentRedeems$1, paymentRedeemsCoverTotal, resolvePaymentPolicy, resolveProviderEnvironment, scopedPaymentStorage, taxBreakdown, toChargeAmount, withTerminalLock } from "@fire-spark/payments";
|
|
2
2
|
import advancedFormat from "dayjs/plugin/advancedFormat.js";
|
|
3
3
|
import isBetween from "dayjs/plugin/isBetween.js";
|
|
4
4
|
import isSameOrAfter from "dayjs/plugin/isSameOrAfter.js";
|
|
@@ -3465,40 +3465,42 @@ const isDeviceCharged = (tender) => {
|
|
|
3465
3465
|
/** Most a REDEMPTION can cover: order total − tip − delivery (D18). */
|
|
3466
3466
|
const redemptionCap = (order) => {
|
|
3467
3467
|
const primaryOnly = order.lines.filter((line) => line.parent_uid == null).reduce((sum, line) => {
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3468
|
+
if (!(isTipFee(line) || isDeliveryFee(line))) return sum;
|
|
3469
|
+
return sum.plus(line.group_total);
|
|
3470
|
+
}, moneyZero());
|
|
3471
|
+
const cap = money(order.totals.total).minus(primaryOnly);
|
|
3472
|
+
if (cap.lt(0)) return 0;
|
|
3473
|
+
return moneyNumber(cap);
|
|
3472
3474
|
};
|
|
3473
3475
|
/**
|
|
3474
3476
|
* Room left for order-level payment redeems: the D18 cap minus what the
|
|
3475
3477
|
* payment redeems kept on lines already cover.
|
|
3476
3478
|
*/
|
|
3477
3479
|
const orderRedeemCap = (order) => {
|
|
3478
|
-
const used = orderPaymentRedeems({ lines: order.lines }).reduce((sum, item) =>
|
|
3479
|
-
|
|
3480
|
-
|
|
3480
|
+
const used = orderPaymentRedeems({ lines: order.lines }).reduce((sum, item) => {
|
|
3481
|
+
return sum.plus(item.amount ?? 0);
|
|
3482
|
+
}, moneyZero());
|
|
3483
|
+
const left = money(redemptionCap(order)).minus(used);
|
|
3484
|
+
if (left.lt(0)) return 0;
|
|
3485
|
+
return moneyNumber(left);
|
|
3481
3486
|
};
|
|
3482
3487
|
/**
|
|
3483
|
-
* Trims payment redeems in application order so their sum fits the cap
|
|
3484
|
-
*
|
|
3488
|
+
* Trims payment redeems in application order so their sum fits the cap.
|
|
3489
|
+
* Discount redeems are left untouched. The last trimmed amount is whatever
|
|
3490
|
+
* room remains, so the kept amounts sum to at most `cap`.
|
|
3485
3491
|
*/
|
|
3486
|
-
const clampPaymentRedeems = (redeem, cap,
|
|
3487
|
-
|
|
3488
|
-
let left = cap;
|
|
3492
|
+
const clampPaymentRedeems = (redeem, cap, _currency) => {
|
|
3493
|
+
let left = money(cap);
|
|
3489
3494
|
return redeem.flatMap((item) => {
|
|
3490
3495
|
if (!isPaymentRedeem(item)) return [item];
|
|
3491
|
-
const amount = item.amount ?? 0;
|
|
3492
|
-
const fits =
|
|
3493
|
-
left =
|
|
3494
|
-
if (fits
|
|
3495
|
-
if (fits
|
|
3496
|
-
const ratio = amount > 0 ? fits / amount : 0;
|
|
3497
|
-
const points = roundTo((item.points ?? 0) * ratio, 0);
|
|
3496
|
+
const amount = money(item.amount ?? 0);
|
|
3497
|
+
const fits = amount.lt(left) ? amount : left;
|
|
3498
|
+
left = left.minus(fits);
|
|
3499
|
+
if (fits.lte(0)) return [];
|
|
3500
|
+
if (fits.eq(amount)) return [item];
|
|
3498
3501
|
return [{
|
|
3499
3502
|
...item,
|
|
3500
|
-
amount: fits
|
|
3501
|
-
points
|
|
3503
|
+
amount: moneyNumber(fits)
|
|
3502
3504
|
}];
|
|
3503
3505
|
});
|
|
3504
3506
|
};
|
|
@@ -3887,14 +3889,15 @@ const chargedTenders = (intents) => {
|
|
|
3887
3889
|
};
|
|
3888
3890
|
/** Settled tenders plus PENDING `confirm_after_inject`, order currency. */
|
|
3889
3891
|
const coveredAmount = (intents) => {
|
|
3890
|
-
return chargedTenders(intents).reduce((sum, tender) => {
|
|
3891
|
-
return sum
|
|
3892
|
-
},
|
|
3892
|
+
return moneyNumber(chargedTenders(intents).reduce((sum, tender) => {
|
|
3893
|
+
return sum.plus(tender.amount.total);
|
|
3894
|
+
}, moneyZero()));
|
|
3893
3895
|
};
|
|
3894
3896
|
const remainingAmount = (order) => {
|
|
3895
|
-
const
|
|
3896
|
-
const left = order.totals.total
|
|
3897
|
-
|
|
3897
|
+
const covered = money(coveredAmount(order.payment_intents));
|
|
3898
|
+
const left = money(order.totals.total).minus(covered);
|
|
3899
|
+
if (left.lt(0)) return 0;
|
|
3900
|
+
return moneyNumber(left);
|
|
3898
3901
|
};
|
|
3899
3902
|
const isPlaced = (order) => order.status !== "OPEN";
|
|
3900
3903
|
/** Orchestrator failures carry the charge-currency amount, not the order's. */
|
|
@@ -4024,12 +4027,9 @@ var PaymentSession = class {
|
|
|
4024
4027
|
const conversion = await this.chargeAmount(payment, amount, server);
|
|
4025
4028
|
const config = record(payment.config);
|
|
4026
4029
|
const methodConfig = record(payment.method.config);
|
|
4027
|
-
const
|
|
4028
|
-
const
|
|
4029
|
-
const taxes = taxBreakdown(order.lines, {
|
|
4030
|
-
share,
|
|
4031
|
-
decimals
|
|
4032
|
-
});
|
|
4030
|
+
const total = order.totals.total;
|
|
4031
|
+
const share = moneyNumber(total > 0 ? money(amount).div(total) : money(1));
|
|
4032
|
+
const taxes = taxBreakdown(order.lines, { share });
|
|
4033
4033
|
const info = order.customer.customer_info;
|
|
4034
4034
|
const customer = {
|
|
4035
4035
|
document: info.document_number ?? null,
|
|
@@ -4722,17 +4722,14 @@ const emptyTotals = (currency = "USD") => {
|
|
|
4722
4722
|
//#region src/order/redemptions.ts
|
|
4723
4723
|
const ORDER_KEY = "ORDER";
|
|
4724
4724
|
const buildPaymentRedeem = (input) => {
|
|
4725
|
-
const { reward_id, quantity, payment_id, amount
|
|
4725
|
+
const { reward_id, quantity, payment_id, amount } = input;
|
|
4726
4726
|
if (amount === void 0 || !(amount > 0)) throw new Error("Redeem with payment_id requires amount > 0");
|
|
4727
|
-
if (points === void 0 || !(points >= 0)) throw new Error("Redeem with payment_id requires points ≥ 0");
|
|
4728
4727
|
const redeem = {
|
|
4729
4728
|
reward_id,
|
|
4730
4729
|
quantity,
|
|
4731
4730
|
payment_id,
|
|
4732
4731
|
amount
|
|
4733
4732
|
};
|
|
4734
|
-
redeem.points = points;
|
|
4735
|
-
if (input.program !== void 0) redeem.program = input.program;
|
|
4736
4733
|
if (input.expires_at !== void 0) redeem.expires_at = input.expires_at;
|
|
4737
4734
|
return redeem;
|
|
4738
4735
|
};
|
|
@@ -4766,12 +4763,10 @@ const keepUnits = (item, rest) => {
|
|
|
4766
4763
|
};
|
|
4767
4764
|
const ratio = new Big(rest).div(item.quantity);
|
|
4768
4765
|
const amount = new Big(item.amount ?? 0).times(ratio).round(6).toNumber();
|
|
4769
|
-
const points = new Big(item.points ?? 0).times(ratio).round(0).toNumber();
|
|
4770
4766
|
return {
|
|
4771
4767
|
...item,
|
|
4772
4768
|
quantity: rest,
|
|
4773
|
-
amount
|
|
4774
|
-
points
|
|
4769
|
+
amount
|
|
4775
4770
|
};
|
|
4776
4771
|
};
|
|
4777
4772
|
/**
|
|
@@ -5094,7 +5089,7 @@ const cloneOrder = (order) => {
|
|
|
5094
5089
|
};
|
|
5095
5090
|
const redeemSignature$1 = (items) => {
|
|
5096
5091
|
return items.map((item) => {
|
|
5097
|
-
return `${`${item.reward_id}:${item.quantity}:${item.payment_id ?? ""}`}:${`${item.amount ?? ""}
|
|
5092
|
+
return `${`${item.reward_id}:${item.quantity}:${item.payment_id ?? ""}`}:${`${item.amount ?? ""}`}:${`${item.discount_rate ?? ""}:${item.discount_amount ?? ""}`}`;
|
|
5098
5093
|
}).join("|");
|
|
5099
5094
|
};
|
|
5100
5095
|
const buildHttpOptions = (options) => {
|
|
@@ -5509,9 +5504,9 @@ var Order = class Order {
|
|
|
5509
5504
|
}
|
|
5510
5505
|
/**
|
|
5511
5506
|
* Appends a redeem to `line_uid` (root line) or to the order. Steps keep
|
|
5512
|
-
* the order in which redeems were applied. With `payment_id` (
|
|
5513
|
-
* balance) it is not a discount: `amount`
|
|
5514
|
-
*
|
|
5507
|
+
* the order in which redeems were applied. With `payment_id` (voucher,
|
|
5508
|
+
* balance…) it is not a discount: `amount` is the total of the whole
|
|
5509
|
+
* entry (every unit, not per unit) and `quantity` is informational.
|
|
5515
5510
|
* Every redeem with the same `payment_id` is paid by one REDEMPTION
|
|
5516
5511
|
* tender whose `details.rewards[]` lists them; their summed amount is
|
|
5517
5512
|
* capped at total − tip − delivery (D15, D18, D19).
|
|
@@ -5520,7 +5515,7 @@ var Order = class Order {
|
|
|
5520
5515
|
if (this.deleted) return;
|
|
5521
5516
|
const { line_uid, provider, ...rest } = input;
|
|
5522
5517
|
const redeem = buildRedeem(rest);
|
|
5523
|
-
if (isPaymentRedeem(redeem) && line_uid !== void 0) throw new Error("
|
|
5518
|
+
if (isPaymentRedeem(redeem) && line_uid !== void 0) throw new Error("Payment redeems apply to the whole order");
|
|
5524
5519
|
if (redeem.payment_id && provider) this.redemptionProviders.set(redeem.payment_id, provider);
|
|
5525
5520
|
if (line_uid === void 0) {
|
|
5526
5521
|
this.commitLines({ redeem: [...this.appliedRedeem(), redeem] });
|
|
@@ -5540,9 +5535,8 @@ var Order = class Order {
|
|
|
5540
5535
|
/**
|
|
5541
5536
|
* Removes `quantity` units of `reward_id` from `line_uid` or the order,
|
|
5542
5537
|
* latest applied first. Removing more units than applied clears them all
|
|
5543
|
-
* (and the REDEMPTION tender of a
|
|
5544
|
-
*
|
|
5545
|
-
* `amount` and `points`.
|
|
5538
|
+
* (and the REDEMPTION tender of a payment redeem). A payment redeem that
|
|
5539
|
+
* keeps some units keeps the same share of its `amount`.
|
|
5546
5540
|
*/
|
|
5547
5541
|
removeRedeem(input) {
|
|
5548
5542
|
if (this.deleted) return;
|
|
@@ -5649,7 +5643,7 @@ var Order = class Order {
|
|
|
5649
5643
|
}
|
|
5650
5644
|
/**
|
|
5651
5645
|
* Reopens this placed, unpaid order: the server voids every payment
|
|
5652
|
-
* (REDEMPTION
|
|
5646
|
+
* (REDEMPTION rewards released), cancels it as `VOIDED` and opens a new
|
|
5653
5647
|
* `OPEN` order with the same content. This instance is closed afterwards;
|
|
5654
5648
|
* replace it with the returned `Order` (same HTTP options and listener).
|
|
5655
5649
|
*
|
|
@@ -6968,391 +6962,8 @@ const verifyWebhookSignature = async (input) => {
|
|
|
6968
6962
|
return safeEqual(received, await hmacHex(secret, body));
|
|
6969
6963
|
};
|
|
6970
6964
|
//#endregion
|
|
6971
|
-
//#region
|
|
6972
|
-
|
|
6973
|
-
if (value) return;
|
|
6974
|
-
throw new Error(message);
|
|
6975
|
-
};
|
|
6976
|
-
//#endregion
|
|
6977
|
-
//#region src/rules/common.ts
|
|
6978
|
-
const primitiveError = (primitive) => {
|
|
6979
|
-
return `Condition primitive must be ${primitive}`;
|
|
6980
|
-
};
|
|
6981
|
-
const typeError = (type) => {
|
|
6982
|
-
return `Condition type must be ${type}`;
|
|
6983
|
-
};
|
|
6984
|
-
const defaultConditionError = "Condition was not met";
|
|
6985
|
-
const getLineByProductId = (lines, productId) => {
|
|
6986
|
-
if (!productId) return void 0;
|
|
6987
|
-
return lines?.find((line) => line.id === productId);
|
|
6988
|
-
};
|
|
6989
|
-
const countCartProducts = (lines, includeModifiers) => {
|
|
6990
|
-
if (!lines?.length) return 0;
|
|
6991
|
-
return lines.filter((line) => {
|
|
6992
|
-
if (line.type === "FEE") return false;
|
|
6993
|
-
if (includeModifiers) return true;
|
|
6994
|
-
if (!(line.parent_uid == null)) return false;
|
|
6995
|
-
return line.type === "PRODUCT" || line.type == null;
|
|
6996
|
-
}).reduce((sum, line) => sum + line.quantity, 0);
|
|
6997
|
-
};
|
|
6998
|
-
const getLineTagValues = (lines, tag) => {
|
|
6999
|
-
if (!tag) return [];
|
|
7000
|
-
const values = [];
|
|
7001
|
-
for (const line of lines ?? []) {
|
|
7002
|
-
const value = line.tags?.[tag];
|
|
7003
|
-
if (typeof value === "undefined") continue;
|
|
7004
|
-
values.push(value);
|
|
7005
|
-
}
|
|
7006
|
-
return values;
|
|
7007
|
-
};
|
|
7008
|
-
//#endregion
|
|
7009
|
-
//#region src/rules/booleanMatcher.ts
|
|
7010
|
-
const matchBooleanOperator = (condition, matcher) => {
|
|
7011
|
-
const { primitive, operator, value } = condition;
|
|
7012
|
-
assert(primitive === "boolean", primitiveError("boolean"));
|
|
7013
|
-
if (typeof matcher === "undefined") return false;
|
|
7014
|
-
assert(typeof value === "boolean", "Invalid boolean value");
|
|
7015
|
-
switch (operator) {
|
|
7016
|
-
case "is": return matcher === value;
|
|
7017
|
-
case "is not": return matcher !== value;
|
|
7018
|
-
default: throw new Error(`Operator ${operator} not supported in boolean matcher`);
|
|
7019
|
-
}
|
|
7020
|
-
};
|
|
7021
|
-
const isAnonymousCustomer = (condition, isAnonymous) => {
|
|
7022
|
-
const { type, primitive, value } = condition;
|
|
7023
|
-
const matcherType = "Customer is anonymous";
|
|
7024
|
-
assert(primitive === "boolean", primitiveError("boolean"));
|
|
7025
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7026
|
-
assert(value != null, "Invalid value");
|
|
7027
|
-
return matchBooleanOperator(condition, isAnonymous ?? false);
|
|
7028
|
-
};
|
|
7029
|
-
const orderIncludesProductWithTag$3 = (condition, matcher) => {
|
|
7030
|
-
const { type, primitive, value } = condition;
|
|
7031
|
-
const matcherType = "Order includes product with tag";
|
|
7032
|
-
assert(primitive === "boolean", primitiveError("boolean"));
|
|
7033
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7034
|
-
assert(value != null, "Invalid value");
|
|
7035
|
-
return matchBooleanOperator(condition, matcher);
|
|
7036
|
-
};
|
|
7037
|
-
const isAnonymousOrder = (condition, isAnonymous) => {
|
|
7038
|
-
const { type, primitive, value } = condition;
|
|
7039
|
-
const matcherType = "Is anonymous order";
|
|
7040
|
-
assert(primitive === "boolean", primitiveError("boolean"));
|
|
7041
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7042
|
-
assert(value != null, "Invalid value");
|
|
7043
|
-
return matchBooleanOperator(condition, isAnonymous ?? false);
|
|
7044
|
-
};
|
|
7045
|
-
const paymentIsMixed = (condition, isMixed) => {
|
|
7046
|
-
const { type, primitive, value } = condition;
|
|
7047
|
-
const matcherType = "Payment is mixed";
|
|
7048
|
-
assert(primitive === "boolean", primitiveError("boolean"));
|
|
7049
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7050
|
-
assert(value != null, "Invalid value");
|
|
7051
|
-
return matchBooleanOperator(condition, isMixed ?? false);
|
|
7052
|
-
};
|
|
7053
|
-
const paymentCaptured = (condition, captured) => {
|
|
7054
|
-
const { type, primitive, value } = condition;
|
|
7055
|
-
const matcherType = "Payment captured";
|
|
7056
|
-
assert(primitive === "boolean", primitiveError("boolean"));
|
|
7057
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7058
|
-
assert(value != null, "Invalid value");
|
|
7059
|
-
return matchBooleanOperator(condition, captured ?? false);
|
|
7060
|
-
};
|
|
7061
|
-
//#endregion
|
|
7062
|
-
//#region src/rules/currencyMatcher.ts
|
|
7063
|
-
const matchCurrencyOperator = (condition, matcher) => {
|
|
7064
|
-
const { primitive, operator, value } = condition;
|
|
7065
|
-
assert(primitive === "currency", primitiveError("currency"));
|
|
7066
|
-
if (typeof matcher === "undefined") return false;
|
|
7067
|
-
assert(typeof value === "number", "Invalid currency value");
|
|
7068
|
-
switch (operator) {
|
|
7069
|
-
case "equal to": return matcher === value;
|
|
7070
|
-
case "not equal to": return matcher !== value;
|
|
7071
|
-
case "greater than": return matcher > value;
|
|
7072
|
-
case "greater than or equal to": return matcher >= value;
|
|
7073
|
-
case "less than": return matcher < value;
|
|
7074
|
-
case "less than or equal to": return matcher <= value;
|
|
7075
|
-
default: throw new Error(`Operator ${operator} not supported in currency matcher`);
|
|
7076
|
-
}
|
|
7077
|
-
};
|
|
7078
|
-
const orderIncludesProductWithTag$2 = (condition, matcher) => {
|
|
7079
|
-
const { type, primitive, value } = condition;
|
|
7080
|
-
const matcherType = "Order includes product with tag";
|
|
7081
|
-
assert(primitive === "currency", primitiveError("currency"));
|
|
7082
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7083
|
-
assert(value != null, "Invalid value");
|
|
7084
|
-
return matchCurrencyOperator(condition, matcher);
|
|
7085
|
-
};
|
|
7086
|
-
const orderTotal = (condition, total) => {
|
|
7087
|
-
const { type, primitive, value } = condition;
|
|
7088
|
-
const matcherType = "Order total";
|
|
7089
|
-
assert(primitive === "currency", primitiveError("currency"));
|
|
7090
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7091
|
-
assert(value != null, "Invalid value");
|
|
7092
|
-
return matchCurrencyOperator(condition, total);
|
|
7093
|
-
};
|
|
7094
|
-
//#endregion
|
|
7095
|
-
//#region src/rules/customMatcher.ts
|
|
7096
|
-
const MULTI_OPERATORS = [
|
|
7097
|
-
"is any of",
|
|
7098
|
-
"is none of",
|
|
7099
|
-
"is all of"
|
|
7100
|
-
];
|
|
7101
|
-
const isMultiOperator = (operator) => {
|
|
7102
|
-
return MULTI_OPERATORS.includes(operator);
|
|
7103
|
-
};
|
|
7104
|
-
const splitCsv = (value) => {
|
|
7105
|
-
return value.split(",").map((part) => part.trim()).filter(Boolean);
|
|
7106
|
-
};
|
|
7107
|
-
const matchCustomEquality = (condition, matcher) => {
|
|
7108
|
-
const { primitive, operator, value } = condition;
|
|
7109
|
-
assert(primitive === "custom", primitiveError("custom"));
|
|
7110
|
-
if (typeof matcher === "undefined") return false;
|
|
7111
|
-
assert(typeof value === "string", "Invalid custom value");
|
|
7112
|
-
switch (operator) {
|
|
7113
|
-
case "is": return matcher === value;
|
|
7114
|
-
case "is not": return matcher !== value;
|
|
7115
|
-
default: throw new Error(`Operator ${operator} not supported in custom matcher`);
|
|
7116
|
-
}
|
|
7117
|
-
};
|
|
7118
|
-
const matchCustomMulti = (condition, matcher) => {
|
|
7119
|
-
const { primitive, operator, value } = condition;
|
|
7120
|
-
assert(primitive === "custom", primitiveError("custom"));
|
|
7121
|
-
if (typeof matcher === "undefined") return false;
|
|
7122
|
-
assert(typeof value === "string", "Invalid custom value");
|
|
7123
|
-
const allowed = splitCsv(value);
|
|
7124
|
-
if (allowed.length === 0) return false;
|
|
7125
|
-
const candidates = typeof matcher === "object" ? matcher : [matcher];
|
|
7126
|
-
if (candidates.length === 0) return false;
|
|
7127
|
-
if (operator === "is any of") return allowed.some((id) => candidates.includes(id));
|
|
7128
|
-
if (operator === "is none of") return !allowed.some((id) => candidates.includes(id));
|
|
7129
|
-
if (operator === "is all of") return allowed.every((id) => candidates.includes(id));
|
|
7130
|
-
throw new Error(`Operator ${operator} not supported in custom matcher`);
|
|
7131
|
-
};
|
|
7132
|
-
/**
|
|
7133
|
-
* The operator picks the arity: `Payment` is single-select in order rules and
|
|
7134
|
-
* multi-select in fulfillment workflows. `matcher` may be a set (one id per
|
|
7135
|
-
* tender): `is` matches when some id matches, `is not` when none does.
|
|
7136
|
-
*/
|
|
7137
|
-
const matchCustom = (condition, matcher) => {
|
|
7138
|
-
if (isMultiOperator(condition.operator)) return matchCustomMulti(condition, matcher);
|
|
7139
|
-
if (typeof matcher === "undefined" || typeof matcher === "string") return matchCustomEquality(condition, matcher);
|
|
7140
|
-
if (matcher.length === 0) return false;
|
|
7141
|
-
const matches = (id) => matchCustomEquality(condition, id);
|
|
7142
|
-
if (condition.operator === "is not") return matcher.every(matches);
|
|
7143
|
-
return matcher.some(matches);
|
|
7144
|
-
};
|
|
7145
|
-
const assertCustom = (condition, order) => {
|
|
7146
|
-
const { type } = condition;
|
|
7147
|
-
switch (type) {
|
|
7148
|
-
case "Fulfillment":
|
|
7149
|
-
case "Fulfillments": return matchCustom(condition, order.fulfillment_id);
|
|
7150
|
-
case "Store":
|
|
7151
|
-
case "Stores": return matchCustom(condition, order.store_id);
|
|
7152
|
-
case "Channel":
|
|
7153
|
-
case "Channels": return matchCustom(condition, order.channel_id);
|
|
7154
|
-
case "Brand":
|
|
7155
|
-
case "Brands": return matchCustom(condition, order.brand_id);
|
|
7156
|
-
default: throw new Error(`Condition ${type} not supported in custom matcher`);
|
|
7157
|
-
}
|
|
7158
|
-
};
|
|
7159
|
-
//#endregion
|
|
7160
|
-
//#region src/rules/enumMatcher.ts
|
|
7161
|
-
const matchEnumOperator = (condition, matcher) => {
|
|
7162
|
-
const { primitive, operator, value } = condition;
|
|
7163
|
-
assert(primitive === "enum", primitiveError("enum"));
|
|
7164
|
-
if (typeof matcher === "undefined") return false;
|
|
7165
|
-
switch (operator) {
|
|
7166
|
-
case "is":
|
|
7167
|
-
assert(typeof value === "string", "Expected a single enum value");
|
|
7168
|
-
return matcher === value;
|
|
7169
|
-
case "is not":
|
|
7170
|
-
assert(typeof value === "string", "Expected a single enum value");
|
|
7171
|
-
return matcher !== value;
|
|
7172
|
-
case "is any of":
|
|
7173
|
-
assert(Array.isArray(value), "Expected a list of enum values");
|
|
7174
|
-
return value.includes(matcher);
|
|
7175
|
-
case "is none of":
|
|
7176
|
-
assert(Array.isArray(value), "Expected a list of enum values");
|
|
7177
|
-
return !value.includes(matcher);
|
|
7178
|
-
default: throw new Error(`Operator ${operator} not supported in enum matcher`);
|
|
7179
|
-
}
|
|
7180
|
-
};
|
|
7181
|
-
const NEGATIVE_ENUM_OPERATORS = ["is not", "is none of"];
|
|
7182
|
-
/**
|
|
7183
|
-
* Enum condition over a set (one value per tender): positive operators
|
|
7184
|
-
* match when some value matches, negative ones when none matches.
|
|
7185
|
-
*/
|
|
7186
|
-
const matchEnumSet = (condition, matcher) => {
|
|
7187
|
-
if (typeof matcher === "undefined" || typeof matcher === "string") return matchEnumOperator(condition, matcher);
|
|
7188
|
-
if (matcher.length === 0) return false;
|
|
7189
|
-
const matches = (value) => matchEnumOperator(condition, value);
|
|
7190
|
-
if (NEGATIVE_ENUM_OPERATORS.includes(condition.operator)) return matcher.every(matches);
|
|
7191
|
-
return matcher.some(matches);
|
|
7192
|
-
};
|
|
7193
|
-
const paymentCollectAt = (condition, collectAt) => {
|
|
7194
|
-
const matcherType = "Payment collect at";
|
|
7195
|
-
assert(condition.primitive === "enum", primitiveError("enum"));
|
|
7196
|
-
assert(condition.type === matcherType, typeError(matcherType));
|
|
7197
|
-
return matchEnumSet(condition, collectAt);
|
|
7198
|
-
};
|
|
7199
|
-
const paymentMethod = (condition, method) => {
|
|
7200
|
-
const matcherType = "Payment method";
|
|
7201
|
-
assert(condition.primitive === "enum", primitiveError("enum"));
|
|
7202
|
-
assert(condition.type === matcherType, typeError(matcherType));
|
|
7203
|
-
return matchEnumSet(condition, method);
|
|
7204
|
-
};
|
|
7205
|
-
const paymentStatus = (condition, status) => {
|
|
7206
|
-
const matcherType = "Payment status";
|
|
7207
|
-
assert(condition.primitive === "enum", primitiveError("enum"));
|
|
7208
|
-
assert(condition.type === matcherType, typeError(matcherType));
|
|
7209
|
-
return matchEnumOperator(condition, status);
|
|
7210
|
-
};
|
|
7211
|
-
const CONDITION_INTERPRETATION_DEFAULT = "server";
|
|
7212
|
-
const isInterpretationCompatible = (interpretation, runtime, fallback) => {
|
|
7213
|
-
if (!runtime) return true;
|
|
7214
|
-
const value = interpretation ?? fallback;
|
|
7215
|
-
if (value === "both") return true;
|
|
7216
|
-
return value === runtime;
|
|
7217
|
-
};
|
|
7218
|
-
//#endregion
|
|
7219
|
-
//#region src/rules/numberMatcher.ts
|
|
7220
|
-
const matchNumberOperator = (condition, matcher) => {
|
|
7221
|
-
const { primitive, operator, value } = condition;
|
|
7222
|
-
assert(primitive === "number", primitiveError("number"));
|
|
7223
|
-
if (typeof matcher === "undefined") return false;
|
|
7224
|
-
assert(typeof value === "number", "Invalid number value");
|
|
7225
|
-
switch (operator) {
|
|
7226
|
-
case "equal to": return matcher === value;
|
|
7227
|
-
case "not equal to": return matcher !== value;
|
|
7228
|
-
case "greater than": return matcher > value;
|
|
7229
|
-
case "greater than or equal to": return matcher >= value;
|
|
7230
|
-
case "less than": return matcher < value;
|
|
7231
|
-
case "less than or equal to": return matcher <= value;
|
|
7232
|
-
default: throw new Error(`Operator ${operator} not supported in number matcher`);
|
|
7233
|
-
}
|
|
7234
|
-
};
|
|
7235
|
-
const orderIncludesProduct = (condition, quantity) => {
|
|
7236
|
-
const { type, primitive, value } = condition;
|
|
7237
|
-
const matcherType = "Order includes product";
|
|
7238
|
-
assert(primitive === "number", primitiveError("number"));
|
|
7239
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7240
|
-
assert(value != null, "Invalid value");
|
|
7241
|
-
return matchNumberOperator(condition, quantity ?? 0);
|
|
7242
|
-
};
|
|
7243
|
-
const cartProductCount = (condition, count) => {
|
|
7244
|
-
const { type, primitive, value } = condition;
|
|
7245
|
-
const matcherType = "Cart product count";
|
|
7246
|
-
assert(primitive === "number", primitiveError("number"));
|
|
7247
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7248
|
-
assert(value != null, "Invalid value");
|
|
7249
|
-
return matchNumberOperator(condition, count);
|
|
7250
|
-
};
|
|
7251
|
-
const orderIncludesProductWithTag$1 = (condition, matcher) => {
|
|
7252
|
-
const { type, primitive, value } = condition;
|
|
7253
|
-
const matcherType = "Order includes product with tag";
|
|
7254
|
-
assert(primitive === "number", primitiveError("number"));
|
|
7255
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7256
|
-
assert(value != null, "Invalid value");
|
|
7257
|
-
return matchNumberOperator(condition, matcher);
|
|
7258
|
-
};
|
|
7259
|
-
//#endregion
|
|
7260
|
-
//#region src/rules/stringCondition.utils.ts
|
|
7261
|
-
const getCaseInsensitive = (condition) => {
|
|
7262
|
-
return condition.caseInsensitive ?? false;
|
|
7263
|
-
};
|
|
7264
|
-
const normalizeStringForMatch = (value, caseInsensitive) => {
|
|
7265
|
-
if (caseInsensitive) return value.toLowerCase();
|
|
7266
|
-
return value;
|
|
7267
|
-
};
|
|
7268
|
-
const normalizePairForMatch = (matcher, expected, caseInsensitive) => {
|
|
7269
|
-
return {
|
|
7270
|
-
matcher: normalizeStringForMatch(matcher, caseInsensitive),
|
|
7271
|
-
expected: normalizeStringForMatch(expected, caseInsensitive)
|
|
7272
|
-
};
|
|
7273
|
-
};
|
|
7274
|
-
//#endregion
|
|
7275
|
-
//#region src/rules/stringMatcher.ts
|
|
7276
|
-
const matchStringOperator = (condition, matcher) => {
|
|
7277
|
-
const { primitive, operator, value } = condition;
|
|
7278
|
-
assert(primitive === "string", primitiveError("string"));
|
|
7279
|
-
if (typeof matcher === "undefined") return false;
|
|
7280
|
-
const caseInsensitive = getCaseInsensitive(condition);
|
|
7281
|
-
const pair = normalizePairForMatch(matcher, String(value), caseInsensitive);
|
|
7282
|
-
const normalizedMatcher = pair.matcher;
|
|
7283
|
-
const normalizedExpected = pair.expected;
|
|
7284
|
-
switch (operator) {
|
|
7285
|
-
case "equal to": return normalizedMatcher === normalizedExpected;
|
|
7286
|
-
case "not equal to": return normalizedMatcher !== normalizedExpected;
|
|
7287
|
-
case "starts with": return normalizedMatcher.startsWith(normalizedExpected);
|
|
7288
|
-
case "ends with": return normalizedMatcher.endsWith(normalizedExpected);
|
|
7289
|
-
case "contains": return normalizedMatcher.includes(normalizedExpected);
|
|
7290
|
-
default: throw new Error(`Operator ${operator} not supported in string matcher`);
|
|
7291
|
-
}
|
|
7292
|
-
};
|
|
7293
|
-
const orderIncludesProductWithTag = (condition, matcher) => {
|
|
7294
|
-
const { type, primitive, value } = condition;
|
|
7295
|
-
const matcherType = "Order includes product with tag";
|
|
7296
|
-
assert(primitive === "string", primitiveError("string"));
|
|
7297
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7298
|
-
assert(value != null, "Invalid value");
|
|
7299
|
-
return matchStringOperator(condition, matcher);
|
|
7300
|
-
};
|
|
7301
|
-
//#endregion
|
|
7302
|
-
//#region src/rules/monitoringMatcher.ts
|
|
7303
|
-
const windowOf = (condition) => {
|
|
7304
|
-
if (!("window_minutes" in condition)) return void 0;
|
|
7305
|
-
return condition.window_minutes;
|
|
7306
|
-
};
|
|
7307
|
-
const accumulatedEvents = (condition, count) => {
|
|
7308
|
-
const { type, primitive, value } = condition;
|
|
7309
|
-
const matcherType = "Accumulated events";
|
|
7310
|
-
assert(primitive === "number", primitiveError("number"));
|
|
7311
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7312
|
-
assert(value != null, "Invalid value");
|
|
7313
|
-
if (typeof count === "undefined") return false;
|
|
7314
|
-
return matchNumberOperator(condition, count);
|
|
7315
|
-
};
|
|
7316
|
-
const apiErrorRate = (condition, rate) => {
|
|
7317
|
-
const { type, primitive, value } = condition;
|
|
7318
|
-
const matcherType = "API error rate";
|
|
7319
|
-
assert(primitive === "number", primitiveError("number"));
|
|
7320
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7321
|
-
assert(value != null, "Invalid value");
|
|
7322
|
-
if (typeof rate === "undefined") return false;
|
|
7323
|
-
return matchNumberOperator(condition, rate);
|
|
7324
|
-
};
|
|
7325
|
-
const httpStatus = (condition, status) => {
|
|
7326
|
-
const { type, primitive, value } = condition;
|
|
7327
|
-
const matcherType = "HTTP status";
|
|
7328
|
-
assert(primitive === "number", primitiveError("number"));
|
|
7329
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7330
|
-
assert(value != null, "Invalid value");
|
|
7331
|
-
if (typeof status === "undefined") return false;
|
|
7332
|
-
return matchNumberOperator(condition, status);
|
|
7333
|
-
};
|
|
7334
|
-
const requestPath = (condition, path) => {
|
|
7335
|
-
const { type, primitive, value } = condition;
|
|
7336
|
-
const matcherType = "Request path";
|
|
7337
|
-
assert(primitive === "string", primitiveError("string"));
|
|
7338
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7339
|
-
assert(value != null, "Invalid value");
|
|
7340
|
-
return matchStringOperator(condition, path);
|
|
7341
|
-
};
|
|
7342
|
-
const failureMessage = (condition, message) => {
|
|
7343
|
-
const { type, primitive, value } = condition;
|
|
7344
|
-
const matcherType = "Failure message";
|
|
7345
|
-
assert(primitive === "string", primitiveError("string"));
|
|
7346
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7347
|
-
assert(value != null, "Invalid value");
|
|
7348
|
-
return matchStringOperator(condition, message);
|
|
7349
|
-
};
|
|
7350
|
-
const windowMinutes = (condition) => {
|
|
7351
|
-
return windowOf(condition);
|
|
7352
|
-
};
|
|
7353
|
-
//#endregion
|
|
7354
|
-
//#region src/rules/recurringDateCron.ts
|
|
7355
|
-
var import_dayjs_min = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
6965
|
+
//#region ../../node_modules/.pnpm/dayjs@1.11.9/node_modules/dayjs/dayjs.min.js
|
|
6966
|
+
var require_dayjs_min = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
7356
6967
|
(function(t, e) {
|
|
7357
6968
|
"object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).dayjs = e();
|
|
7358
6969
|
})(exports, (function() {
|
|
@@ -7647,7 +7258,412 @@ var import_dayjs_min = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((
|
|
|
7647
7258
|
return w(1e3 * t);
|
|
7648
7259
|
}, w.en = D[g], w.Ls = D, w.p = {}, w;
|
|
7649
7260
|
}));
|
|
7650
|
-
}))
|
|
7261
|
+
}));
|
|
7262
|
+
//#endregion
|
|
7263
|
+
//#region src/rules/assert.ts
|
|
7264
|
+
const assert = (value, message) => {
|
|
7265
|
+
if (value) return;
|
|
7266
|
+
throw new Error(message);
|
|
7267
|
+
};
|
|
7268
|
+
//#endregion
|
|
7269
|
+
//#region src/rules/common.ts
|
|
7270
|
+
const primitiveError = (primitive) => {
|
|
7271
|
+
return `Condition primitive must be ${primitive}`;
|
|
7272
|
+
};
|
|
7273
|
+
const typeError = (type) => {
|
|
7274
|
+
return `Condition type must be ${type}`;
|
|
7275
|
+
};
|
|
7276
|
+
const defaultConditionError = "Condition was not met";
|
|
7277
|
+
const getLineByProductId = (lines, productId) => {
|
|
7278
|
+
if (!productId) return void 0;
|
|
7279
|
+
return lines?.find((line) => line.id === productId);
|
|
7280
|
+
};
|
|
7281
|
+
const countCartProducts = (lines, includeModifiers) => {
|
|
7282
|
+
if (!lines?.length) return 0;
|
|
7283
|
+
return lines.filter((line) => {
|
|
7284
|
+
if (line.type === "FEE") return false;
|
|
7285
|
+
if (includeModifiers) return true;
|
|
7286
|
+
if (!(line.parent_uid == null)) return false;
|
|
7287
|
+
return line.type === "PRODUCT" || line.type == null;
|
|
7288
|
+
}).reduce((sum, line) => sum + line.quantity, 0);
|
|
7289
|
+
};
|
|
7290
|
+
const getLineTagValues = (lines, tag) => {
|
|
7291
|
+
if (!tag) return [];
|
|
7292
|
+
const values = [];
|
|
7293
|
+
for (const line of lines ?? []) {
|
|
7294
|
+
const value = line.tags?.[tag];
|
|
7295
|
+
if (typeof value === "undefined") continue;
|
|
7296
|
+
values.push(value);
|
|
7297
|
+
}
|
|
7298
|
+
return values;
|
|
7299
|
+
};
|
|
7300
|
+
//#endregion
|
|
7301
|
+
//#region src/rules/absoluteDateMatcher.ts
|
|
7302
|
+
var import_dayjs_min = /* @__PURE__ */ __toESM(require_dayjs_min(), 1);
|
|
7303
|
+
const matchAbsoluteDateOperator = (condition, matcher) => {
|
|
7304
|
+
const { primitive, operator, value } = condition;
|
|
7305
|
+
assert(primitive === "absolute date", primitiveError("absolute date"));
|
|
7306
|
+
if (typeof matcher === "undefined") return false;
|
|
7307
|
+
const matcherDate = (0, import_dayjs_min.default)(matcher);
|
|
7308
|
+
const expectedDate = (0, import_dayjs_min.default)(value);
|
|
7309
|
+
switch (operator) {
|
|
7310
|
+
case "is": return matcherDate.isSame(expectedDate, "day");
|
|
7311
|
+
case "is not": return !matcherDate.isSame(expectedDate, "day");
|
|
7312
|
+
case "is before": return matcherDate.isBefore(expectedDate, "day");
|
|
7313
|
+
case "is after": return matcherDate.isAfter(expectedDate, "day");
|
|
7314
|
+
default: {
|
|
7315
|
+
const msg = `Operator ${operator} not supported in absolute date matcher`;
|
|
7316
|
+
throw new Error(msg);
|
|
7317
|
+
}
|
|
7318
|
+
}
|
|
7319
|
+
};
|
|
7320
|
+
//#endregion
|
|
7321
|
+
//#region src/rules/booleanMatcher.ts
|
|
7322
|
+
const matchBooleanOperator = (condition, matcher) => {
|
|
7323
|
+
const { primitive, operator, value } = condition;
|
|
7324
|
+
assert(primitive === "boolean", primitiveError("boolean"));
|
|
7325
|
+
if (typeof matcher === "undefined") return false;
|
|
7326
|
+
assert(typeof value === "boolean", "Invalid boolean value");
|
|
7327
|
+
switch (operator) {
|
|
7328
|
+
case "is": return matcher === value;
|
|
7329
|
+
case "is not": return matcher !== value;
|
|
7330
|
+
default: throw new Error(`Operator ${operator} not supported in boolean matcher`);
|
|
7331
|
+
}
|
|
7332
|
+
};
|
|
7333
|
+
const isAnonymousCustomer = (condition, isAnonymous) => {
|
|
7334
|
+
const { type, primitive, value } = condition;
|
|
7335
|
+
const matcherType = "Customer is anonymous";
|
|
7336
|
+
assert(primitive === "boolean", primitiveError("boolean"));
|
|
7337
|
+
assert(type === matcherType, typeError(matcherType));
|
|
7338
|
+
assert(value != null, "Invalid value");
|
|
7339
|
+
return matchBooleanOperator(condition, isAnonymous ?? false);
|
|
7340
|
+
};
|
|
7341
|
+
const orderIncludesProductWithTag$3 = (condition, matcher) => {
|
|
7342
|
+
const { type, primitive, value } = condition;
|
|
7343
|
+
const matcherType = "Order includes product with tag";
|
|
7344
|
+
assert(primitive === "boolean", primitiveError("boolean"));
|
|
7345
|
+
assert(type === matcherType, typeError(matcherType));
|
|
7346
|
+
assert(value != null, "Invalid value");
|
|
7347
|
+
return matchBooleanOperator(condition, matcher);
|
|
7348
|
+
};
|
|
7349
|
+
const isAnonymousOrder = (condition, isAnonymous) => {
|
|
7350
|
+
const { type, primitive, value } = condition;
|
|
7351
|
+
const matcherType = "Is anonymous order";
|
|
7352
|
+
assert(primitive === "boolean", primitiveError("boolean"));
|
|
7353
|
+
assert(type === matcherType, typeError(matcherType));
|
|
7354
|
+
assert(value != null, "Invalid value");
|
|
7355
|
+
return matchBooleanOperator(condition, isAnonymous ?? false);
|
|
7356
|
+
};
|
|
7357
|
+
const paymentIsMixed = (condition, isMixed) => {
|
|
7358
|
+
const { type, primitive, value } = condition;
|
|
7359
|
+
const matcherType = "Payment is mixed";
|
|
7360
|
+
assert(primitive === "boolean", primitiveError("boolean"));
|
|
7361
|
+
assert(type === matcherType, typeError(matcherType));
|
|
7362
|
+
assert(value != null, "Invalid value");
|
|
7363
|
+
return matchBooleanOperator(condition, isMixed ?? false);
|
|
7364
|
+
};
|
|
7365
|
+
const paymentCaptured = (condition, captured) => {
|
|
7366
|
+
const { type, primitive, value } = condition;
|
|
7367
|
+
const matcherType = "Payment captured";
|
|
7368
|
+
assert(primitive === "boolean", primitiveError("boolean"));
|
|
7369
|
+
assert(type === matcherType, typeError(matcherType));
|
|
7370
|
+
assert(value != null, "Invalid value");
|
|
7371
|
+
return matchBooleanOperator(condition, captured ?? false);
|
|
7372
|
+
};
|
|
7373
|
+
//#endregion
|
|
7374
|
+
//#region src/rules/currencyMatcher.ts
|
|
7375
|
+
const matchCurrencyOperator = (condition, matcher) => {
|
|
7376
|
+
const { primitive, operator, value } = condition;
|
|
7377
|
+
assert(primitive === "currency", primitiveError("currency"));
|
|
7378
|
+
if (typeof matcher === "undefined") return false;
|
|
7379
|
+
assert(typeof value === "number", "Invalid currency value");
|
|
7380
|
+
switch (operator) {
|
|
7381
|
+
case "equal to": return matcher === value;
|
|
7382
|
+
case "not equal to": return matcher !== value;
|
|
7383
|
+
case "greater than": return matcher > value;
|
|
7384
|
+
case "greater than or equal to": return matcher >= value;
|
|
7385
|
+
case "less than": return matcher < value;
|
|
7386
|
+
case "less than or equal to": return matcher <= value;
|
|
7387
|
+
default: throw new Error(`Operator ${operator} not supported in currency matcher`);
|
|
7388
|
+
}
|
|
7389
|
+
};
|
|
7390
|
+
const orderIncludesProductWithTag$2 = (condition, matcher) => {
|
|
7391
|
+
const { type, primitive, value } = condition;
|
|
7392
|
+
const matcherType = "Order includes product with tag";
|
|
7393
|
+
assert(primitive === "currency", primitiveError("currency"));
|
|
7394
|
+
assert(type === matcherType, typeError(matcherType));
|
|
7395
|
+
assert(value != null, "Invalid value");
|
|
7396
|
+
return matchCurrencyOperator(condition, matcher);
|
|
7397
|
+
};
|
|
7398
|
+
const orderTotal = (condition, total) => {
|
|
7399
|
+
const { type, primitive, value } = condition;
|
|
7400
|
+
const matcherType = "Order total";
|
|
7401
|
+
assert(primitive === "currency", primitiveError("currency"));
|
|
7402
|
+
assert(type === matcherType, typeError(matcherType));
|
|
7403
|
+
assert(value != null, "Invalid value");
|
|
7404
|
+
return matchCurrencyOperator(condition, total);
|
|
7405
|
+
};
|
|
7406
|
+
//#endregion
|
|
7407
|
+
//#region src/rules/customMatcher.ts
|
|
7408
|
+
const MULTI_OPERATORS = [
|
|
7409
|
+
"is any of",
|
|
7410
|
+
"is none of",
|
|
7411
|
+
"is all of"
|
|
7412
|
+
];
|
|
7413
|
+
const isMultiOperator = (operator) => {
|
|
7414
|
+
return MULTI_OPERATORS.includes(operator);
|
|
7415
|
+
};
|
|
7416
|
+
const splitCsv = (value) => {
|
|
7417
|
+
return value.split(",").map((part) => part.trim()).filter(Boolean);
|
|
7418
|
+
};
|
|
7419
|
+
const matchCustomEquality = (condition, matcher) => {
|
|
7420
|
+
const { primitive, operator, value } = condition;
|
|
7421
|
+
assert(primitive === "custom", primitiveError("custom"));
|
|
7422
|
+
if (typeof matcher === "undefined") return false;
|
|
7423
|
+
assert(typeof value === "string", "Invalid custom value");
|
|
7424
|
+
switch (operator) {
|
|
7425
|
+
case "is": return matcher === value;
|
|
7426
|
+
case "is not": return matcher !== value;
|
|
7427
|
+
default: throw new Error(`Operator ${operator} not supported in custom matcher`);
|
|
7428
|
+
}
|
|
7429
|
+
};
|
|
7430
|
+
const matchCustomMulti = (condition, matcher) => {
|
|
7431
|
+
const { primitive, operator, value } = condition;
|
|
7432
|
+
assert(primitive === "custom", primitiveError("custom"));
|
|
7433
|
+
if (typeof matcher === "undefined") return false;
|
|
7434
|
+
assert(typeof value === "string", "Invalid custom value");
|
|
7435
|
+
const allowed = splitCsv(value);
|
|
7436
|
+
if (allowed.length === 0) return false;
|
|
7437
|
+
const candidates = typeof matcher === "object" ? matcher : [matcher];
|
|
7438
|
+
if (candidates.length === 0) return false;
|
|
7439
|
+
if (operator === "is any of") return allowed.some((id) => candidates.includes(id));
|
|
7440
|
+
if (operator === "is none of") return !allowed.some((id) => candidates.includes(id));
|
|
7441
|
+
if (operator === "is all of") return allowed.every((id) => candidates.includes(id));
|
|
7442
|
+
throw new Error(`Operator ${operator} not supported in custom matcher`);
|
|
7443
|
+
};
|
|
7444
|
+
/**
|
|
7445
|
+
* The operator picks the arity: `Payment` is single-select in order rules and
|
|
7446
|
+
* multi-select in fulfillment workflows. `matcher` may be a set (one id per
|
|
7447
|
+
* tender): `is` matches when some id matches, `is not` when none does.
|
|
7448
|
+
*/
|
|
7449
|
+
const matchCustom = (condition, matcher) => {
|
|
7450
|
+
if (isMultiOperator(condition.operator)) return matchCustomMulti(condition, matcher);
|
|
7451
|
+
if (typeof matcher === "undefined" || typeof matcher === "string") return matchCustomEquality(condition, matcher);
|
|
7452
|
+
if (matcher.length === 0) return false;
|
|
7453
|
+
const matches = (id) => matchCustomEquality(condition, id);
|
|
7454
|
+
if (condition.operator === "is not") return matcher.every(matches);
|
|
7455
|
+
return matcher.some(matches);
|
|
7456
|
+
};
|
|
7457
|
+
const assertCustom = (condition, order) => {
|
|
7458
|
+
const { type } = condition;
|
|
7459
|
+
switch (type) {
|
|
7460
|
+
case "Fulfillment":
|
|
7461
|
+
case "Fulfillments": return matchCustom(condition, order.fulfillment_id);
|
|
7462
|
+
case "Store":
|
|
7463
|
+
case "Stores": return matchCustom(condition, order.store_id);
|
|
7464
|
+
case "Channel":
|
|
7465
|
+
case "Channels": return matchCustom(condition, order.channel_id);
|
|
7466
|
+
case "Brand":
|
|
7467
|
+
case "Brands": return matchCustom(condition, order.brand_id);
|
|
7468
|
+
default: throw new Error(`Condition ${type} not supported in custom matcher`);
|
|
7469
|
+
}
|
|
7470
|
+
};
|
|
7471
|
+
//#endregion
|
|
7472
|
+
//#region src/rules/enumMatcher.ts
|
|
7473
|
+
const matchEnumOperator = (condition, matcher) => {
|
|
7474
|
+
const { primitive, operator, value } = condition;
|
|
7475
|
+
assert(primitive === "enum", primitiveError("enum"));
|
|
7476
|
+
if (typeof matcher === "undefined") return false;
|
|
7477
|
+
switch (operator) {
|
|
7478
|
+
case "is":
|
|
7479
|
+
assert(typeof value === "string", "Expected a single enum value");
|
|
7480
|
+
return matcher === value;
|
|
7481
|
+
case "is not":
|
|
7482
|
+
assert(typeof value === "string", "Expected a single enum value");
|
|
7483
|
+
return matcher !== value;
|
|
7484
|
+
case "is any of":
|
|
7485
|
+
assert(Array.isArray(value), "Expected a list of enum values");
|
|
7486
|
+
return value.includes(matcher);
|
|
7487
|
+
case "is none of":
|
|
7488
|
+
assert(Array.isArray(value), "Expected a list of enum values");
|
|
7489
|
+
return !value.includes(matcher);
|
|
7490
|
+
default: throw new Error(`Operator ${operator} not supported in enum matcher`);
|
|
7491
|
+
}
|
|
7492
|
+
};
|
|
7493
|
+
const NEGATIVE_ENUM_OPERATORS = ["is not", "is none of"];
|
|
7494
|
+
/**
|
|
7495
|
+
* Enum condition over a set (one value per tender): positive operators
|
|
7496
|
+
* match when some value matches, negative ones when none matches.
|
|
7497
|
+
*/
|
|
7498
|
+
const matchEnumSet = (condition, matcher) => {
|
|
7499
|
+
if (typeof matcher === "undefined" || typeof matcher === "string") return matchEnumOperator(condition, matcher);
|
|
7500
|
+
if (matcher.length === 0) return false;
|
|
7501
|
+
const matches = (value) => matchEnumOperator(condition, value);
|
|
7502
|
+
if (NEGATIVE_ENUM_OPERATORS.includes(condition.operator)) return matcher.every(matches);
|
|
7503
|
+
return matcher.some(matches);
|
|
7504
|
+
};
|
|
7505
|
+
const paymentCollectAt = (condition, collectAt) => {
|
|
7506
|
+
const matcherType = "Payment collect at";
|
|
7507
|
+
assert(condition.primitive === "enum", primitiveError("enum"));
|
|
7508
|
+
assert(condition.type === matcherType, typeError(matcherType));
|
|
7509
|
+
return matchEnumSet(condition, collectAt);
|
|
7510
|
+
};
|
|
7511
|
+
const paymentMethod = (condition, method) => {
|
|
7512
|
+
const matcherType = "Payment method";
|
|
7513
|
+
assert(condition.primitive === "enum", primitiveError("enum"));
|
|
7514
|
+
assert(condition.type === matcherType, typeError(matcherType));
|
|
7515
|
+
return matchEnumSet(condition, method);
|
|
7516
|
+
};
|
|
7517
|
+
const paymentStatus = (condition, status) => {
|
|
7518
|
+
const matcherType = "Payment status";
|
|
7519
|
+
assert(condition.primitive === "enum", primitiveError("enum"));
|
|
7520
|
+
assert(condition.type === matcherType, typeError(matcherType));
|
|
7521
|
+
return matchEnumOperator(condition, status);
|
|
7522
|
+
};
|
|
7523
|
+
const CONDITION_INTERPRETATION_DEFAULT = "server";
|
|
7524
|
+
const isInterpretationCompatible = (interpretation, runtime, fallback) => {
|
|
7525
|
+
if (!runtime) return true;
|
|
7526
|
+
const value = interpretation ?? fallback;
|
|
7527
|
+
if (value === "both") return true;
|
|
7528
|
+
return value === runtime;
|
|
7529
|
+
};
|
|
7530
|
+
//#endregion
|
|
7531
|
+
//#region src/rules/numberMatcher.ts
|
|
7532
|
+
const matchNumberOperator = (condition, matcher) => {
|
|
7533
|
+
const { primitive, operator, value } = condition;
|
|
7534
|
+
assert(primitive === "number", primitiveError("number"));
|
|
7535
|
+
if (typeof matcher === "undefined") return false;
|
|
7536
|
+
assert(typeof value === "number", "Invalid number value");
|
|
7537
|
+
switch (operator) {
|
|
7538
|
+
case "equal to": return matcher === value;
|
|
7539
|
+
case "not equal to": return matcher !== value;
|
|
7540
|
+
case "greater than": return matcher > value;
|
|
7541
|
+
case "greater than or equal to": return matcher >= value;
|
|
7542
|
+
case "less than": return matcher < value;
|
|
7543
|
+
case "less than or equal to": return matcher <= value;
|
|
7544
|
+
default: throw new Error(`Operator ${operator} not supported in number matcher`);
|
|
7545
|
+
}
|
|
7546
|
+
};
|
|
7547
|
+
const orderIncludesProduct = (condition, quantity) => {
|
|
7548
|
+
const { type, primitive, value } = condition;
|
|
7549
|
+
const matcherType = "Order includes product";
|
|
7550
|
+
assert(primitive === "number", primitiveError("number"));
|
|
7551
|
+
assert(type === matcherType, typeError(matcherType));
|
|
7552
|
+
assert(value != null, "Invalid value");
|
|
7553
|
+
return matchNumberOperator(condition, quantity ?? 0);
|
|
7554
|
+
};
|
|
7555
|
+
const cartProductCount = (condition, count) => {
|
|
7556
|
+
const { type, primitive, value } = condition;
|
|
7557
|
+
const matcherType = "Cart product count";
|
|
7558
|
+
assert(primitive === "number", primitiveError("number"));
|
|
7559
|
+
assert(type === matcherType, typeError(matcherType));
|
|
7560
|
+
assert(value != null, "Invalid value");
|
|
7561
|
+
return matchNumberOperator(condition, count);
|
|
7562
|
+
};
|
|
7563
|
+
const orderIncludesProductWithTag$1 = (condition, matcher) => {
|
|
7564
|
+
const { type, primitive, value } = condition;
|
|
7565
|
+
const matcherType = "Order includes product with tag";
|
|
7566
|
+
assert(primitive === "number", primitiveError("number"));
|
|
7567
|
+
assert(type === matcherType, typeError(matcherType));
|
|
7568
|
+
assert(value != null, "Invalid value");
|
|
7569
|
+
return matchNumberOperator(condition, matcher);
|
|
7570
|
+
};
|
|
7571
|
+
//#endregion
|
|
7572
|
+
//#region src/rules/stringCondition.utils.ts
|
|
7573
|
+
const getCaseInsensitive = (condition) => {
|
|
7574
|
+
return condition.caseInsensitive ?? false;
|
|
7575
|
+
};
|
|
7576
|
+
const normalizeStringForMatch = (value, caseInsensitive) => {
|
|
7577
|
+
if (caseInsensitive) return value.toLowerCase();
|
|
7578
|
+
return value;
|
|
7579
|
+
};
|
|
7580
|
+
const normalizePairForMatch = (matcher, expected, caseInsensitive) => {
|
|
7581
|
+
return {
|
|
7582
|
+
matcher: normalizeStringForMatch(matcher, caseInsensitive),
|
|
7583
|
+
expected: normalizeStringForMatch(expected, caseInsensitive)
|
|
7584
|
+
};
|
|
7585
|
+
};
|
|
7586
|
+
//#endregion
|
|
7587
|
+
//#region src/rules/stringMatcher.ts
|
|
7588
|
+
const matchStringOperator = (condition, matcher) => {
|
|
7589
|
+
const { primitive, operator, value } = condition;
|
|
7590
|
+
assert(primitive === "string", primitiveError("string"));
|
|
7591
|
+
if (typeof matcher === "undefined") return false;
|
|
7592
|
+
const caseInsensitive = getCaseInsensitive(condition);
|
|
7593
|
+
const pair = normalizePairForMatch(matcher, String(value), caseInsensitive);
|
|
7594
|
+
const normalizedMatcher = pair.matcher;
|
|
7595
|
+
const normalizedExpected = pair.expected;
|
|
7596
|
+
switch (operator) {
|
|
7597
|
+
case "equal to": return normalizedMatcher === normalizedExpected;
|
|
7598
|
+
case "not equal to": return normalizedMatcher !== normalizedExpected;
|
|
7599
|
+
case "starts with": return normalizedMatcher.startsWith(normalizedExpected);
|
|
7600
|
+
case "ends with": return normalizedMatcher.endsWith(normalizedExpected);
|
|
7601
|
+
case "contains": return normalizedMatcher.includes(normalizedExpected);
|
|
7602
|
+
default: throw new Error(`Operator ${operator} not supported in string matcher`);
|
|
7603
|
+
}
|
|
7604
|
+
};
|
|
7605
|
+
const orderIncludesProductWithTag = (condition, matcher) => {
|
|
7606
|
+
const { type, primitive, value } = condition;
|
|
7607
|
+
const matcherType = "Order includes product with tag";
|
|
7608
|
+
assert(primitive === "string", primitiveError("string"));
|
|
7609
|
+
assert(type === matcherType, typeError(matcherType));
|
|
7610
|
+
assert(value != null, "Invalid value");
|
|
7611
|
+
return matchStringOperator(condition, matcher);
|
|
7612
|
+
};
|
|
7613
|
+
//#endregion
|
|
7614
|
+
//#region src/rules/monitoringMatcher.ts
|
|
7615
|
+
const windowOf = (condition) => {
|
|
7616
|
+
if (!("window_minutes" in condition)) return void 0;
|
|
7617
|
+
return condition.window_minutes;
|
|
7618
|
+
};
|
|
7619
|
+
const accumulatedEvents = (condition, count) => {
|
|
7620
|
+
const { type, primitive, value } = condition;
|
|
7621
|
+
const matcherType = "Accumulated events";
|
|
7622
|
+
assert(primitive === "number", primitiveError("number"));
|
|
7623
|
+
assert(type === matcherType, typeError(matcherType));
|
|
7624
|
+
assert(value != null, "Invalid value");
|
|
7625
|
+
if (typeof count === "undefined") return false;
|
|
7626
|
+
return matchNumberOperator(condition, count);
|
|
7627
|
+
};
|
|
7628
|
+
const apiErrorRate = (condition, rate) => {
|
|
7629
|
+
const { type, primitive, value } = condition;
|
|
7630
|
+
const matcherType = "API error rate";
|
|
7631
|
+
assert(primitive === "number", primitiveError("number"));
|
|
7632
|
+
assert(type === matcherType, typeError(matcherType));
|
|
7633
|
+
assert(value != null, "Invalid value");
|
|
7634
|
+
if (typeof rate === "undefined") return false;
|
|
7635
|
+
return matchNumberOperator(condition, rate);
|
|
7636
|
+
};
|
|
7637
|
+
const httpStatus = (condition, status) => {
|
|
7638
|
+
const { type, primitive, value } = condition;
|
|
7639
|
+
const matcherType = "HTTP status";
|
|
7640
|
+
assert(primitive === "number", primitiveError("number"));
|
|
7641
|
+
assert(type === matcherType, typeError(matcherType));
|
|
7642
|
+
assert(value != null, "Invalid value");
|
|
7643
|
+
if (typeof status === "undefined") return false;
|
|
7644
|
+
return matchNumberOperator(condition, status);
|
|
7645
|
+
};
|
|
7646
|
+
const requestPath = (condition, path) => {
|
|
7647
|
+
const { type, primitive, value } = condition;
|
|
7648
|
+
const matcherType = "Request path";
|
|
7649
|
+
assert(primitive === "string", primitiveError("string"));
|
|
7650
|
+
assert(type === matcherType, typeError(matcherType));
|
|
7651
|
+
assert(value != null, "Invalid value");
|
|
7652
|
+
return matchStringOperator(condition, path);
|
|
7653
|
+
};
|
|
7654
|
+
const failureMessage = (condition, message) => {
|
|
7655
|
+
const { type, primitive, value } = condition;
|
|
7656
|
+
const matcherType = "Failure message";
|
|
7657
|
+
assert(primitive === "string", primitiveError("string"));
|
|
7658
|
+
assert(type === matcherType, typeError(matcherType));
|
|
7659
|
+
assert(value != null, "Invalid value");
|
|
7660
|
+
return matchStringOperator(condition, message);
|
|
7661
|
+
};
|
|
7662
|
+
const windowMinutes = (condition) => {
|
|
7663
|
+
return windowOf(condition);
|
|
7664
|
+
};
|
|
7665
|
+
//#endregion
|
|
7666
|
+
//#region src/rules/recurringDateCron.ts
|
|
7651
7667
|
import_dayjs_min.default.extend(utc);
|
|
7652
7668
|
import_dayjs_min.default.extend(timezone);
|
|
7653
7669
|
import_dayjs_min.default.extend(isBetween);
|
|
@@ -7844,6 +7860,47 @@ const currentDate = (condition, referenceDate) => {
|
|
|
7844
7860
|
return matchRecurringDateOperator(condition, current);
|
|
7845
7861
|
};
|
|
7846
7862
|
//#endregion
|
|
7863
|
+
//#region src/rules/relativeDateMatcher.ts
|
|
7864
|
+
const matchRelativeDateOperator = (condition, matcher, referenceDate) => {
|
|
7865
|
+
const { primitive, operator, value } = condition;
|
|
7866
|
+
assert(primitive === "relative date", primitiveError("relative date"));
|
|
7867
|
+
if (typeof matcher === "undefined") return false;
|
|
7868
|
+
assert(typeof value === "string", "Invalid relative date value");
|
|
7869
|
+
const [amountString, unitRaw] = value.split("/");
|
|
7870
|
+
assert(amountString && unitRaw, "Amount & Unit required");
|
|
7871
|
+
const amount = parseInt(amountString, 10);
|
|
7872
|
+
const unit = unitRaw;
|
|
7873
|
+
const issueDate = (0, import_dayjs_min.default)(referenceDate ?? (/* @__PURE__ */ new Date()).toISOString());
|
|
7874
|
+
const matcherDate = (0, import_dayjs_min.default)(matcher);
|
|
7875
|
+
switch (operator) {
|
|
7876
|
+
case "occurred": {
|
|
7877
|
+
const endDate = issueDate.subtract(amount, unit).endOf(unit);
|
|
7878
|
+
const startDate = endDate.startOf(unit);
|
|
7879
|
+
const sameStart = matcherDate.isSame(startDate, unit);
|
|
7880
|
+
const afterStart = matcherDate.isAfter(startDate);
|
|
7881
|
+
const sameEnd = matcherDate.isSame(endDate, unit);
|
|
7882
|
+
const beforeEnd = matcherDate.isBefore(endDate);
|
|
7883
|
+
return (sameStart || afterStart) && (sameEnd || beforeEnd);
|
|
7884
|
+
}
|
|
7885
|
+
case "occurred in the last": {
|
|
7886
|
+
const targetDate = issueDate.subtract(amount, unit);
|
|
7887
|
+
const sameTarget = matcherDate.isSame(targetDate, unit);
|
|
7888
|
+
const afterTarget = matcherDate.isAfter(targetDate);
|
|
7889
|
+
const sameIssue = matcherDate.isSame(issueDate, unit);
|
|
7890
|
+
const beforeIssue = matcherDate.isBefore(issueDate);
|
|
7891
|
+
return (sameTarget || afterTarget) && (sameIssue || beforeIssue);
|
|
7892
|
+
}
|
|
7893
|
+
case "occurred more than": {
|
|
7894
|
+
const targetDate = issueDate.subtract(amount, unit);
|
|
7895
|
+
return matcherDate.isBefore(targetDate, unit);
|
|
7896
|
+
}
|
|
7897
|
+
default: {
|
|
7898
|
+
const msg = `Operator ${operator} not supported in relative date matcher`;
|
|
7899
|
+
throw new Error(msg);
|
|
7900
|
+
}
|
|
7901
|
+
}
|
|
7902
|
+
};
|
|
7903
|
+
//#endregion
|
|
7847
7904
|
//#region src/rules/RulesEngine.ts
|
|
7848
7905
|
var RulesEngine = class {
|
|
7849
7906
|
order;
|
|
@@ -7941,6 +7998,7 @@ var RulesEngine = class {
|
|
|
7941
7998
|
const monitoring = this.getDependency("monitoring", type);
|
|
7942
7999
|
return httpStatus(condition, monitoring.http_status);
|
|
7943
8000
|
}
|
|
8001
|
+
if (type === "Customer age") return matchNumberOperator(condition, this.customerAge());
|
|
7944
8002
|
throw new Error(`Condition ${type} not supported in number matcher`);
|
|
7945
8003
|
};
|
|
7946
8004
|
assertBoolean = (condition) => {
|
|
@@ -7998,17 +8056,40 @@ var RulesEngine = class {
|
|
|
7998
8056
|
default: throw new Error(`Condition ${type} not supported in enum matcher`);
|
|
7999
8057
|
}
|
|
8000
8058
|
};
|
|
8059
|
+
customerAge = () => {
|
|
8060
|
+
const dob = this.customer?.dob;
|
|
8061
|
+
if (!dob) return void 0;
|
|
8062
|
+
const born = new Date(dob);
|
|
8063
|
+
const now = new Date(this.referenceDate ?? Date.now());
|
|
8064
|
+
if (Number.isNaN(born.getTime()) || Number.isNaN(now.getTime())) return;
|
|
8065
|
+
let age = now.getUTCFullYear() - born.getUTCFullYear();
|
|
8066
|
+
const month = now.getUTCMonth() - born.getUTCMonth();
|
|
8067
|
+
const beforeBirthday = now.getUTCDate() < born.getUTCDate();
|
|
8068
|
+
if (month < 0 || month === 0 && beforeBirthday) age -= 1;
|
|
8069
|
+
return age;
|
|
8070
|
+
};
|
|
8071
|
+
customerDate = (type) => {
|
|
8072
|
+
const customer = this.customer;
|
|
8073
|
+
if (type === "Birthday") return customer?.dob;
|
|
8074
|
+
if (type === "Registration") return customer?.registered_at;
|
|
8075
|
+
if (type === "Customer last visit") return customer?.last_visit_at;
|
|
8076
|
+
if (type === "Customer last session") return customer?.last_session_at;
|
|
8077
|
+
};
|
|
8001
8078
|
assertAbsoluteDate = (condition) => {
|
|
8002
8079
|
const { primitive, type } = condition;
|
|
8003
8080
|
assert(primitive === "absolute date", primitiveError("absolute date"));
|
|
8004
|
-
|
|
8005
|
-
|
|
8081
|
+
if (type !== "Today") {
|
|
8082
|
+
const msg = `Condition ${type} not supported in absolute date matcher`;
|
|
8083
|
+
throw new Error(msg);
|
|
8084
|
+
}
|
|
8085
|
+
const today = this.referenceDate ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
8086
|
+
return matchAbsoluteDateOperator(condition, today);
|
|
8006
8087
|
};
|
|
8007
8088
|
assertRelativeDate = (condition) => {
|
|
8008
8089
|
const { primitive, type } = condition;
|
|
8009
8090
|
assert(primitive === "relative date", primitiveError("relative date"));
|
|
8010
|
-
const
|
|
8011
|
-
|
|
8091
|
+
const fact = this.customerDate(type);
|
|
8092
|
+
return matchRelativeDateOperator(condition, fact, this.referenceDate);
|
|
8012
8093
|
};
|
|
8013
8094
|
assertRecurringDate = (condition) => {
|
|
8014
8095
|
const { primitive, type } = condition;
|
|
@@ -8113,66 +8194,6 @@ var RulesEngine = class {
|
|
|
8113
8194
|
};
|
|
8114
8195
|
};
|
|
8115
8196
|
//#endregion
|
|
8116
|
-
//#region src/rules/absoluteDateMatcher.ts
|
|
8117
|
-
const matchAbsoluteDateOperator = (condition, matcher) => {
|
|
8118
|
-
const { primitive, operator, value } = condition;
|
|
8119
|
-
assert(primitive === "absolute date", primitiveError("absolute date"));
|
|
8120
|
-
if (typeof matcher === "undefined") return false;
|
|
8121
|
-
const matcherDate = (0, import_dayjs_min.default)(matcher);
|
|
8122
|
-
const expectedDate = (0, import_dayjs_min.default)(value);
|
|
8123
|
-
switch (operator) {
|
|
8124
|
-
case "is": return matcherDate.isSame(expectedDate, "day");
|
|
8125
|
-
case "is not": return !matcherDate.isSame(expectedDate, "day");
|
|
8126
|
-
case "is before": return matcherDate.isBefore(expectedDate, "day");
|
|
8127
|
-
case "is after": return matcherDate.isAfter(expectedDate, "day");
|
|
8128
|
-
default: {
|
|
8129
|
-
const msg = `Operator ${operator} not supported in absolute date matcher`;
|
|
8130
|
-
throw new Error(msg);
|
|
8131
|
-
}
|
|
8132
|
-
}
|
|
8133
|
-
};
|
|
8134
|
-
//#endregion
|
|
8135
|
-
//#region src/rules/relativeDateMatcher.ts
|
|
8136
|
-
const matchRelativeDateOperator = (condition, matcher, referenceDate) => {
|
|
8137
|
-
const { primitive, operator, value } = condition;
|
|
8138
|
-
assert(primitive === "relative date", primitiveError("relative date"));
|
|
8139
|
-
if (typeof matcher === "undefined") return false;
|
|
8140
|
-
assert(typeof value === "string", "Invalid relative date value");
|
|
8141
|
-
const [amountString, unitRaw] = value.split("/");
|
|
8142
|
-
assert(amountString && unitRaw, "Amount & Unit required");
|
|
8143
|
-
const amount = parseInt(amountString, 10);
|
|
8144
|
-
const unit = unitRaw;
|
|
8145
|
-
const issueDate = (0, import_dayjs_min.default)(referenceDate ?? (/* @__PURE__ */ new Date()).toISOString());
|
|
8146
|
-
const matcherDate = (0, import_dayjs_min.default)(matcher);
|
|
8147
|
-
switch (operator) {
|
|
8148
|
-
case "occurred": {
|
|
8149
|
-
const endDate = issueDate.subtract(amount, unit).endOf(unit);
|
|
8150
|
-
const startDate = endDate.startOf(unit);
|
|
8151
|
-
const sameStart = matcherDate.isSame(startDate, unit);
|
|
8152
|
-
const afterStart = matcherDate.isAfter(startDate);
|
|
8153
|
-
const sameEnd = matcherDate.isSame(endDate, unit);
|
|
8154
|
-
const beforeEnd = matcherDate.isBefore(endDate);
|
|
8155
|
-
return (sameStart || afterStart) && (sameEnd || beforeEnd);
|
|
8156
|
-
}
|
|
8157
|
-
case "occurred in the last": {
|
|
8158
|
-
const targetDate = issueDate.subtract(amount, unit);
|
|
8159
|
-
const sameTarget = matcherDate.isSame(targetDate, unit);
|
|
8160
|
-
const afterTarget = matcherDate.isAfter(targetDate);
|
|
8161
|
-
const sameIssue = matcherDate.isSame(issueDate, unit);
|
|
8162
|
-
const beforeIssue = matcherDate.isBefore(issueDate);
|
|
8163
|
-
return (sameTarget || afterTarget) && (sameIssue || beforeIssue);
|
|
8164
|
-
}
|
|
8165
|
-
case "occurred more than": {
|
|
8166
|
-
const targetDate = issueDate.subtract(amount, unit);
|
|
8167
|
-
return matcherDate.isBefore(targetDate, unit);
|
|
8168
|
-
}
|
|
8169
|
-
default: {
|
|
8170
|
-
const msg = `Operator ${operator} not supported in relative date matcher`;
|
|
8171
|
-
throw new Error(msg);
|
|
8172
|
-
}
|
|
8173
|
-
}
|
|
8174
|
-
};
|
|
8175
|
-
//#endregion
|
|
8176
8197
|
//#region src/rules/checkout.ts
|
|
8177
8198
|
const DEFAULT_SHIPPING_FIELDS = {
|
|
8178
8199
|
address_line1: "required",
|
|
@@ -8625,4 +8646,4 @@ const cardPreviewGroups = (value) => {
|
|
|
8625
8646
|
return groups;
|
|
8626
8647
|
};
|
|
8627
8648
|
//#endregion
|
|
8628
|
-
export { CARD_BRANDS, DEFAULT_RECOMMENDATION_TYPES, Order, OrderSyncPendingError, OrderValidationError, RecurringDateCron, RulesEngine, StorefrontHttpError, addLines, auth, buildCustomerSnapshot, cardBrandSpec, cardDigits, cardNumberGaps, cardNumberMaxLength, cardPreviewGroups, caretAfterDigits, collectOrderLineDiffs, computeStartingPrice, countCartProducts, coveredAmount, createSession, currentNodes, cvcMaxLength, defaultConditionError, detectCardBrand, flattenMenuCategories, flushLogs, formatCardNumber, formatCvc, formatExpiry, fromOrderLines, genId, genOrderId, getLogContext, getScheduleWindow, indexMenu, isCardNumberComplete, isCardNumberDefinitelyInvalid, isCheckoutFieldFilled, isCvcComplete, isDeviceCharged, isDeviceNotLinked, isExpiryComplete, isKioskUnauthorized, isMilestoneStepDone, isOnSchedule, isOrderValidationError, isPosDeviceNotLinked, isPosUnauthorized, isRealPaymentMethod, isStorefrontNotFound, isVariantBase, listMenusStream, localizePosError, log, logError, luhnCheck, matchAbsoluteDateOperator, matchBooleanOperator, matchCurrencyOperator, matchEnumOperator, matchNumberOperator, matchRecurringDateOperator, matchRelativeDateOperator, matchStringOperator, milestoneSteps, milestoneTrack, nestMenuCategories, normalizeCardholderName, orderPaymentRedeems, parseExpiry, paymentMatchesClientConditions, recomputeRedemptions, recomputeTotals, redemptionCap, remainingAmount, removeLine, replaceLine, resolveCheckout, reversePayment, runPaymentAttempt, selectablePayments, serializeError, services, setLineQuantity, storefrontJson, stripRedemptionLines, toOrderLines, toPaymentCardBrand, validateCardNumber, validateCardholderName, validateCvc, validateExpiry, validateOrderAgainstMenu, verifyWebhookSignature, visitedStates };
|
|
8649
|
+
export { CARD_BRANDS, DEFAULT_RECOMMENDATION_TYPES, Order, OrderSyncPendingError, OrderValidationError, RecurringDateCron, RulesEngine, StorefrontHttpError, addLines, auth, buildCustomerSnapshot, cardBrandSpec, cardDigits, cardNumberGaps, cardNumberMaxLength, cardPreviewGroups, caretAfterDigits, clampPaymentRedeems, collectOrderLineDiffs, computeStartingPrice, countCartProducts, coveredAmount, createSession, currentNodes, cvcMaxLength, defaultConditionError, detectCardBrand, flattenMenuCategories, flushLogs, formatCardNumber, formatCvc, formatExpiry, fromOrderLines, genId, genOrderId, getLogContext, getScheduleWindow, indexMenu, isCardNumberComplete, isCardNumberDefinitelyInvalid, isCheckoutFieldFilled, isCvcComplete, isDeviceCharged, isDeviceNotLinked, isExpiryComplete, isKioskUnauthorized, isMilestoneStepDone, isOnSchedule, isOrderValidationError, isPosDeviceNotLinked, isPosUnauthorized, isRealPaymentMethod, isStorefrontNotFound, isVariantBase, listMenusStream, localizePosError, log, logError, luhnCheck, matchAbsoluteDateOperator, matchBooleanOperator, matchCurrencyOperator, matchEnumOperator, matchNumberOperator, matchRecurringDateOperator, matchRelativeDateOperator, matchStringOperator, milestoneSteps, milestoneTrack, nestMenuCategories, normalizeCardholderName, orderPaymentRedeems, parseExpiry, paymentMatchesClientConditions, recomputeRedemptions, recomputeTotals, redemptionCap, remainingAmount, removeLine, replaceLine, resolveCheckout, reversePayment, runPaymentAttempt, selectablePayments, serializeError, services, setLineQuantity, storefrontJson, stripRedemptionLines, toOrderLines, toPaymentCardBrand, validateCardNumber, validateCardholderName, validateCvc, validateExpiry, validateOrderAgainstMenu, verifyWebhookSignature, visitedStates };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fire-spark/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Fire Spark headless SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"big.js": "6.2.1",
|
|
21
21
|
"dayjs": "1.11.9",
|
|
22
22
|
"@types/big.js": "6.2.2",
|
|
23
|
-
"@fire-spark/payments": "^0.
|
|
24
|
-
"@fire-spark/types": "^0.
|
|
23
|
+
"@fire-spark/payments": "^0.4.1",
|
|
24
|
+
"@fire-spark/types": "^0.4.1"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"tsdown": "^0.22.14",
|