@classytic/promo 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +41 -0
- package/dist/config-iZjn_8pp.d.mts +71 -0
- package/dist/constants-BVajdyL3.mjs +45 -0
- package/dist/domain/enums/index.d.mts +2 -0
- package/dist/domain/enums/index.mjs +2 -0
- package/dist/domain/index.d.mts +61 -0
- package/dist/domain/index.mjs +4 -0
- package/dist/domain-errors-BEkXvy5O.mjs +80 -0
- package/dist/event-emitter.port-DaodlJSG.d.mts +8 -0
- package/dist/event-types-CsTV1FKX.mjs +25 -0
- package/dist/events/index.d.mts +2 -0
- package/dist/events/index.mjs +3 -0
- package/dist/events-CprEWlN7.mjs +25 -0
- package/dist/index-B7lLH19a.d.mts +13 -0
- package/dist/index-C52zSBkI.d.mts +96 -0
- package/dist/index-Cu9iwy4v.d.mts +99 -0
- package/dist/index-J5BC20DN.d.mts +21 -0
- package/dist/index-l09KqnlE.d.mts +81 -0
- package/dist/index.d.mts +20 -0
- package/dist/index.mjs +86 -0
- package/dist/models/index.d.mts +2 -0
- package/dist/models/index.mjs +2 -0
- package/dist/models-DdBNae7h.mjs +277 -0
- package/dist/repositories/index.d.mts +2 -0
- package/dist/repositories/index.mjs +2 -0
- package/dist/repositories-DgZIY9wD.mjs +295 -0
- package/dist/results-Ca5ZCNbN.d.mts +218 -0
- package/dist/services/index.d.mts +2 -0
- package/dist/services/index.mjs +2 -0
- package/dist/services-Cz0gHrmX.mjs +815 -0
- package/dist/types/index.d.mts +3 -0
- package/dist/types/index.mjs +1 -0
- package/dist/unit-of-work.port-DaMW8WZK.d.mts +7 -0
- package/dist/voucher.port-yxfb3MHJ.d.mts +146 -0
- package/package.json +108 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Classytic
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# @classytic/promo
|
|
2
|
+
|
|
3
|
+
Promotions engine for MongoDB — discount codes, vouchers, cashback, BOGO, and rule-based pricing.
|
|
4
|
+
|
|
5
|
+
Framework-agnostic. Works with Fastify (Arc), Express, NestJS, Next.js, or any Node.js app with a Mongoose connection.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @classytic/promo
|
|
11
|
+
# Peer deps:
|
|
12
|
+
npm install mongoose@^9 @classytic/mongokit@^3.5
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { createPromoEngine } from '@classytic/promo';
|
|
19
|
+
import mongoose from 'mongoose';
|
|
20
|
+
|
|
21
|
+
const engine = createPromoEngine({
|
|
22
|
+
mongoose: mongoose.connection,
|
|
23
|
+
tenant: { field: 'organizationId' }, // multi-tenant (or false for single)
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Create a discount program
|
|
27
|
+
const program = await engine.services.program.create({
|
|
28
|
+
name: 'Summer Sale',
|
|
29
|
+
type: 'discount_code',
|
|
30
|
+
startsAt: new Date(),
|
|
31
|
+
}, ctx);
|
|
32
|
+
|
|
33
|
+
// Evaluate cart against active promotions
|
|
34
|
+
const result = await engine.services.evaluation.evaluate({
|
|
35
|
+
cart: { items, subtotal },
|
|
36
|
+
}, ctx);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## License
|
|
40
|
+
|
|
41
|
+
MIT
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { t as EventEmitterPort } from "./event-emitter.port-DaodlJSG.mjs";
|
|
2
|
+
import { Connection } from "mongoose";
|
|
3
|
+
import { PluginType } from "@classytic/mongokit";
|
|
4
|
+
|
|
5
|
+
//#region src/types/config.d.ts
|
|
6
|
+
interface TenantEnabled {
|
|
7
|
+
field?: string;
|
|
8
|
+
ref?: string;
|
|
9
|
+
type?: 'ObjectId' | 'string';
|
|
10
|
+
contextKey?: string;
|
|
11
|
+
}
|
|
12
|
+
type TenantConfig = false | TenantEnabled;
|
|
13
|
+
interface IndexDefinition {
|
|
14
|
+
fields: Record<string, 1 | -1>;
|
|
15
|
+
options?: Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
type ModelName = 'program' | 'rule' | 'reward' | 'voucher';
|
|
18
|
+
type PluginType$1 = PluginType;
|
|
19
|
+
interface RepositoryPlugins {
|
|
20
|
+
program?: PluginType[];
|
|
21
|
+
rule?: PluginType[];
|
|
22
|
+
reward?: PluginType[];
|
|
23
|
+
voucher?: PluginType[];
|
|
24
|
+
}
|
|
25
|
+
interface PromoConfig {
|
|
26
|
+
mongoose: Connection;
|
|
27
|
+
tenant?: TenantConfig;
|
|
28
|
+
evaluation?: {
|
|
29
|
+
maxStackablePromotions?: number;
|
|
30
|
+
allowExclusiveAndStackable?: boolean;
|
|
31
|
+
};
|
|
32
|
+
voucher?: {
|
|
33
|
+
codeLength?: number;
|
|
34
|
+
codePrefix?: string;
|
|
35
|
+
defaultExpiryDays?: number;
|
|
36
|
+
};
|
|
37
|
+
giftCard?: {
|
|
38
|
+
allowNegativeBalance?: boolean;
|
|
39
|
+
maxBalance?: number;
|
|
40
|
+
};
|
|
41
|
+
indexes?: Partial<Record<ModelName, IndexDefinition[]>>;
|
|
42
|
+
events?: {
|
|
43
|
+
adapter?: EventEmitterPort;
|
|
44
|
+
};
|
|
45
|
+
plugins?: RepositoryPlugins;
|
|
46
|
+
}
|
|
47
|
+
interface ResolvedTenant {
|
|
48
|
+
enabled: boolean;
|
|
49
|
+
field: string;
|
|
50
|
+
ref?: string;
|
|
51
|
+
type: 'ObjectId' | 'string';
|
|
52
|
+
contextKey: string;
|
|
53
|
+
}
|
|
54
|
+
interface ResolvedConfig {
|
|
55
|
+
evaluation: {
|
|
56
|
+
maxStackablePromotions: number;
|
|
57
|
+
allowExclusiveAndStackable: boolean;
|
|
58
|
+
};
|
|
59
|
+
voucher: {
|
|
60
|
+
codeLength: number;
|
|
61
|
+
codePrefix: string;
|
|
62
|
+
defaultExpiryDays: number | null;
|
|
63
|
+
};
|
|
64
|
+
giftCard: {
|
|
65
|
+
allowNegativeBalance: boolean;
|
|
66
|
+
maxBalance: number | null;
|
|
67
|
+
};
|
|
68
|
+
tenant: ResolvedTenant;
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
export { RepositoryPlugins as a, TenantConfig as c, PromoConfig as i, ModelName as n, ResolvedConfig as o, PluginType$1 as r, ResolvedTenant as s, IndexDefinition as t };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//#region src/constants.ts
|
|
2
|
+
const PROGRAM_TYPES = [
|
|
3
|
+
"promotion",
|
|
4
|
+
"coupon",
|
|
5
|
+
"discount_code",
|
|
6
|
+
"buy_x_get_y",
|
|
7
|
+
"gift_card"
|
|
8
|
+
];
|
|
9
|
+
const TRIGGER_MODES = ["auto", "code"];
|
|
10
|
+
const PROGRAM_STATUSES = [
|
|
11
|
+
"draft",
|
|
12
|
+
"active",
|
|
13
|
+
"paused",
|
|
14
|
+
"expired",
|
|
15
|
+
"archived"
|
|
16
|
+
];
|
|
17
|
+
const STACKING_MODES = ["exclusive", "stackable"];
|
|
18
|
+
const REWARD_TYPES = ["discount", "free_product"];
|
|
19
|
+
const DISCOUNT_MODES = ["percentage", "fixed"];
|
|
20
|
+
const DISCOUNT_SCOPES = [
|
|
21
|
+
"order",
|
|
22
|
+
"cheapest",
|
|
23
|
+
"specific_products"
|
|
24
|
+
];
|
|
25
|
+
const VOUCHER_STATUSES = [
|
|
26
|
+
"active",
|
|
27
|
+
"used",
|
|
28
|
+
"expired",
|
|
29
|
+
"cancelled"
|
|
30
|
+
];
|
|
31
|
+
const DEFAULT_TENANT_FIELD = "organizationId";
|
|
32
|
+
const DEFAULT_TENANT_TYPE = "ObjectId";
|
|
33
|
+
const PROGRAM_TRANSITIONS = {
|
|
34
|
+
draft: ["active", "archived"],
|
|
35
|
+
active: [
|
|
36
|
+
"paused",
|
|
37
|
+
"expired",
|
|
38
|
+
"archived"
|
|
39
|
+
],
|
|
40
|
+
paused: ["active", "archived"],
|
|
41
|
+
expired: ["archived"],
|
|
42
|
+
archived: []
|
|
43
|
+
};
|
|
44
|
+
//#endregion
|
|
45
|
+
export { PROGRAM_STATUSES as a, REWARD_TYPES as c, VOUCHER_STATUSES as d, DISCOUNT_SCOPES as i, STACKING_MODES as l, DEFAULT_TENANT_TYPE as n, PROGRAM_TRANSITIONS as o, DISCOUNT_MODES as r, PROGRAM_TYPES as s, DEFAULT_TENANT_FIELD as t, TRIGGER_MODES as u };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as RewardType, c as VoucherStatus, d as PROGRAM_STATUSES, f as PROGRAM_TYPES, g as VOUCHER_STATUSES, h as TRIGGER_MODES, i as ProgramType, l as DISCOUNT_MODES, m as STACKING_MODES, n as DiscountScope, o as StackingMode, p as REWARD_TYPES, r as ProgramStatus, s as TriggerMode, t as DiscountMode, u as DISCOUNT_SCOPES } from "../../index-J5BC20DN.mjs";
|
|
2
|
+
export { DISCOUNT_MODES, DISCOUNT_SCOPES, DiscountMode, DiscountScope, PROGRAM_STATUSES, PROGRAM_TYPES, ProgramStatus, ProgramType, REWARD_TYPES, RewardType, STACKING_MODES, StackingMode, TRIGGER_MODES, TriggerMode, VOUCHER_STATUSES, VoucherStatus };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as PROGRAM_STATUSES, c as REWARD_TYPES, d as VOUCHER_STATUSES, i as DISCOUNT_SCOPES, l as STACKING_MODES, r as DISCOUNT_MODES, s as PROGRAM_TYPES, u as TRIGGER_MODES } from "../../constants-BVajdyL3.mjs";
|
|
2
|
+
export { DISCOUNT_MODES, DISCOUNT_SCOPES, PROGRAM_STATUSES, PROGRAM_TYPES, REWARD_TYPES, STACKING_MODES, TRIGGER_MODES, VOUCHER_STATUSES };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { a as RewardType, c as VoucherStatus, d as PROGRAM_STATUSES, f as PROGRAM_TYPES, g as VOUCHER_STATUSES, h as TRIGGER_MODES, i as ProgramType, l as DISCOUNT_MODES, m as STACKING_MODES, n as DiscountScope, o as StackingMode, p as REWARD_TYPES, r as ProgramStatus, s as TriggerMode, t as DiscountMode, u as DISCOUNT_SCOPES } from "../index-J5BC20DN.mjs";
|
|
2
|
+
import { a as BalanceLedgerEntry, c as Rule, i as ProgramPort, l as Reward, n as RulePort, o as Voucher, r as RewardPort, s as VoucherRedemption, t as VoucherPort, u as Program } from "../voucher.port-yxfb3MHJ.mjs";
|
|
3
|
+
import { t as EventEmitterPort } from "../event-emitter.port-DaodlJSG.mjs";
|
|
4
|
+
import { n as UnitOfWork, t as TransactionSession } from "../unit-of-work.port-DaMW8WZK.mjs";
|
|
5
|
+
|
|
6
|
+
//#region src/domain/errors/base.d.ts
|
|
7
|
+
declare abstract class PromoError extends Error {
|
|
8
|
+
abstract readonly code: string;
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/domain/errors/domain-errors.d.ts
|
|
13
|
+
declare class ValidationError extends PromoError {
|
|
14
|
+
readonly code = "VALIDATION_ERROR";
|
|
15
|
+
}
|
|
16
|
+
declare class ProgramNotFoundError extends PromoError {
|
|
17
|
+
readonly code = "PROGRAM_NOT_FOUND";
|
|
18
|
+
constructor(id?: string);
|
|
19
|
+
}
|
|
20
|
+
declare class RuleNotFoundError extends PromoError {
|
|
21
|
+
readonly code = "RULE_NOT_FOUND";
|
|
22
|
+
constructor(id?: string);
|
|
23
|
+
}
|
|
24
|
+
declare class RewardNotFoundError extends PromoError {
|
|
25
|
+
readonly code = "REWARD_NOT_FOUND";
|
|
26
|
+
constructor(id?: string);
|
|
27
|
+
}
|
|
28
|
+
declare class VoucherNotFoundError extends PromoError {
|
|
29
|
+
readonly code = "VOUCHER_NOT_FOUND";
|
|
30
|
+
constructor(codeOrId?: string);
|
|
31
|
+
}
|
|
32
|
+
declare class InvalidTransitionError extends PromoError {
|
|
33
|
+
readonly code = "INVALID_TRANSITION";
|
|
34
|
+
constructor(from: string, to: string);
|
|
35
|
+
}
|
|
36
|
+
declare class VoucherExpiredError extends PromoError {
|
|
37
|
+
readonly code = "VOUCHER_EXPIRED";
|
|
38
|
+
constructor(code: string);
|
|
39
|
+
}
|
|
40
|
+
declare class VoucherExhaustedError extends PromoError {
|
|
41
|
+
readonly code = "VOUCHER_EXHAUSTED";
|
|
42
|
+
constructor(code: string);
|
|
43
|
+
}
|
|
44
|
+
declare class InsufficientBalanceError extends PromoError {
|
|
45
|
+
readonly code = "INSUFFICIENT_BALANCE";
|
|
46
|
+
constructor(code: string, available: number, requested: number);
|
|
47
|
+
}
|
|
48
|
+
declare class TenantIsolationError extends PromoError {
|
|
49
|
+
readonly code = "TENANT_ISOLATION";
|
|
50
|
+
constructor();
|
|
51
|
+
}
|
|
52
|
+
declare class DuplicateRedemptionError extends PromoError {
|
|
53
|
+
readonly code = "DUPLICATE_REDEMPTION";
|
|
54
|
+
constructor(key: string);
|
|
55
|
+
}
|
|
56
|
+
declare class EvaluationNotFoundError extends PromoError {
|
|
57
|
+
readonly code = "EVALUATION_NOT_FOUND";
|
|
58
|
+
constructor(id: string);
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
export { BalanceLedgerEntry, DISCOUNT_MODES, DISCOUNT_SCOPES, DiscountMode, DiscountScope, DuplicateRedemptionError, EvaluationNotFoundError, EventEmitterPort, InsufficientBalanceError, InvalidTransitionError, PROGRAM_STATUSES, PROGRAM_TYPES, Program, ProgramNotFoundError, ProgramPort, ProgramStatus, ProgramType, PromoError, REWARD_TYPES, Reward, RewardNotFoundError, RewardPort, RewardType, Rule, RuleNotFoundError, RulePort, STACKING_MODES, StackingMode, TRIGGER_MODES, TenantIsolationError, TransactionSession, TriggerMode, UnitOfWork, VOUCHER_STATUSES, ValidationError, Voucher, VoucherExhaustedError, VoucherExpiredError, VoucherNotFoundError, VoucherPort, VoucherRedemption, VoucherStatus };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { a as PROGRAM_STATUSES, c as REWARD_TYPES, d as VOUCHER_STATUSES, i as DISCOUNT_SCOPES, l as STACKING_MODES, r as DISCOUNT_MODES, s as PROGRAM_TYPES, u as TRIGGER_MODES } from "../constants-BVajdyL3.mjs";
|
|
2
|
+
import { a as ProgramNotFoundError, c as TenantIsolationError, d as VoucherExpiredError, f as VoucherNotFoundError, i as InvalidTransitionError, l as ValidationError, n as EvaluationNotFoundError, o as RewardNotFoundError, p as PromoError, r as InsufficientBalanceError, s as RuleNotFoundError, t as DuplicateRedemptionError, u as VoucherExhaustedError } from "../domain-errors-BEkXvy5O.mjs";
|
|
3
|
+
import "./enums/index.mjs";
|
|
4
|
+
export { DISCOUNT_MODES, DISCOUNT_SCOPES, DuplicateRedemptionError, EvaluationNotFoundError, InsufficientBalanceError, InvalidTransitionError, PROGRAM_STATUSES, PROGRAM_TYPES, ProgramNotFoundError, PromoError, REWARD_TYPES, RewardNotFoundError, RuleNotFoundError, STACKING_MODES, TRIGGER_MODES, TenantIsolationError, VOUCHER_STATUSES, ValidationError, VoucherExhaustedError, VoucherExpiredError, VoucherNotFoundError };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
//#region src/domain/errors/base.ts
|
|
2
|
+
var PromoError = class extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = this.constructor.name;
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/domain/errors/domain-errors.ts
|
|
10
|
+
var ValidationError = class extends PromoError {
|
|
11
|
+
code = "VALIDATION_ERROR";
|
|
12
|
+
};
|
|
13
|
+
var ProgramNotFoundError = class extends PromoError {
|
|
14
|
+
code = "PROGRAM_NOT_FOUND";
|
|
15
|
+
constructor(id) {
|
|
16
|
+
super(id ? `Program '${id}' not found` : "Program not found");
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
var RuleNotFoundError = class extends PromoError {
|
|
20
|
+
code = "RULE_NOT_FOUND";
|
|
21
|
+
constructor(id) {
|
|
22
|
+
super(id ? `Rule '${id}' not found` : "Rule not found");
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var RewardNotFoundError = class extends PromoError {
|
|
26
|
+
code = "REWARD_NOT_FOUND";
|
|
27
|
+
constructor(id) {
|
|
28
|
+
super(id ? `Reward '${id}' not found` : "Reward not found");
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
var VoucherNotFoundError = class extends PromoError {
|
|
32
|
+
code = "VOUCHER_NOT_FOUND";
|
|
33
|
+
constructor(codeOrId) {
|
|
34
|
+
super(codeOrId ? `Voucher '${codeOrId}' not found` : "Voucher not found");
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
var InvalidTransitionError = class extends PromoError {
|
|
38
|
+
code = "INVALID_TRANSITION";
|
|
39
|
+
constructor(from, to) {
|
|
40
|
+
super(`Cannot transition from '${from}' to '${to}'`);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
var VoucherExpiredError = class extends PromoError {
|
|
44
|
+
code = "VOUCHER_EXPIRED";
|
|
45
|
+
constructor(code) {
|
|
46
|
+
super(`Voucher '${code}' has expired`);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var VoucherExhaustedError = class extends PromoError {
|
|
50
|
+
code = "VOUCHER_EXHAUSTED";
|
|
51
|
+
constructor(code) {
|
|
52
|
+
super(`Voucher '${code}' has reached its usage limit`);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var InsufficientBalanceError = class extends PromoError {
|
|
56
|
+
code = "INSUFFICIENT_BALANCE";
|
|
57
|
+
constructor(code, available, requested) {
|
|
58
|
+
super(`Voucher '${code}' has insufficient balance: ${available} available, ${requested} requested`);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var TenantIsolationError = class extends PromoError {
|
|
62
|
+
code = "TENANT_ISOLATION";
|
|
63
|
+
constructor() {
|
|
64
|
+
super("Tenant context is required but was not provided");
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var DuplicateRedemptionError = class extends PromoError {
|
|
68
|
+
code = "DUPLICATE_REDEMPTION";
|
|
69
|
+
constructor(key) {
|
|
70
|
+
super(`Duplicate redemption detected for idempotency key '${key}'`);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
var EvaluationNotFoundError = class extends PromoError {
|
|
74
|
+
code = "EVALUATION_NOT_FOUND";
|
|
75
|
+
constructor(id) {
|
|
76
|
+
super(`Evaluation '${id}' not found or already committed`);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
//#endregion
|
|
80
|
+
export { ProgramNotFoundError as a, TenantIsolationError as c, VoucherExpiredError as d, VoucherNotFoundError as f, InvalidTransitionError as i, ValidationError as l, EvaluationNotFoundError as n, RewardNotFoundError as o, PromoError as p, InsufficientBalanceError as r, RuleNotFoundError as s, DuplicateRedemptionError as t, VoucherExhaustedError as u };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/domain/ports/event-emitter.port.d.ts
|
|
2
|
+
interface EventEmitterPort {
|
|
3
|
+
emit(event: string, payload: unknown): void;
|
|
4
|
+
on(event: string, handler: (payload: unknown) => void | Promise<void>): void;
|
|
5
|
+
off(event: string, handler: (payload: unknown) => void | Promise<void>): void;
|
|
6
|
+
}
|
|
7
|
+
//#endregion
|
|
8
|
+
export { EventEmitterPort as t };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/events/event-types.ts
|
|
2
|
+
const PromoEvents = {
|
|
3
|
+
PROGRAM_CREATED: "promo.program.created",
|
|
4
|
+
PROGRAM_ACTIVATED: "promo.program.activated",
|
|
5
|
+
PROGRAM_PAUSED: "promo.program.paused",
|
|
6
|
+
PROGRAM_ARCHIVED: "promo.program.archived",
|
|
7
|
+
RULE_ADDED: "promo.rule.added",
|
|
8
|
+
RULE_UPDATED: "promo.rule.updated",
|
|
9
|
+
RULE_REMOVED: "promo.rule.removed",
|
|
10
|
+
REWARD_ADDED: "promo.reward.added",
|
|
11
|
+
REWARD_UPDATED: "promo.reward.updated",
|
|
12
|
+
REWARD_REMOVED: "promo.reward.removed",
|
|
13
|
+
VOUCHER_GENERATED: "promo.voucher.generated",
|
|
14
|
+
VOUCHER_REDEEMED: "promo.voucher.redeemed",
|
|
15
|
+
VOUCHER_CANCELLED: "promo.voucher.cancelled",
|
|
16
|
+
VOUCHER_EXPIRED: "promo.voucher.expired",
|
|
17
|
+
GIFT_CARD_SPENT: "promo.gift_card.spent",
|
|
18
|
+
GIFT_CARD_TOPPED_UP: "promo.gift_card.topped_up",
|
|
19
|
+
GIFT_CARD_EXHAUSTED: "promo.gift_card.exhausted",
|
|
20
|
+
EVALUATION_COMPLETED: "promo.evaluation.completed",
|
|
21
|
+
EVALUATION_COMMITTED: "promo.evaluation.committed",
|
|
22
|
+
EVALUATION_ROLLED_BACK: "promo.evaluation.rolled_back"
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { PromoEvents as t };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as ProgramLifecyclePayload, c as RulePayload, d as VoucherRedeemedPayload, f as InProcessEventBus, i as GiftCardToppedUpPayload, l as VoucherGeneratedPayload, n as EvaluationCompletedPayload, o as PromoEvents, r as GiftCardSpentPayload, s as RewardPayload, t as EvaluationCommittedPayload, u as VoucherLifecyclePayload } from "../index-C52zSBkI.mjs";
|
|
2
|
+
export { EvaluationCommittedPayload, EvaluationCompletedPayload, GiftCardSpentPayload, GiftCardToppedUpPayload, InProcessEventBus, ProgramLifecyclePayload, PromoEvents, RewardPayload, RulePayload, VoucherGeneratedPayload, VoucherLifecyclePayload, VoucherRedeemedPayload };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/events/event-bus.ts
|
|
2
|
+
var InProcessEventBus = class {
|
|
3
|
+
handlers = /* @__PURE__ */ new Map();
|
|
4
|
+
emit(event, payload) {
|
|
5
|
+
const set = this.handlers.get(event);
|
|
6
|
+
if (!set) return;
|
|
7
|
+
for (const handler of set) try {
|
|
8
|
+
const result = handler(payload);
|
|
9
|
+
if (result && typeof result.catch === "function") result.catch(() => {});
|
|
10
|
+
} catch {}
|
|
11
|
+
}
|
|
12
|
+
on(event, handler) {
|
|
13
|
+
let set = this.handlers.get(event);
|
|
14
|
+
if (!set) {
|
|
15
|
+
set = /* @__PURE__ */ new Set();
|
|
16
|
+
this.handlers.set(event, set);
|
|
17
|
+
}
|
|
18
|
+
set.add(handler);
|
|
19
|
+
}
|
|
20
|
+
off(event, handler) {
|
|
21
|
+
this.handlers.get(event)?.delete(handler);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { InProcessEventBus as t };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { n as ModelName, s as ResolvedTenant, t as IndexDefinition } from "./config-iZjn_8pp.mjs";
|
|
2
|
+
import { Connection, Model } from "mongoose";
|
|
3
|
+
|
|
4
|
+
//#region src/models/index.d.ts
|
|
5
|
+
interface PromoModels {
|
|
6
|
+
Program: Model<unknown>;
|
|
7
|
+
Rule: Model<unknown>;
|
|
8
|
+
Reward: Model<unknown>;
|
|
9
|
+
Voucher: Model<unknown>;
|
|
10
|
+
}
|
|
11
|
+
declare function createModels(connection: Connection, tenant: ResolvedTenant, indexes?: Partial<Record<ModelName, IndexDefinition[]>>): PromoModels;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { createModels as n, PromoModels as t };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { t as EventEmitterPort } from "./event-emitter.port-DaodlJSG.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/events/event-bus.d.ts
|
|
4
|
+
type Handler = (payload: unknown) => void | Promise<void>;
|
|
5
|
+
declare class InProcessEventBus implements EventEmitterPort {
|
|
6
|
+
private handlers;
|
|
7
|
+
emit(event: string, payload: unknown): void;
|
|
8
|
+
on(event: string, handler: Handler): void;
|
|
9
|
+
off(event: string, handler: Handler): void;
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/events/event-types.d.ts
|
|
13
|
+
declare const PromoEvents: {
|
|
14
|
+
readonly PROGRAM_CREATED: "promo.program.created";
|
|
15
|
+
readonly PROGRAM_ACTIVATED: "promo.program.activated";
|
|
16
|
+
readonly PROGRAM_PAUSED: "promo.program.paused";
|
|
17
|
+
readonly PROGRAM_ARCHIVED: "promo.program.archived";
|
|
18
|
+
readonly RULE_ADDED: "promo.rule.added";
|
|
19
|
+
readonly RULE_UPDATED: "promo.rule.updated";
|
|
20
|
+
readonly RULE_REMOVED: "promo.rule.removed";
|
|
21
|
+
readonly REWARD_ADDED: "promo.reward.added";
|
|
22
|
+
readonly REWARD_UPDATED: "promo.reward.updated";
|
|
23
|
+
readonly REWARD_REMOVED: "promo.reward.removed";
|
|
24
|
+
readonly VOUCHER_GENERATED: "promo.voucher.generated";
|
|
25
|
+
readonly VOUCHER_REDEEMED: "promo.voucher.redeemed";
|
|
26
|
+
readonly VOUCHER_CANCELLED: "promo.voucher.cancelled";
|
|
27
|
+
readonly VOUCHER_EXPIRED: "promo.voucher.expired";
|
|
28
|
+
readonly GIFT_CARD_SPENT: "promo.gift_card.spent";
|
|
29
|
+
readonly GIFT_CARD_TOPPED_UP: "promo.gift_card.topped_up";
|
|
30
|
+
readonly GIFT_CARD_EXHAUSTED: "promo.gift_card.exhausted";
|
|
31
|
+
readonly EVALUATION_COMPLETED: "promo.evaluation.completed";
|
|
32
|
+
readonly EVALUATION_COMMITTED: "promo.evaluation.committed";
|
|
33
|
+
readonly EVALUATION_ROLLED_BACK: "promo.evaluation.rolled_back";
|
|
34
|
+
};
|
|
35
|
+
interface ProgramLifecyclePayload {
|
|
36
|
+
programId: string;
|
|
37
|
+
programType: string;
|
|
38
|
+
status: string;
|
|
39
|
+
actorId?: string;
|
|
40
|
+
}
|
|
41
|
+
interface RulePayload {
|
|
42
|
+
programId: string;
|
|
43
|
+
ruleId: string;
|
|
44
|
+
actorId?: string;
|
|
45
|
+
}
|
|
46
|
+
interface RewardPayload {
|
|
47
|
+
programId: string;
|
|
48
|
+
rewardId: string;
|
|
49
|
+
actorId?: string;
|
|
50
|
+
}
|
|
51
|
+
interface VoucherGeneratedPayload {
|
|
52
|
+
programId: string;
|
|
53
|
+
voucherIds: string[];
|
|
54
|
+
codes: string[];
|
|
55
|
+
count: number;
|
|
56
|
+
actorId?: string;
|
|
57
|
+
}
|
|
58
|
+
interface VoucherRedeemedPayload {
|
|
59
|
+
voucherId: string;
|
|
60
|
+
code: string;
|
|
61
|
+
orderId: string;
|
|
62
|
+
discountAmount: number;
|
|
63
|
+
customerId?: string;
|
|
64
|
+
}
|
|
65
|
+
interface VoucherLifecyclePayload {
|
|
66
|
+
voucherId: string;
|
|
67
|
+
code: string;
|
|
68
|
+
status: string;
|
|
69
|
+
}
|
|
70
|
+
interface GiftCardSpentPayload {
|
|
71
|
+
voucherId: string;
|
|
72
|
+
code: string;
|
|
73
|
+
amount: number;
|
|
74
|
+
remainingBalance: number;
|
|
75
|
+
orderId: string;
|
|
76
|
+
}
|
|
77
|
+
interface GiftCardToppedUpPayload {
|
|
78
|
+
voucherId: string;
|
|
79
|
+
code: string;
|
|
80
|
+
amount: number;
|
|
81
|
+
newBalance: number;
|
|
82
|
+
}
|
|
83
|
+
interface EvaluationCompletedPayload {
|
|
84
|
+
evaluationId: string;
|
|
85
|
+
totalDiscount: number;
|
|
86
|
+
programsApplied: number;
|
|
87
|
+
codesUsed: string[];
|
|
88
|
+
isPreview: boolean;
|
|
89
|
+
}
|
|
90
|
+
interface EvaluationCommittedPayload {
|
|
91
|
+
evaluationId: string;
|
|
92
|
+
orderId: string;
|
|
93
|
+
totalDiscount: number;
|
|
94
|
+
}
|
|
95
|
+
//#endregion
|
|
96
|
+
export { ProgramLifecyclePayload as a, RulePayload as c, VoucherRedeemedPayload as d, InProcessEventBus as f, GiftCardToppedUpPayload as i, VoucherGeneratedPayload as l, EvaluationCompletedPayload as n, PromoEvents as o, GiftCardSpentPayload as r, RewardPayload as s, EvaluationCommittedPayload as t, VoucherLifecyclePayload as u };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { c as Rule, i as ProgramPort, l as Reward, n as RulePort, o as Voucher, r as RewardPort, t as VoucherPort, u as Program } from "./voucher.port-yxfb3MHJ.mjs";
|
|
2
|
+
import { t as EventEmitterPort } from "./event-emitter.port-DaodlJSG.mjs";
|
|
3
|
+
import { n as UnitOfWork } from "./unit-of-work.port-DaMW8WZK.mjs";
|
|
4
|
+
import { o as ResolvedConfig } from "./config-iZjn_8pp.mjs";
|
|
5
|
+
import { t as PromoRepositories } from "./index-l09KqnlE.mjs";
|
|
6
|
+
import { C as UpdateRuleInput, S as UpdateRewardInput, _ as GiftCardTopUpInput, a as GiftCardBalance, b as RedeemVoucherInput, c as VoucherValidation, d as CreateRewardInput, f as CreateRuleInput, g as GiftCardSpendInput, h as GenerateSingleCodeInput, m as GenerateCodesInput, o as PaginatedResult, p as EvaluateInput, r as EvaluationResult, t as CommitResult, u as CreateProgramInput, v as ListQuery, x as UpdateProgramInput, y as PromoContext } from "./results-Ca5ZCNbN.mjs";
|
|
7
|
+
|
|
8
|
+
//#region src/services/evaluation.service.d.ts
|
|
9
|
+
declare class EvaluationService {
|
|
10
|
+
private programPort;
|
|
11
|
+
private rulePort;
|
|
12
|
+
private rewardPort;
|
|
13
|
+
private voucherPort;
|
|
14
|
+
private unitOfWork;
|
|
15
|
+
private events;
|
|
16
|
+
private config;
|
|
17
|
+
private pendingEvaluations;
|
|
18
|
+
constructor(programPort: ProgramPort, rulePort: RulePort, rewardPort: RewardPort, voucherPort: VoucherPort, unitOfWork: UnitOfWork, events: EventEmitterPort, config: ResolvedConfig);
|
|
19
|
+
evaluate(input: EvaluateInput, ctx: PromoContext): Promise<EvaluationResult>;
|
|
20
|
+
preview(input: EvaluateInput, ctx: PromoContext): Promise<EvaluationResult>;
|
|
21
|
+
commit(evaluationId: string, orderId: string, ctx: PromoContext): Promise<CommitResult>;
|
|
22
|
+
rollback(evaluationId: string, ctx: PromoContext): Promise<void>;
|
|
23
|
+
private doEvaluate;
|
|
24
|
+
private isCustomerEligible;
|
|
25
|
+
private matchBestRule;
|
|
26
|
+
private matchSingleRule;
|
|
27
|
+
private filterRewardsByRule;
|
|
28
|
+
private computeDiscount;
|
|
29
|
+
private getEligibleItems;
|
|
30
|
+
private describeDiscount;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/services/program.service.d.ts
|
|
34
|
+
interface FullProgram extends Program {
|
|
35
|
+
rules: Rule[];
|
|
36
|
+
rewards: Reward[];
|
|
37
|
+
}
|
|
38
|
+
declare class ProgramService {
|
|
39
|
+
private programPort;
|
|
40
|
+
private rulePort;
|
|
41
|
+
private rewardPort;
|
|
42
|
+
private events;
|
|
43
|
+
private config;
|
|
44
|
+
constructor(programPort: ProgramPort, rulePort: RulePort, rewardPort: RewardPort, events: EventEmitterPort, config: ResolvedConfig);
|
|
45
|
+
create(input: CreateProgramInput, ctx: PromoContext): Promise<Program>;
|
|
46
|
+
getById(id: string, ctx: PromoContext): Promise<Program>;
|
|
47
|
+
list(query: ListQuery, ctx: PromoContext): Promise<PaginatedResult<Program>>;
|
|
48
|
+
update(id: string, input: UpdateProgramInput, ctx: PromoContext): Promise<Program>;
|
|
49
|
+
activate(id: string, ctx: PromoContext): Promise<Program>;
|
|
50
|
+
pause(id: string, ctx: PromoContext): Promise<Program>;
|
|
51
|
+
archive(id: string, ctx: PromoContext): Promise<Program>;
|
|
52
|
+
private transition;
|
|
53
|
+
addRule(programId: string, input: CreateRuleInput, ctx: PromoContext): Promise<Rule>;
|
|
54
|
+
updateRule(programId: string, ruleId: string, input: UpdateRuleInput, ctx: PromoContext): Promise<Rule>;
|
|
55
|
+
removeRule(programId: string, ruleId: string, ctx: PromoContext): Promise<void>;
|
|
56
|
+
listRules(programId: string, ctx: PromoContext): Promise<Rule[]>;
|
|
57
|
+
addReward(programId: string, input: CreateRewardInput, ctx: PromoContext): Promise<Reward>;
|
|
58
|
+
updateReward(programId: string, rewardId: string, input: UpdateRewardInput, ctx: PromoContext): Promise<Reward>;
|
|
59
|
+
removeReward(programId: string, rewardId: string, ctx: PromoContext): Promise<void>;
|
|
60
|
+
listRewards(programId: string, ctx: PromoContext): Promise<Reward[]>;
|
|
61
|
+
getFullProgram(id: string, ctx: PromoContext): Promise<FullProgram>;
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/services/voucher.service.d.ts
|
|
65
|
+
declare class VoucherService {
|
|
66
|
+
private voucherPort;
|
|
67
|
+
private programPort;
|
|
68
|
+
private unitOfWork;
|
|
69
|
+
private events;
|
|
70
|
+
private config;
|
|
71
|
+
constructor(voucherPort: VoucherPort, programPort: ProgramPort, unitOfWork: UnitOfWork, events: EventEmitterPort, config: ResolvedConfig);
|
|
72
|
+
generateCodes(input: GenerateCodesInput, ctx: PromoContext): Promise<Voucher[]>;
|
|
73
|
+
generateSingleCode(input: GenerateSingleCodeInput, ctx: PromoContext): Promise<Voucher>;
|
|
74
|
+
validateCode(code: string, ctx: PromoContext): Promise<VoucherValidation>;
|
|
75
|
+
getByCode(code: string, ctx: PromoContext): Promise<Voucher | null>;
|
|
76
|
+
getById(id: string, ctx: PromoContext): Promise<Voucher | null>;
|
|
77
|
+
cancel(id: string, ctx: PromoContext): Promise<Voucher>;
|
|
78
|
+
redeem(input: RedeemVoucherInput, ctx: PromoContext): Promise<Voucher>;
|
|
79
|
+
getBalance(code: string, ctx: PromoContext): Promise<GiftCardBalance>;
|
|
80
|
+
spend(input: GiftCardSpendInput, ctx: PromoContext): Promise<GiftCardBalance>;
|
|
81
|
+
topUp(input: GiftCardTopUpInput, ctx: PromoContext): Promise<GiftCardBalance>;
|
|
82
|
+
expireExpired(ctx: PromoContext): Promise<number>;
|
|
83
|
+
private assertVoucherUsable;
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/services/index.d.ts
|
|
87
|
+
interface PromoServices {
|
|
88
|
+
program: ProgramService;
|
|
89
|
+
voucher: VoucherService;
|
|
90
|
+
evaluation: EvaluationService;
|
|
91
|
+
}
|
|
92
|
+
declare function createServices(deps: {
|
|
93
|
+
repositories: PromoRepositories;
|
|
94
|
+
unitOfWork: UnitOfWork;
|
|
95
|
+
events: EventEmitterPort;
|
|
96
|
+
config: ResolvedConfig;
|
|
97
|
+
}): PromoServices;
|
|
98
|
+
//#endregion
|
|
99
|
+
export { ProgramService as a, FullProgram as i, createServices as n, EvaluationService as o, VoucherService as r, PromoServices as t };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/constants.d.ts
|
|
2
|
+
declare const PROGRAM_TYPES: readonly ["promotion", "coupon", "discount_code", "buy_x_get_y", "gift_card"];
|
|
3
|
+
declare const TRIGGER_MODES: readonly ["auto", "code"];
|
|
4
|
+
declare const PROGRAM_STATUSES: readonly ["draft", "active", "paused", "expired", "archived"];
|
|
5
|
+
declare const STACKING_MODES: readonly ["exclusive", "stackable"];
|
|
6
|
+
declare const REWARD_TYPES: readonly ["discount", "free_product"];
|
|
7
|
+
declare const DISCOUNT_MODES: readonly ["percentage", "fixed"];
|
|
8
|
+
declare const DISCOUNT_SCOPES: readonly ["order", "cheapest", "specific_products"];
|
|
9
|
+
declare const VOUCHER_STATUSES: readonly ["active", "used", "expired", "cancelled"];
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/domain/enums/index.d.ts
|
|
12
|
+
type ProgramType = (typeof PROGRAM_TYPES)[number];
|
|
13
|
+
type TriggerMode = (typeof TRIGGER_MODES)[number];
|
|
14
|
+
type ProgramStatus = (typeof PROGRAM_STATUSES)[number];
|
|
15
|
+
type StackingMode = (typeof STACKING_MODES)[number];
|
|
16
|
+
type RewardType = (typeof REWARD_TYPES)[number];
|
|
17
|
+
type DiscountMode = (typeof DISCOUNT_MODES)[number];
|
|
18
|
+
type DiscountScope = (typeof DISCOUNT_SCOPES)[number];
|
|
19
|
+
type VoucherStatus = (typeof VOUCHER_STATUSES)[number];
|
|
20
|
+
//#endregion
|
|
21
|
+
export { RewardType as a, VoucherStatus as c, PROGRAM_STATUSES as d, PROGRAM_TYPES as f, VOUCHER_STATUSES as g, TRIGGER_MODES as h, ProgramType as i, DISCOUNT_MODES as l, STACKING_MODES as m, DiscountScope as n, StackingMode as o, REWARD_TYPES as p, ProgramStatus as r, TriggerMode as s, DiscountMode as t, DISCOUNT_SCOPES as u };
|