@fire-spark/core 0.4.0 → 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 +9 -1
- package/dist/index.js +511 -481
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -783,6 +783,12 @@ declare const isDeviceCharged: (tender: {
|
|
|
783
783
|
}) => boolean;
|
|
784
784
|
/** Most a REDEMPTION can cover: order total − tip − delivery (D18). */
|
|
785
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[];
|
|
786
792
|
type SimpleTenderPayment = {
|
|
787
793
|
id?: string;
|
|
788
794
|
type?: PaymentTenderInput["method"];
|
|
@@ -1142,6 +1148,8 @@ declare class RulesEngine {
|
|
|
1142
1148
|
private assertBoolean;
|
|
1143
1149
|
private assertCurrency;
|
|
1144
1150
|
private assertEnum;
|
|
1151
|
+
private customerAge;
|
|
1152
|
+
private customerDate;
|
|
1145
1153
|
private assertAbsoluteDate;
|
|
1146
1154
|
private assertRelativeDate;
|
|
1147
1155
|
private assertRecurringDate;
|
|
@@ -1316,4 +1324,4 @@ declare const validateCardholderName: (value: string) => CardholderNameError | n
|
|
|
1316
1324
|
*/
|
|
1317
1325
|
declare const cardPreviewGroups: (value: string) => CardPreviewSlot[][];
|
|
1318
1326
|
//#endregion
|
|
1319
|
-
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,37 +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
3488
|
* Trims payment redeems in application order so their sum fits the cap.
|
|
3484
|
-
* Discount redeems are left untouched.
|
|
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 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];
|
|
3496
3501
|
return [{
|
|
3497
3502
|
...item,
|
|
3498
|
-
amount: fits
|
|
3503
|
+
amount: moneyNumber(fits)
|
|
3499
3504
|
}];
|
|
3500
3505
|
});
|
|
3501
3506
|
};
|
|
@@ -3884,14 +3889,15 @@ const chargedTenders = (intents) => {
|
|
|
3884
3889
|
};
|
|
3885
3890
|
/** Settled tenders plus PENDING `confirm_after_inject`, order currency. */
|
|
3886
3891
|
const coveredAmount = (intents) => {
|
|
3887
|
-
return chargedTenders(intents).reduce((sum, tender) => {
|
|
3888
|
-
return sum
|
|
3889
|
-
},
|
|
3892
|
+
return moneyNumber(chargedTenders(intents).reduce((sum, tender) => {
|
|
3893
|
+
return sum.plus(tender.amount.total);
|
|
3894
|
+
}, moneyZero()));
|
|
3890
3895
|
};
|
|
3891
3896
|
const remainingAmount = (order) => {
|
|
3892
|
-
const
|
|
3893
|
-
const left = order.totals.total
|
|
3894
|
-
|
|
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);
|
|
3895
3901
|
};
|
|
3896
3902
|
const isPlaced = (order) => order.status !== "OPEN";
|
|
3897
3903
|
/** Orchestrator failures carry the charge-currency amount, not the order's. */
|
|
@@ -4021,12 +4027,9 @@ var PaymentSession = class {
|
|
|
4021
4027
|
const conversion = await this.chargeAmount(payment, amount, server);
|
|
4022
4028
|
const config = record(payment.config);
|
|
4023
4029
|
const methodConfig = record(payment.method.config);
|
|
4024
|
-
const
|
|
4025
|
-
const
|
|
4026
|
-
const taxes = taxBreakdown(order.lines, {
|
|
4027
|
-
share,
|
|
4028
|
-
decimals
|
|
4029
|
-
});
|
|
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 });
|
|
4030
4033
|
const info = order.customer.customer_info;
|
|
4031
4034
|
const customer = {
|
|
4032
4035
|
document: info.document_number ?? null,
|
|
@@ -6959,391 +6962,8 @@ const verifyWebhookSignature = async (input) => {
|
|
|
6959
6962
|
return safeEqual(received, await hmacHex(secret, body));
|
|
6960
6963
|
};
|
|
6961
6964
|
//#endregion
|
|
6962
|
-
//#region
|
|
6963
|
-
|
|
6964
|
-
if (value) return;
|
|
6965
|
-
throw new Error(message);
|
|
6966
|
-
};
|
|
6967
|
-
//#endregion
|
|
6968
|
-
//#region src/rules/common.ts
|
|
6969
|
-
const primitiveError = (primitive) => {
|
|
6970
|
-
return `Condition primitive must be ${primitive}`;
|
|
6971
|
-
};
|
|
6972
|
-
const typeError = (type) => {
|
|
6973
|
-
return `Condition type must be ${type}`;
|
|
6974
|
-
};
|
|
6975
|
-
const defaultConditionError = "Condition was not met";
|
|
6976
|
-
const getLineByProductId = (lines, productId) => {
|
|
6977
|
-
if (!productId) return void 0;
|
|
6978
|
-
return lines?.find((line) => line.id === productId);
|
|
6979
|
-
};
|
|
6980
|
-
const countCartProducts = (lines, includeModifiers) => {
|
|
6981
|
-
if (!lines?.length) return 0;
|
|
6982
|
-
return lines.filter((line) => {
|
|
6983
|
-
if (line.type === "FEE") return false;
|
|
6984
|
-
if (includeModifiers) return true;
|
|
6985
|
-
if (!(line.parent_uid == null)) return false;
|
|
6986
|
-
return line.type === "PRODUCT" || line.type == null;
|
|
6987
|
-
}).reduce((sum, line) => sum + line.quantity, 0);
|
|
6988
|
-
};
|
|
6989
|
-
const getLineTagValues = (lines, tag) => {
|
|
6990
|
-
if (!tag) return [];
|
|
6991
|
-
const values = [];
|
|
6992
|
-
for (const line of lines ?? []) {
|
|
6993
|
-
const value = line.tags?.[tag];
|
|
6994
|
-
if (typeof value === "undefined") continue;
|
|
6995
|
-
values.push(value);
|
|
6996
|
-
}
|
|
6997
|
-
return values;
|
|
6998
|
-
};
|
|
6999
|
-
//#endregion
|
|
7000
|
-
//#region src/rules/booleanMatcher.ts
|
|
7001
|
-
const matchBooleanOperator = (condition, matcher) => {
|
|
7002
|
-
const { primitive, operator, value } = condition;
|
|
7003
|
-
assert(primitive === "boolean", primitiveError("boolean"));
|
|
7004
|
-
if (typeof matcher === "undefined") return false;
|
|
7005
|
-
assert(typeof value === "boolean", "Invalid boolean value");
|
|
7006
|
-
switch (operator) {
|
|
7007
|
-
case "is": return matcher === value;
|
|
7008
|
-
case "is not": return matcher !== value;
|
|
7009
|
-
default: throw new Error(`Operator ${operator} not supported in boolean matcher`);
|
|
7010
|
-
}
|
|
7011
|
-
};
|
|
7012
|
-
const isAnonymousCustomer = (condition, isAnonymous) => {
|
|
7013
|
-
const { type, primitive, value } = condition;
|
|
7014
|
-
const matcherType = "Customer is anonymous";
|
|
7015
|
-
assert(primitive === "boolean", primitiveError("boolean"));
|
|
7016
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7017
|
-
assert(value != null, "Invalid value");
|
|
7018
|
-
return matchBooleanOperator(condition, isAnonymous ?? false);
|
|
7019
|
-
};
|
|
7020
|
-
const orderIncludesProductWithTag$3 = (condition, matcher) => {
|
|
7021
|
-
const { type, primitive, value } = condition;
|
|
7022
|
-
const matcherType = "Order includes product with tag";
|
|
7023
|
-
assert(primitive === "boolean", primitiveError("boolean"));
|
|
7024
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7025
|
-
assert(value != null, "Invalid value");
|
|
7026
|
-
return matchBooleanOperator(condition, matcher);
|
|
7027
|
-
};
|
|
7028
|
-
const isAnonymousOrder = (condition, isAnonymous) => {
|
|
7029
|
-
const { type, primitive, value } = condition;
|
|
7030
|
-
const matcherType = "Is anonymous order";
|
|
7031
|
-
assert(primitive === "boolean", primitiveError("boolean"));
|
|
7032
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7033
|
-
assert(value != null, "Invalid value");
|
|
7034
|
-
return matchBooleanOperator(condition, isAnonymous ?? false);
|
|
7035
|
-
};
|
|
7036
|
-
const paymentIsMixed = (condition, isMixed) => {
|
|
7037
|
-
const { type, primitive, value } = condition;
|
|
7038
|
-
const matcherType = "Payment is mixed";
|
|
7039
|
-
assert(primitive === "boolean", primitiveError("boolean"));
|
|
7040
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7041
|
-
assert(value != null, "Invalid value");
|
|
7042
|
-
return matchBooleanOperator(condition, isMixed ?? false);
|
|
7043
|
-
};
|
|
7044
|
-
const paymentCaptured = (condition, captured) => {
|
|
7045
|
-
const { type, primitive, value } = condition;
|
|
7046
|
-
const matcherType = "Payment captured";
|
|
7047
|
-
assert(primitive === "boolean", primitiveError("boolean"));
|
|
7048
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7049
|
-
assert(value != null, "Invalid value");
|
|
7050
|
-
return matchBooleanOperator(condition, captured ?? false);
|
|
7051
|
-
};
|
|
7052
|
-
//#endregion
|
|
7053
|
-
//#region src/rules/currencyMatcher.ts
|
|
7054
|
-
const matchCurrencyOperator = (condition, matcher) => {
|
|
7055
|
-
const { primitive, operator, value } = condition;
|
|
7056
|
-
assert(primitive === "currency", primitiveError("currency"));
|
|
7057
|
-
if (typeof matcher === "undefined") return false;
|
|
7058
|
-
assert(typeof value === "number", "Invalid currency value");
|
|
7059
|
-
switch (operator) {
|
|
7060
|
-
case "equal to": return matcher === value;
|
|
7061
|
-
case "not equal to": return matcher !== value;
|
|
7062
|
-
case "greater than": return matcher > value;
|
|
7063
|
-
case "greater than or equal to": return matcher >= value;
|
|
7064
|
-
case "less than": return matcher < value;
|
|
7065
|
-
case "less than or equal to": return matcher <= value;
|
|
7066
|
-
default: throw new Error(`Operator ${operator} not supported in currency matcher`);
|
|
7067
|
-
}
|
|
7068
|
-
};
|
|
7069
|
-
const orderIncludesProductWithTag$2 = (condition, matcher) => {
|
|
7070
|
-
const { type, primitive, value } = condition;
|
|
7071
|
-
const matcherType = "Order includes product with tag";
|
|
7072
|
-
assert(primitive === "currency", primitiveError("currency"));
|
|
7073
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7074
|
-
assert(value != null, "Invalid value");
|
|
7075
|
-
return matchCurrencyOperator(condition, matcher);
|
|
7076
|
-
};
|
|
7077
|
-
const orderTotal = (condition, total) => {
|
|
7078
|
-
const { type, primitive, value } = condition;
|
|
7079
|
-
const matcherType = "Order total";
|
|
7080
|
-
assert(primitive === "currency", primitiveError("currency"));
|
|
7081
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7082
|
-
assert(value != null, "Invalid value");
|
|
7083
|
-
return matchCurrencyOperator(condition, total);
|
|
7084
|
-
};
|
|
7085
|
-
//#endregion
|
|
7086
|
-
//#region src/rules/customMatcher.ts
|
|
7087
|
-
const MULTI_OPERATORS = [
|
|
7088
|
-
"is any of",
|
|
7089
|
-
"is none of",
|
|
7090
|
-
"is all of"
|
|
7091
|
-
];
|
|
7092
|
-
const isMultiOperator = (operator) => {
|
|
7093
|
-
return MULTI_OPERATORS.includes(operator);
|
|
7094
|
-
};
|
|
7095
|
-
const splitCsv = (value) => {
|
|
7096
|
-
return value.split(",").map((part) => part.trim()).filter(Boolean);
|
|
7097
|
-
};
|
|
7098
|
-
const matchCustomEquality = (condition, matcher) => {
|
|
7099
|
-
const { primitive, operator, value } = condition;
|
|
7100
|
-
assert(primitive === "custom", primitiveError("custom"));
|
|
7101
|
-
if (typeof matcher === "undefined") return false;
|
|
7102
|
-
assert(typeof value === "string", "Invalid custom value");
|
|
7103
|
-
switch (operator) {
|
|
7104
|
-
case "is": return matcher === value;
|
|
7105
|
-
case "is not": return matcher !== value;
|
|
7106
|
-
default: throw new Error(`Operator ${operator} not supported in custom matcher`);
|
|
7107
|
-
}
|
|
7108
|
-
};
|
|
7109
|
-
const matchCustomMulti = (condition, matcher) => {
|
|
7110
|
-
const { primitive, operator, value } = condition;
|
|
7111
|
-
assert(primitive === "custom", primitiveError("custom"));
|
|
7112
|
-
if (typeof matcher === "undefined") return false;
|
|
7113
|
-
assert(typeof value === "string", "Invalid custom value");
|
|
7114
|
-
const allowed = splitCsv(value);
|
|
7115
|
-
if (allowed.length === 0) return false;
|
|
7116
|
-
const candidates = typeof matcher === "object" ? matcher : [matcher];
|
|
7117
|
-
if (candidates.length === 0) return false;
|
|
7118
|
-
if (operator === "is any of") return allowed.some((id) => candidates.includes(id));
|
|
7119
|
-
if (operator === "is none of") return !allowed.some((id) => candidates.includes(id));
|
|
7120
|
-
if (operator === "is all of") return allowed.every((id) => candidates.includes(id));
|
|
7121
|
-
throw new Error(`Operator ${operator} not supported in custom matcher`);
|
|
7122
|
-
};
|
|
7123
|
-
/**
|
|
7124
|
-
* The operator picks the arity: `Payment` is single-select in order rules and
|
|
7125
|
-
* multi-select in fulfillment workflows. `matcher` may be a set (one id per
|
|
7126
|
-
* tender): `is` matches when some id matches, `is not` when none does.
|
|
7127
|
-
*/
|
|
7128
|
-
const matchCustom = (condition, matcher) => {
|
|
7129
|
-
if (isMultiOperator(condition.operator)) return matchCustomMulti(condition, matcher);
|
|
7130
|
-
if (typeof matcher === "undefined" || typeof matcher === "string") return matchCustomEquality(condition, matcher);
|
|
7131
|
-
if (matcher.length === 0) return false;
|
|
7132
|
-
const matches = (id) => matchCustomEquality(condition, id);
|
|
7133
|
-
if (condition.operator === "is not") return matcher.every(matches);
|
|
7134
|
-
return matcher.some(matches);
|
|
7135
|
-
};
|
|
7136
|
-
const assertCustom = (condition, order) => {
|
|
7137
|
-
const { type } = condition;
|
|
7138
|
-
switch (type) {
|
|
7139
|
-
case "Fulfillment":
|
|
7140
|
-
case "Fulfillments": return matchCustom(condition, order.fulfillment_id);
|
|
7141
|
-
case "Store":
|
|
7142
|
-
case "Stores": return matchCustom(condition, order.store_id);
|
|
7143
|
-
case "Channel":
|
|
7144
|
-
case "Channels": return matchCustom(condition, order.channel_id);
|
|
7145
|
-
case "Brand":
|
|
7146
|
-
case "Brands": return matchCustom(condition, order.brand_id);
|
|
7147
|
-
default: throw new Error(`Condition ${type} not supported in custom matcher`);
|
|
7148
|
-
}
|
|
7149
|
-
};
|
|
7150
|
-
//#endregion
|
|
7151
|
-
//#region src/rules/enumMatcher.ts
|
|
7152
|
-
const matchEnumOperator = (condition, matcher) => {
|
|
7153
|
-
const { primitive, operator, value } = condition;
|
|
7154
|
-
assert(primitive === "enum", primitiveError("enum"));
|
|
7155
|
-
if (typeof matcher === "undefined") return false;
|
|
7156
|
-
switch (operator) {
|
|
7157
|
-
case "is":
|
|
7158
|
-
assert(typeof value === "string", "Expected a single enum value");
|
|
7159
|
-
return matcher === value;
|
|
7160
|
-
case "is not":
|
|
7161
|
-
assert(typeof value === "string", "Expected a single enum value");
|
|
7162
|
-
return matcher !== value;
|
|
7163
|
-
case "is any of":
|
|
7164
|
-
assert(Array.isArray(value), "Expected a list of enum values");
|
|
7165
|
-
return value.includes(matcher);
|
|
7166
|
-
case "is none of":
|
|
7167
|
-
assert(Array.isArray(value), "Expected a list of enum values");
|
|
7168
|
-
return !value.includes(matcher);
|
|
7169
|
-
default: throw new Error(`Operator ${operator} not supported in enum matcher`);
|
|
7170
|
-
}
|
|
7171
|
-
};
|
|
7172
|
-
const NEGATIVE_ENUM_OPERATORS = ["is not", "is none of"];
|
|
7173
|
-
/**
|
|
7174
|
-
* Enum condition over a set (one value per tender): positive operators
|
|
7175
|
-
* match when some value matches, negative ones when none matches.
|
|
7176
|
-
*/
|
|
7177
|
-
const matchEnumSet = (condition, matcher) => {
|
|
7178
|
-
if (typeof matcher === "undefined" || typeof matcher === "string") return matchEnumOperator(condition, matcher);
|
|
7179
|
-
if (matcher.length === 0) return false;
|
|
7180
|
-
const matches = (value) => matchEnumOperator(condition, value);
|
|
7181
|
-
if (NEGATIVE_ENUM_OPERATORS.includes(condition.operator)) return matcher.every(matches);
|
|
7182
|
-
return matcher.some(matches);
|
|
7183
|
-
};
|
|
7184
|
-
const paymentCollectAt = (condition, collectAt) => {
|
|
7185
|
-
const matcherType = "Payment collect at";
|
|
7186
|
-
assert(condition.primitive === "enum", primitiveError("enum"));
|
|
7187
|
-
assert(condition.type === matcherType, typeError(matcherType));
|
|
7188
|
-
return matchEnumSet(condition, collectAt);
|
|
7189
|
-
};
|
|
7190
|
-
const paymentMethod = (condition, method) => {
|
|
7191
|
-
const matcherType = "Payment method";
|
|
7192
|
-
assert(condition.primitive === "enum", primitiveError("enum"));
|
|
7193
|
-
assert(condition.type === matcherType, typeError(matcherType));
|
|
7194
|
-
return matchEnumSet(condition, method);
|
|
7195
|
-
};
|
|
7196
|
-
const paymentStatus = (condition, status) => {
|
|
7197
|
-
const matcherType = "Payment status";
|
|
7198
|
-
assert(condition.primitive === "enum", primitiveError("enum"));
|
|
7199
|
-
assert(condition.type === matcherType, typeError(matcherType));
|
|
7200
|
-
return matchEnumOperator(condition, status);
|
|
7201
|
-
};
|
|
7202
|
-
const CONDITION_INTERPRETATION_DEFAULT = "server";
|
|
7203
|
-
const isInterpretationCompatible = (interpretation, runtime, fallback) => {
|
|
7204
|
-
if (!runtime) return true;
|
|
7205
|
-
const value = interpretation ?? fallback;
|
|
7206
|
-
if (value === "both") return true;
|
|
7207
|
-
return value === runtime;
|
|
7208
|
-
};
|
|
7209
|
-
//#endregion
|
|
7210
|
-
//#region src/rules/numberMatcher.ts
|
|
7211
|
-
const matchNumberOperator = (condition, matcher) => {
|
|
7212
|
-
const { primitive, operator, value } = condition;
|
|
7213
|
-
assert(primitive === "number", primitiveError("number"));
|
|
7214
|
-
if (typeof matcher === "undefined") return false;
|
|
7215
|
-
assert(typeof value === "number", "Invalid number value");
|
|
7216
|
-
switch (operator) {
|
|
7217
|
-
case "equal to": return matcher === value;
|
|
7218
|
-
case "not equal to": return matcher !== value;
|
|
7219
|
-
case "greater than": return matcher > value;
|
|
7220
|
-
case "greater than or equal to": return matcher >= value;
|
|
7221
|
-
case "less than": return matcher < value;
|
|
7222
|
-
case "less than or equal to": return matcher <= value;
|
|
7223
|
-
default: throw new Error(`Operator ${operator} not supported in number matcher`);
|
|
7224
|
-
}
|
|
7225
|
-
};
|
|
7226
|
-
const orderIncludesProduct = (condition, quantity) => {
|
|
7227
|
-
const { type, primitive, value } = condition;
|
|
7228
|
-
const matcherType = "Order includes product";
|
|
7229
|
-
assert(primitive === "number", primitiveError("number"));
|
|
7230
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7231
|
-
assert(value != null, "Invalid value");
|
|
7232
|
-
return matchNumberOperator(condition, quantity ?? 0);
|
|
7233
|
-
};
|
|
7234
|
-
const cartProductCount = (condition, count) => {
|
|
7235
|
-
const { type, primitive, value } = condition;
|
|
7236
|
-
const matcherType = "Cart product count";
|
|
7237
|
-
assert(primitive === "number", primitiveError("number"));
|
|
7238
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7239
|
-
assert(value != null, "Invalid value");
|
|
7240
|
-
return matchNumberOperator(condition, count);
|
|
7241
|
-
};
|
|
7242
|
-
const orderIncludesProductWithTag$1 = (condition, matcher) => {
|
|
7243
|
-
const { type, primitive, value } = condition;
|
|
7244
|
-
const matcherType = "Order includes product with tag";
|
|
7245
|
-
assert(primitive === "number", primitiveError("number"));
|
|
7246
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7247
|
-
assert(value != null, "Invalid value");
|
|
7248
|
-
return matchNumberOperator(condition, matcher);
|
|
7249
|
-
};
|
|
7250
|
-
//#endregion
|
|
7251
|
-
//#region src/rules/stringCondition.utils.ts
|
|
7252
|
-
const getCaseInsensitive = (condition) => {
|
|
7253
|
-
return condition.caseInsensitive ?? false;
|
|
7254
|
-
};
|
|
7255
|
-
const normalizeStringForMatch = (value, caseInsensitive) => {
|
|
7256
|
-
if (caseInsensitive) return value.toLowerCase();
|
|
7257
|
-
return value;
|
|
7258
|
-
};
|
|
7259
|
-
const normalizePairForMatch = (matcher, expected, caseInsensitive) => {
|
|
7260
|
-
return {
|
|
7261
|
-
matcher: normalizeStringForMatch(matcher, caseInsensitive),
|
|
7262
|
-
expected: normalizeStringForMatch(expected, caseInsensitive)
|
|
7263
|
-
};
|
|
7264
|
-
};
|
|
7265
|
-
//#endregion
|
|
7266
|
-
//#region src/rules/stringMatcher.ts
|
|
7267
|
-
const matchStringOperator = (condition, matcher) => {
|
|
7268
|
-
const { primitive, operator, value } = condition;
|
|
7269
|
-
assert(primitive === "string", primitiveError("string"));
|
|
7270
|
-
if (typeof matcher === "undefined") return false;
|
|
7271
|
-
const caseInsensitive = getCaseInsensitive(condition);
|
|
7272
|
-
const pair = normalizePairForMatch(matcher, String(value), caseInsensitive);
|
|
7273
|
-
const normalizedMatcher = pair.matcher;
|
|
7274
|
-
const normalizedExpected = pair.expected;
|
|
7275
|
-
switch (operator) {
|
|
7276
|
-
case "equal to": return normalizedMatcher === normalizedExpected;
|
|
7277
|
-
case "not equal to": return normalizedMatcher !== normalizedExpected;
|
|
7278
|
-
case "starts with": return normalizedMatcher.startsWith(normalizedExpected);
|
|
7279
|
-
case "ends with": return normalizedMatcher.endsWith(normalizedExpected);
|
|
7280
|
-
case "contains": return normalizedMatcher.includes(normalizedExpected);
|
|
7281
|
-
default: throw new Error(`Operator ${operator} not supported in string matcher`);
|
|
7282
|
-
}
|
|
7283
|
-
};
|
|
7284
|
-
const orderIncludesProductWithTag = (condition, matcher) => {
|
|
7285
|
-
const { type, primitive, value } = condition;
|
|
7286
|
-
const matcherType = "Order includes product with tag";
|
|
7287
|
-
assert(primitive === "string", primitiveError("string"));
|
|
7288
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7289
|
-
assert(value != null, "Invalid value");
|
|
7290
|
-
return matchStringOperator(condition, matcher);
|
|
7291
|
-
};
|
|
7292
|
-
//#endregion
|
|
7293
|
-
//#region src/rules/monitoringMatcher.ts
|
|
7294
|
-
const windowOf = (condition) => {
|
|
7295
|
-
if (!("window_minutes" in condition)) return void 0;
|
|
7296
|
-
return condition.window_minutes;
|
|
7297
|
-
};
|
|
7298
|
-
const accumulatedEvents = (condition, count) => {
|
|
7299
|
-
const { type, primitive, value } = condition;
|
|
7300
|
-
const matcherType = "Accumulated events";
|
|
7301
|
-
assert(primitive === "number", primitiveError("number"));
|
|
7302
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7303
|
-
assert(value != null, "Invalid value");
|
|
7304
|
-
if (typeof count === "undefined") return false;
|
|
7305
|
-
return matchNumberOperator(condition, count);
|
|
7306
|
-
};
|
|
7307
|
-
const apiErrorRate = (condition, rate) => {
|
|
7308
|
-
const { type, primitive, value } = condition;
|
|
7309
|
-
const matcherType = "API error rate";
|
|
7310
|
-
assert(primitive === "number", primitiveError("number"));
|
|
7311
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7312
|
-
assert(value != null, "Invalid value");
|
|
7313
|
-
if (typeof rate === "undefined") return false;
|
|
7314
|
-
return matchNumberOperator(condition, rate);
|
|
7315
|
-
};
|
|
7316
|
-
const httpStatus = (condition, status) => {
|
|
7317
|
-
const { type, primitive, value } = condition;
|
|
7318
|
-
const matcherType = "HTTP status";
|
|
7319
|
-
assert(primitive === "number", primitiveError("number"));
|
|
7320
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7321
|
-
assert(value != null, "Invalid value");
|
|
7322
|
-
if (typeof status === "undefined") return false;
|
|
7323
|
-
return matchNumberOperator(condition, status);
|
|
7324
|
-
};
|
|
7325
|
-
const requestPath = (condition, path) => {
|
|
7326
|
-
const { type, primitive, value } = condition;
|
|
7327
|
-
const matcherType = "Request path";
|
|
7328
|
-
assert(primitive === "string", primitiveError("string"));
|
|
7329
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7330
|
-
assert(value != null, "Invalid value");
|
|
7331
|
-
return matchStringOperator(condition, path);
|
|
7332
|
-
};
|
|
7333
|
-
const failureMessage = (condition, message) => {
|
|
7334
|
-
const { type, primitive, value } = condition;
|
|
7335
|
-
const matcherType = "Failure message";
|
|
7336
|
-
assert(primitive === "string", primitiveError("string"));
|
|
7337
|
-
assert(type === matcherType, typeError(matcherType));
|
|
7338
|
-
assert(value != null, "Invalid value");
|
|
7339
|
-
return matchStringOperator(condition, message);
|
|
7340
|
-
};
|
|
7341
|
-
const windowMinutes = (condition) => {
|
|
7342
|
-
return windowOf(condition);
|
|
7343
|
-
};
|
|
7344
|
-
//#endregion
|
|
7345
|
-
//#region src/rules/recurringDateCron.ts
|
|
7346
|
-
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) => {
|
|
7347
6967
|
(function(t, e) {
|
|
7348
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();
|
|
7349
6969
|
})(exports, (function() {
|
|
@@ -7638,7 +7258,412 @@ var import_dayjs_min = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((
|
|
|
7638
7258
|
return w(1e3 * t);
|
|
7639
7259
|
}, w.en = D[g], w.Ls = D, w.p = {}, w;
|
|
7640
7260
|
}));
|
|
7641
|
-
}))
|
|
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
|
|
7642
7667
|
import_dayjs_min.default.extend(utc);
|
|
7643
7668
|
import_dayjs_min.default.extend(timezone);
|
|
7644
7669
|
import_dayjs_min.default.extend(isBetween);
|
|
@@ -7835,6 +7860,47 @@ const currentDate = (condition, referenceDate) => {
|
|
|
7835
7860
|
return matchRecurringDateOperator(condition, current);
|
|
7836
7861
|
};
|
|
7837
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
|
|
7838
7904
|
//#region src/rules/RulesEngine.ts
|
|
7839
7905
|
var RulesEngine = class {
|
|
7840
7906
|
order;
|
|
@@ -7932,6 +7998,7 @@ var RulesEngine = class {
|
|
|
7932
7998
|
const monitoring = this.getDependency("monitoring", type);
|
|
7933
7999
|
return httpStatus(condition, monitoring.http_status);
|
|
7934
8000
|
}
|
|
8001
|
+
if (type === "Customer age") return matchNumberOperator(condition, this.customerAge());
|
|
7935
8002
|
throw new Error(`Condition ${type} not supported in number matcher`);
|
|
7936
8003
|
};
|
|
7937
8004
|
assertBoolean = (condition) => {
|
|
@@ -7989,17 +8056,40 @@ var RulesEngine = class {
|
|
|
7989
8056
|
default: throw new Error(`Condition ${type} not supported in enum matcher`);
|
|
7990
8057
|
}
|
|
7991
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
|
+
};
|
|
7992
8078
|
assertAbsoluteDate = (condition) => {
|
|
7993
8079
|
const { primitive, type } = condition;
|
|
7994
8080
|
assert(primitive === "absolute date", primitiveError("absolute date"));
|
|
7995
|
-
|
|
7996
|
-
|
|
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);
|
|
7997
8087
|
};
|
|
7998
8088
|
assertRelativeDate = (condition) => {
|
|
7999
8089
|
const { primitive, type } = condition;
|
|
8000
8090
|
assert(primitive === "relative date", primitiveError("relative date"));
|
|
8001
|
-
const
|
|
8002
|
-
|
|
8091
|
+
const fact = this.customerDate(type);
|
|
8092
|
+
return matchRelativeDateOperator(condition, fact, this.referenceDate);
|
|
8003
8093
|
};
|
|
8004
8094
|
assertRecurringDate = (condition) => {
|
|
8005
8095
|
const { primitive, type } = condition;
|
|
@@ -8104,66 +8194,6 @@ var RulesEngine = class {
|
|
|
8104
8194
|
};
|
|
8105
8195
|
};
|
|
8106
8196
|
//#endregion
|
|
8107
|
-
//#region src/rules/absoluteDateMatcher.ts
|
|
8108
|
-
const matchAbsoluteDateOperator = (condition, matcher) => {
|
|
8109
|
-
const { primitive, operator, value } = condition;
|
|
8110
|
-
assert(primitive === "absolute date", primitiveError("absolute date"));
|
|
8111
|
-
if (typeof matcher === "undefined") return false;
|
|
8112
|
-
const matcherDate = (0, import_dayjs_min.default)(matcher);
|
|
8113
|
-
const expectedDate = (0, import_dayjs_min.default)(value);
|
|
8114
|
-
switch (operator) {
|
|
8115
|
-
case "is": return matcherDate.isSame(expectedDate, "day");
|
|
8116
|
-
case "is not": return !matcherDate.isSame(expectedDate, "day");
|
|
8117
|
-
case "is before": return matcherDate.isBefore(expectedDate, "day");
|
|
8118
|
-
case "is after": return matcherDate.isAfter(expectedDate, "day");
|
|
8119
|
-
default: {
|
|
8120
|
-
const msg = `Operator ${operator} not supported in absolute date matcher`;
|
|
8121
|
-
throw new Error(msg);
|
|
8122
|
-
}
|
|
8123
|
-
}
|
|
8124
|
-
};
|
|
8125
|
-
//#endregion
|
|
8126
|
-
//#region src/rules/relativeDateMatcher.ts
|
|
8127
|
-
const matchRelativeDateOperator = (condition, matcher, referenceDate) => {
|
|
8128
|
-
const { primitive, operator, value } = condition;
|
|
8129
|
-
assert(primitive === "relative date", primitiveError("relative date"));
|
|
8130
|
-
if (typeof matcher === "undefined") return false;
|
|
8131
|
-
assert(typeof value === "string", "Invalid relative date value");
|
|
8132
|
-
const [amountString, unitRaw] = value.split("/");
|
|
8133
|
-
assert(amountString && unitRaw, "Amount & Unit required");
|
|
8134
|
-
const amount = parseInt(amountString, 10);
|
|
8135
|
-
const unit = unitRaw;
|
|
8136
|
-
const issueDate = (0, import_dayjs_min.default)(referenceDate ?? (/* @__PURE__ */ new Date()).toISOString());
|
|
8137
|
-
const matcherDate = (0, import_dayjs_min.default)(matcher);
|
|
8138
|
-
switch (operator) {
|
|
8139
|
-
case "occurred": {
|
|
8140
|
-
const endDate = issueDate.subtract(amount, unit).endOf(unit);
|
|
8141
|
-
const startDate = endDate.startOf(unit);
|
|
8142
|
-
const sameStart = matcherDate.isSame(startDate, unit);
|
|
8143
|
-
const afterStart = matcherDate.isAfter(startDate);
|
|
8144
|
-
const sameEnd = matcherDate.isSame(endDate, unit);
|
|
8145
|
-
const beforeEnd = matcherDate.isBefore(endDate);
|
|
8146
|
-
return (sameStart || afterStart) && (sameEnd || beforeEnd);
|
|
8147
|
-
}
|
|
8148
|
-
case "occurred in the last": {
|
|
8149
|
-
const targetDate = issueDate.subtract(amount, unit);
|
|
8150
|
-
const sameTarget = matcherDate.isSame(targetDate, unit);
|
|
8151
|
-
const afterTarget = matcherDate.isAfter(targetDate);
|
|
8152
|
-
const sameIssue = matcherDate.isSame(issueDate, unit);
|
|
8153
|
-
const beforeIssue = matcherDate.isBefore(issueDate);
|
|
8154
|
-
return (sameTarget || afterTarget) && (sameIssue || beforeIssue);
|
|
8155
|
-
}
|
|
8156
|
-
case "occurred more than": {
|
|
8157
|
-
const targetDate = issueDate.subtract(amount, unit);
|
|
8158
|
-
return matcherDate.isBefore(targetDate, unit);
|
|
8159
|
-
}
|
|
8160
|
-
default: {
|
|
8161
|
-
const msg = `Operator ${operator} not supported in relative date matcher`;
|
|
8162
|
-
throw new Error(msg);
|
|
8163
|
-
}
|
|
8164
|
-
}
|
|
8165
|
-
};
|
|
8166
|
-
//#endregion
|
|
8167
8197
|
//#region src/rules/checkout.ts
|
|
8168
8198
|
const DEFAULT_SHIPPING_FIELDS = {
|
|
8169
8199
|
address_line1: "required",
|
|
@@ -8616,4 +8646,4 @@ const cardPreviewGroups = (value) => {
|
|
|
8616
8646
|
return groups;
|
|
8617
8647
|
};
|
|
8618
8648
|
//#endregion
|
|
8619
|
-
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.4.
|
|
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.4.
|
|
24
|
-
"@fire-spark/types": "^0.4.
|
|
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",
|