@classytic/loyalty 0.2.2 → 0.3.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/CHANGELOG.md +101 -0
- package/README.md +69 -35
- package/dist/{config-96Sl6oNJ.d.mts → config-BdSyAdnF.d.mts} +10 -0
- package/dist/events/index.d.mts +1 -1
- package/dist/events/index.mjs +2 -2
- package/dist/{events-BtZOdiPw.mjs → events-DN5_p6T0.mjs} +1 -1
- package/dist/{index-CMSNfGos.d.mts → index-BN96EDac.d.mts} +19 -4
- package/dist/{index-xLLJ7jLy.d.mts → index-CnESLipu.d.mts} +1 -1
- package/dist/{index-Bo5YQ_fu.d.mts → index-D20Cn4KB.d.mts} +23 -0
- package/dist/index.d.mts +154 -6
- package/dist/index.mjs +26 -18
- package/dist/{loyalty-events-Cyt9TwX6.mjs → loyalty-events-C38JA3cU.mjs} +4 -2
- package/dist/models/index.d.mts +513 -465
- package/dist/models/index.mjs +1 -1
- package/dist/{models-uhh9c-7k.mjs → models-Ddh62lgO.mjs} +29 -2
- package/dist/repositories/index.d.mts +1 -1
- package/dist/repositories/index.mjs +1 -1
- package/dist/{repositories-CZlhTYVp.mjs → repositories-C_p8asCn.mjs} +307 -155
- package/dist/types/index.d.mts +2 -2
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,107 @@
|
|
|
3
3
|
Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
4
4
|
adhering to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
5
|
|
|
6
|
+
## [0.3.0] — 2026-06-13
|
|
7
|
+
|
|
8
|
+
Stack upgrade to mongokit 3.16 / repo-core 0.6 / primitives 0.7.2 plus
|
|
9
|
+
verified correctness + PACKAGE_RULES compliance fixes. No new features.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **Stack actually resolves the new floor now (CRITICAL).** The committed
|
|
14
|
+
lockfile silently served `@classytic/mongokit@3.13.0` +
|
|
15
|
+
`@classytic/repo-core@0.4.0` despite the `^3.13.3` / `^0.4.2` pins, so the
|
|
16
|
+
whole test suite ran against the OLD stack. Clean reinstall + peer/dev bumps
|
|
17
|
+
pin the resolved tree to mongokit **3.16.0**, repo-core **0.6.0**, primitives
|
|
18
|
+
**0.7.2** (verified with `npm ls`).
|
|
19
|
+
- **§P8 outbox is now session-bound on the transactional verbs.** `earnPoints`,
|
|
20
|
+
`adjustPoints`, `reserve`, `confirm`, `release`, and `reverseConfirmed`
|
|
21
|
+
previously dispatched the event AFTER `withTransaction` committed, so
|
|
22
|
+
`outbox.save` ran OUTSIDE the unit of work — the business doc could commit
|
|
23
|
+
while the durable event row was lost (or vice-versa). Dispatch now runs
|
|
24
|
+
INSIDE the transaction (outbox row commits atomically with the write) and the
|
|
25
|
+
transport publish is deferred to a post-commit flush (`runLoyaltyUnit` +
|
|
26
|
+
`[PENDING_EVENTS]`), so in-process subscribers never see a state that may roll
|
|
27
|
+
back. A new integration test asserts the save is session-bound and that a
|
|
28
|
+
failing `outbox.save` rolls the `$inc` balance write back.
|
|
29
|
+
- **Redemption slot no longer permanently blocked after release.** The
|
|
30
|
+
`{ ownerType, ownerId }` unique index spanned ALL statuses, so a released /
|
|
31
|
+
reversed reservation E11000-blocked any retry on the same order (payment
|
|
32
|
+
retry, cart re-attempt) forever. It is now a **partial** unique index scoped
|
|
33
|
+
to active statuses (`reserved`, `confirmed`) — terminal states free the slot
|
|
34
|
+
(PACKAGE_RULES rule 32/38/39). Re-reserve-after-release and second-active-
|
|
35
|
+
reserve-blocked tests added.
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- **`UnmanagedSessionError` guard (§P8).** When a host passes `ctx.session`
|
|
40
|
+
(it owns the outer transaction) without attaching a `[PENDING_EVENTS]` queue,
|
|
41
|
+
dispatch throws BEFORE any side effect instead of publishing / saving outside
|
|
42
|
+
the host's unit of work (which would leak ghost events on rollback).
|
|
43
|
+
`PENDING_EVENTS` and `flushPendingLoyaltyEvents` are exported for hosts that
|
|
44
|
+
manage their own transaction.
|
|
45
|
+
- **Capability boot gate.** `createLoyaltyEngine()` asserts the backend declares
|
|
46
|
+
the `transactions` capability (repo-core 0.6 / mongokit 3.16+) at engine
|
|
47
|
+
creation — fail loud at boot rather than with a cryptic driver error on the
|
|
48
|
+
first transactional call. `allowNonTransactional: true` opts out for local-dev
|
|
49
|
+
on standalone Mongo. New `LoyaltyCapabilityError`.
|
|
50
|
+
- **Cooperative cancellation in cron loops.** `processExpirations`, `awardBatch`,
|
|
51
|
+
`cleanupExpired` (redemption + referral), `evaluateAll`, and
|
|
52
|
+
`sweepExpiredOverrides` check `ctx.signal` via `throwIfAborted` between items —
|
|
53
|
+
abortable batches; each per-item write is its own committed unit of work, so
|
|
54
|
+
aborting never rolls back already-processed rows. `LoyaltyContext` gained
|
|
55
|
+
`signal?: AbortSignal`.
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
59
|
+
- **Domain errors implement `HttpError`** from `@classytic/repo-core/errors`
|
|
60
|
+
(was a bespoke `httpStatus` shape). Clean break: `httpStatus` → `status`,
|
|
61
|
+
and codes are now hierarchical + lowercase (`loyalty.<entity>.<problem>`,
|
|
62
|
+
e.g. `loyalty.member.not_found`, `loyalty.points.insufficient`) so arc
|
|
63
|
+
serializes them to `ErrorContract` correctly. No deprecation alias.
|
|
64
|
+
- **Peer-dep ranges raised** — `@classytic/mongokit >=3.16.0`,
|
|
65
|
+
`@classytic/repo-core >=0.6.0` (`>=` style). devDeps bumped to match and
|
|
66
|
+
primitives devDep pinned to `^0.7.2`.
|
|
67
|
+
- **`exactOptionalPropertyTypes: true`** enabled in tsconfig (mandatory per
|
|
68
|
+
PACKAGE_RULES Build Hygiene / P10); fixed the three pre-existing optional-vs-
|
|
69
|
+
undefined violations it surfaced.
|
|
70
|
+
- **tsconfig no longer emits sourcemaps or declaration maps**
|
|
71
|
+
(`sourceMap: false`, `declarationMap: false`) — both leaked source. tsdown
|
|
72
|
+
was already correct; tsconfig now matches.
|
|
73
|
+
|
|
74
|
+
## [0.2.3] — 2026-05-08
|
|
75
|
+
|
|
76
|
+
### Fixed
|
|
77
|
+
|
|
78
|
+
- **§P8 dispatch — `outbox.save` failures now propagate.** [src/events/dispatch.ts](src/events/dispatch.ts)
|
|
79
|
+
previously caught `outbox.save` errors and swallowed them silently. Per
|
|
80
|
+
PACKAGE_RULES §P8 the outbox row is the durability anchor for the
|
|
81
|
+
transactional-outbox pattern: if the save fails, the caller's transaction
|
|
82
|
+
MUST roll back so the business doc and the event row land atomically.
|
|
83
|
+
Swallowing the failure meant a parent doc could commit while the event
|
|
84
|
+
vanished, with no relay row to recover from. Fix re-throws after logging.
|
|
85
|
+
`events.publish` failures continue to be swallowed (correctly, per §P8 —
|
|
86
|
+
the host's outbox relay re-publishes from the durable row), but now log
|
|
87
|
+
before swallowing so failures are observable.
|
|
88
|
+
- **`DispatchDeps.logger` field added.** Optional. Both arms of the
|
|
89
|
+
dispatch helper log through it when set, surfacing failures in the host's
|
|
90
|
+
observability stack without coupling the package to a specific logger.
|
|
91
|
+
|
|
92
|
+
### Changed
|
|
93
|
+
|
|
94
|
+
- **Peer-dep ranges tightened** — `@classytic/mongokit` `>=3.13.3`
|
|
95
|
+
(was `>=3.13.0`) and `@classytic/repo-core` `>=0.4.2` (was `>=0.4.0`).
|
|
96
|
+
3.13.3 preserves `errorLabels` on `MongoServerError` so concurrent
|
|
97
|
+
transactional writes (the `claim()`-based redemption + tier-eval paths)
|
|
98
|
+
retry correctly under contention; 0.4.2 adds the typed `FindAllOptions`
|
|
99
|
+
shape with `limit?` for callers that need a bounded non-paginated read.
|
|
100
|
+
|
|
101
|
+
### Internal
|
|
102
|
+
|
|
103
|
+
- Replaced 4 `arr.find()!` non-null assertions in [src/index.ts](src/index.ts)
|
|
104
|
+
+ scenario tests with explicit narrowing — TS now proves what the runtime
|
|
105
|
+
shape already guaranteed.
|
|
106
|
+
|
|
6
107
|
## [0.2.0]
|
|
7
108
|
|
|
8
109
|
Structural rewrite on top of the 0.1.0 shape. The public API is now a
|
package/README.md
CHANGED
|
@@ -9,7 +9,8 @@ Framework-agnostic. Works with Fastify (Arc), Express, NestJS, Next.js, or any N
|
|
|
9
9
|
```bash
|
|
10
10
|
npm install @classytic/loyalty
|
|
11
11
|
# Peer deps:
|
|
12
|
-
npm install mongoose@^9
|
|
12
|
+
npm install mongoose@^9 zod@^4 \
|
|
13
|
+
@classytic/mongokit@^3.16 @classytic/repo-core@^0.6 @classytic/primitives@^0.7
|
|
13
14
|
```
|
|
14
15
|
|
|
15
16
|
## Quick Start
|
|
@@ -26,15 +27,15 @@ const engine = createLoyaltyEngine({
|
|
|
26
27
|
referral: { referrerRewardPoints: 200, refereeRewardPoints: 100 },
|
|
27
28
|
});
|
|
28
29
|
|
|
29
|
-
//
|
|
30
|
-
const member = await engine.
|
|
30
|
+
// Domain verbs live on the repositories (no service layer — PACKAGE_RULES §1–§3)
|
|
31
|
+
const member = await engine.repositories.member.enroll(
|
|
31
32
|
{ externalId: 'cust_1', externalType: 'customer', cardId: 'MBR-001' },
|
|
32
|
-
{ actorId: 'admin' },
|
|
33
|
+
{ actorId: 'admin', organizationId: 'org_1' },
|
|
33
34
|
);
|
|
34
35
|
|
|
35
|
-
await engine.
|
|
36
|
+
await engine.repositories.pointTransaction.earnPoints(
|
|
36
37
|
{ memberId: member._id, points: 500, description: 'Order #123' },
|
|
37
|
-
{ actorId: 'system' },
|
|
38
|
+
{ actorId: 'system', organizationId: 'org_1' },
|
|
38
39
|
);
|
|
39
40
|
```
|
|
40
41
|
|
|
@@ -42,21 +43,29 @@ await engine.services.ledger.earnPoints(
|
|
|
42
43
|
|
|
43
44
|
```
|
|
44
45
|
createLoyaltyEngine(config)
|
|
45
|
-
-> models 6 Mongoose models (auto-created,
|
|
46
|
-
-> repositories 6 repos (tenant-scoped CRUD
|
|
47
|
-
->
|
|
48
|
-
->
|
|
46
|
+
-> models 6 Mongoose models (auto-created, collision-safe)
|
|
47
|
+
-> repositories 6 repos = the public API (tenant-scoped CRUD + domain verbs)
|
|
48
|
+
-> events Arc-compatible transport (in-process fallback, or plug your own)
|
|
49
|
+
-> syncIndexes() deploy-time index builder
|
|
49
50
|
```
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
The capability boot gate asserts the backend supports multi-document
|
|
53
|
+
`transactions` (MongoDB replica set / sharded cluster) — the point-mutation
|
|
54
|
+
verbs are transactional. For local-dev on standalone Mongo, pass
|
|
55
|
+
`allowNonTransactional: true` (accepts partial-write risk).
|
|
52
56
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
|
59
|
-
|
|
57
|
+
### Repositories (the public API)
|
|
58
|
+
|
|
59
|
+
Domain verbs live on the repositories themselves — there is no service layer
|
|
60
|
+
(PACKAGE_RULES §1–§3). Access via `engine.repositories.<name>`.
|
|
61
|
+
|
|
62
|
+
| Repository | Purpose |
|
|
63
|
+
|------------|---------|
|
|
64
|
+
| `member` | Enroll, deactivate, reactivate, suspend |
|
|
65
|
+
| `pointTransaction` | Earn points, adjust points, `awardBatch`, process expirations |
|
|
66
|
+
| `earningRule` | CRUD earning rules (order, action, category, tier_bonus); `evaluateOrder` / `evaluateAction` |
|
|
67
|
+
| `tierDefinition` | CRUD tier definitions, evaluate members, set/clear/sweep overrides |
|
|
68
|
+
| `redemption` | Validate, reserve, confirm, release, reverse, cleanup expired reservations |
|
|
60
69
|
| `referral` | Generate codes, record referrals, approve/reject, rate limiting |
|
|
61
70
|
|
|
62
71
|
### Key Design Decisions
|
|
@@ -148,42 +157,67 @@ as `PlatformConfig.membership.pointsPerAmount` or `amountPerPoint`. See
|
|
|
148
157
|
|
|
149
158
|
## Events
|
|
150
159
|
|
|
160
|
+
`engine.events` is an arc-compatible `EventTransport` — subscribe glob-style.
|
|
161
|
+
Drop in any arc transport (Memory/Redis/Kafka) via `eventTransport` in config.
|
|
162
|
+
|
|
151
163
|
```typescript
|
|
152
164
|
import { LoyaltyEvents } from '@classytic/loyalty/events';
|
|
153
165
|
|
|
154
|
-
engine.events.
|
|
155
|
-
engine.events.
|
|
156
|
-
engine.events.
|
|
166
|
+
await engine.events.subscribe(LoyaltyEvents.POINTS_EARNED, (e) => { /* sync projection */ });
|
|
167
|
+
await engine.events.subscribe('loyalty.tier.*', (e) => { /* notify customer */ });
|
|
168
|
+
await engine.events.subscribe(LoyaltyEvents.REFERRAL_REWARDED, (e) => { /* notify both */ });
|
|
157
169
|
```
|
|
158
170
|
|
|
171
|
+
For durable delivery, wire a host-owned `outbox` (PACKAGE_RULES §5.5 + §P8):
|
|
172
|
+
the engine saves the event row inside the same transaction as the business
|
|
173
|
+
write (atomic), then publishes after commit. Hosts that own the outer
|
|
174
|
+
transaction must attach a `[PENDING_EVENTS]` queue and call
|
|
175
|
+
`flushPendingLoyaltyEvents(transport, queue)` after their commit, or a bare
|
|
176
|
+
`ctx.session` throws `UnmanagedSessionError`.
|
|
177
|
+
|
|
159
178
|
## Production Cron Jobs
|
|
160
179
|
|
|
180
|
+
All cron loops accept `ctx.signal` (an `AbortSignal`) and check it between
|
|
181
|
+
items, so a shutdown / timeout aborts cleanly between committed units of work.
|
|
182
|
+
|
|
161
183
|
```typescript
|
|
184
|
+
const ctx = { actorId: 'cron', organizationId: 'org_1', signal: ac.signal };
|
|
185
|
+
|
|
162
186
|
// Every 5 min — release expired point reservations
|
|
163
|
-
await engine.
|
|
187
|
+
await engine.repositories.redemption.cleanupExpired(ctx);
|
|
164
188
|
|
|
165
189
|
// Every hour — expire points past their expiresAt
|
|
166
|
-
await engine.
|
|
190
|
+
await engine.repositories.pointTransaction.processExpirations(ctx);
|
|
167
191
|
|
|
168
192
|
// Every 24h — re-evaluate all member tiers
|
|
169
|
-
await engine.
|
|
193
|
+
await engine.repositories.tierDefinition.evaluateAll(ctx);
|
|
194
|
+
|
|
195
|
+
// Hourly — clear expired tier overrides
|
|
196
|
+
await engine.repositories.tierDefinition.sweepExpiredOverrides(ctx);
|
|
170
197
|
```
|
|
171
198
|
|
|
172
199
|
## Error Codes
|
|
173
200
|
|
|
201
|
+
All domain errors implement `HttpError` from `@classytic/repo-core/errors`
|
|
202
|
+
(arc serializes them to the canonical `ErrorContract`). Codes are hierarchical
|
|
203
|
+
and lowercase.
|
|
204
|
+
|
|
174
205
|
| Code | HTTP | When |
|
|
175
206
|
|------|------|------|
|
|
176
|
-
| `
|
|
177
|
-
| `
|
|
178
|
-
| `
|
|
179
|
-
| `
|
|
180
|
-
| `
|
|
181
|
-
| `
|
|
182
|
-
| `
|
|
183
|
-
| `
|
|
184
|
-
| `
|
|
185
|
-
| `
|
|
186
|
-
| `
|
|
207
|
+
| `loyalty.member.not_found` | 404 | Member doesn't exist |
|
|
208
|
+
| `loyalty.member.already_enrolled` | 409 | Duplicate enrollment |
|
|
209
|
+
| `loyalty.member.inactive` | 422 | Operating on inactive member |
|
|
210
|
+
| `loyalty.points.insufficient` | 400 | Not enough points to deduct |
|
|
211
|
+
| `loyalty.earning_rule.not_found` | 404 | Earning rule doesn't exist |
|
|
212
|
+
| `loyalty.tier.not_found` | 404 | Tier definition doesn't exist |
|
|
213
|
+
| `loyalty.redemption.expired` | 410 | Reservation TTL exceeded |
|
|
214
|
+
| `loyalty.referral.duplicate` | 409 | Referee already referred |
|
|
215
|
+
| `loyalty.referral.self` | 422 | Referring yourself |
|
|
216
|
+
| `loyalty.referral.circular` | 422 | A↔B circular referral |
|
|
217
|
+
| `loyalty.referral.limit_exceeded` | 429 | Max referrals per period |
|
|
218
|
+
| `loyalty.tenant.isolation` | 403 | Missing tenant context |
|
|
219
|
+
| `loyalty.transaction.unmanaged_session` | 400 | `ctx.session` passed without a `[PENDING_EVENTS]` queue |
|
|
220
|
+
| `loyalty.engine.missing_capabilities` | 500 | Backend lacks `transactions` at boot |
|
|
187
221
|
|
|
188
222
|
## AI Agent Skill
|
|
189
223
|
|
|
@@ -99,6 +99,16 @@ interface LoyaltyConfig {
|
|
|
99
99
|
outbox?: OutboxStore | undefined;
|
|
100
100
|
notifications?: NotificationAdapter | undefined;
|
|
101
101
|
plugins?: RepositoryPlugins | undefined;
|
|
102
|
+
/**
|
|
103
|
+
* Skip the boot-time capability gate that asserts the backend supports
|
|
104
|
+
* multi-document `transactions`. Loyalty's point-mutation verbs (earn,
|
|
105
|
+
* adjust, reserve, confirm, release, reverse, processExpirations) run inside
|
|
106
|
+
* `withTransaction`; without a replica set those writes are NOT atomic.
|
|
107
|
+
* Leave `false` (default) in production — set `true` only for local-dev on
|
|
108
|
+
* standalone Mongo, accepting partial-write risk. See PACKAGE_RULES
|
|
109
|
+
* "Runtime capabilities".
|
|
110
|
+
*/
|
|
111
|
+
allowNonTransactional?: boolean | undefined;
|
|
102
112
|
}
|
|
103
113
|
interface ResolvedTenant {
|
|
104
114
|
enabled: boolean;
|
package/dist/events/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as PointsExpired, B as ReferralRewarded, C as MemberDeactivated, D as MultiTierReferralAwarded, E as MemberSuspended, F as RedemptionReserved, G as TierUpgraded, H as TierGraceStarted, I as RedemptionReversed, J as InProcessLoyaltyBusOptions, K as loyaltyEventDefinitions, L as ReferralApproved, M as PointsRedeemed, N as RedemptionConfirmed, O as PointsAdjusted, P as RedemptionReleased, R as ReferralRecorded, S as LoyaltyEventSchema, T as MemberReactivated, U as TierOverrideCleared, V as TierDowngraded, W as TierOverrideExpired, Y as createEvent, _ as TierChangedPayload, a as LoyaltyEventMap, b as LoyaltyEventDefinition, c as LoyaltyEvents, d as PointsAdjustedPayload, f as PointsEarnedPayload, g as ReferralPayload, h as RedemptionReversedPayload, i as CampaignAwardedPayload, j as PointsExpiringSoon, k as PointsEarned, l as MemberLifecyclePayload, m as RedemptionPayload, n as EventHandler, o as LoyaltyEventName, p as PointsExpiredPayload, q as InProcessLoyaltyBus, r as EventTransport, s as LoyaltyEventPayload, t as DomainEvent, u as MultiTierReferralAwardedPayload, v as TierOverrideLifecyclePayload, w as MemberEnrolled, x as LoyaltyEventPayloadOf, y as CampaignAwarded, z as ReferralRejected } from "../index-
|
|
1
|
+
import { A as PointsExpired, B as ReferralRewarded, C as MemberDeactivated, D as MultiTierReferralAwarded, E as MemberSuspended, F as RedemptionReserved, G as TierUpgraded, H as TierGraceStarted, I as RedemptionReversed, J as InProcessLoyaltyBusOptions, K as loyaltyEventDefinitions, L as ReferralApproved, M as PointsRedeemed, N as RedemptionConfirmed, O as PointsAdjusted, P as RedemptionReleased, R as ReferralRecorded, S as LoyaltyEventSchema, T as MemberReactivated, U as TierOverrideCleared, V as TierDowngraded, W as TierOverrideExpired, Y as createEvent, _ as TierChangedPayload, a as LoyaltyEventMap, b as LoyaltyEventDefinition, c as LoyaltyEvents, d as PointsAdjustedPayload, f as PointsEarnedPayload, g as ReferralPayload, h as RedemptionReversedPayload, i as CampaignAwardedPayload, j as PointsExpiringSoon, k as PointsEarned, l as MemberLifecyclePayload, m as RedemptionPayload, n as EventHandler, o as LoyaltyEventName, p as PointsExpiredPayload, q as InProcessLoyaltyBus, r as EventTransport, s as LoyaltyEventPayload, t as DomainEvent, u as MultiTierReferralAwardedPayload, v as TierOverrideLifecyclePayload, w as MemberEnrolled, x as LoyaltyEventPayloadOf, y as CampaignAwarded, z as ReferralRejected } from "../index-CnESLipu.mjs";
|
|
2
2
|
export { CampaignAwarded, CampaignAwardedPayload, DomainEvent, EventHandler, EventTransport, InProcessLoyaltyBus, InProcessLoyaltyBusOptions, LoyaltyEventDefinition, LoyaltyEventMap, LoyaltyEventName, LoyaltyEventPayload, LoyaltyEventPayloadOf, LoyaltyEventSchema, LoyaltyEvents, MemberDeactivated, MemberEnrolled, MemberLifecyclePayload, MemberReactivated, MemberSuspended, MultiTierReferralAwarded, MultiTierReferralAwardedPayload, PointsAdjusted, PointsAdjustedPayload, PointsEarned, PointsEarnedPayload, PointsExpired, PointsExpiredPayload, PointsExpiringSoon, PointsRedeemed, RedemptionConfirmed, RedemptionPayload, RedemptionReleased, RedemptionReserved, RedemptionReversed, RedemptionReversedPayload, ReferralApproved, ReferralPayload, ReferralRecorded, ReferralRejected, ReferralRewarded, TierChangedPayload, TierDowngraded, TierGraceStarted, TierOverrideCleared, TierOverrideExpired, TierOverrideLifecyclePayload, TierUpgraded, createEvent, loyaltyEventDefinitions };
|
package/dist/events/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { C as TierOverrideExpired, E as InProcessLoyaltyBus, S as TierOverrideCleared, T as loyaltyEventDefinitions, _ as ReferralRecorded, a as MemberSuspended, b as TierDowngraded, c as PointsEarned, d as PointsRedeemed, f as RedemptionConfirmed, g as ReferralApproved, h as RedemptionReversed, i as MemberReactivated, l as PointsExpired, m as RedemptionReserved, n as MemberDeactivated, o as MultiTierReferralAwarded, p as RedemptionReleased, r as MemberEnrolled, s as PointsAdjusted, t as CampaignAwarded, u as PointsExpiringSoon, v as ReferralRejected, w as TierUpgraded, x as TierGraceStarted, y as ReferralRewarded } from "../events-
|
|
2
|
-
import { n as createEvent, t as LoyaltyEvents } from "../loyalty-events-
|
|
1
|
+
import { C as TierOverrideExpired, E as InProcessLoyaltyBus, S as TierOverrideCleared, T as loyaltyEventDefinitions, _ as ReferralRecorded, a as MemberSuspended, b as TierDowngraded, c as PointsEarned, d as PointsRedeemed, f as RedemptionConfirmed, g as ReferralApproved, h as RedemptionReversed, i as MemberReactivated, l as PointsExpired, m as RedemptionReserved, n as MemberDeactivated, o as MultiTierReferralAwarded, p as RedemptionReleased, r as MemberEnrolled, s as PointsAdjusted, t as CampaignAwarded, u as PointsExpiringSoon, v as ReferralRejected, w as TierUpgraded, x as TierGraceStarted, y as ReferralRewarded } from "../events-DN5_p6T0.mjs";
|
|
2
|
+
import { n as createEvent, t as LoyaltyEvents } from "../loyalty-events-C38JA3cU.mjs";
|
|
3
3
|
export { CampaignAwarded, InProcessLoyaltyBus, LoyaltyEvents, MemberDeactivated, MemberEnrolled, MemberReactivated, MemberSuspended, MultiTierReferralAwarded, PointsAdjusted, PointsEarned, PointsExpired, PointsExpiringSoon, PointsRedeemed, RedemptionConfirmed, RedemptionReleased, RedemptionReserved, RedemptionReversed, ReferralApproved, ReferralRecorded, ReferralRejected, ReferralRewarded, TierDowngraded, TierGraceStarted, TierOverrideCleared, TierOverrideExpired, TierUpgraded, createEvent, loyaltyEventDefinitions };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as LoyaltyEvents } from "./loyalty-events-
|
|
1
|
+
import { t as LoyaltyEvents } from "./loyalty-events-C38JA3cU.mjs";
|
|
2
2
|
import { createEvent, matchEventPattern } from "@classytic/primitives/events";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
//#region src/events/in-process-bus.ts
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { i as ReferralStatus, r as RedemptionStatus } from "./index-DMK8a98J.mjs";
|
|
2
|
-
import { S as EarningRule, _ as TierEvaluationBatch, d as EarningResult, f as ExpirationResult, g as TierEvaluation, h as RedemptionValidation, o as LoyaltyContext, v as PointTransaction, x as TierDefinition, y as Member } from "./index-
|
|
2
|
+
import { S as EarningRule, _ as TierEvaluationBatch, d as EarningResult, f as ExpirationResult, g as TierEvaluation, h as RedemptionValidation, o as LoyaltyContext, v as PointTransaction, x as TierDefinition, y as Member } from "./index-D20Cn4KB.mjs";
|
|
3
3
|
import { t as NotificationAdapter } from "./notification.port-CJ9nXj0k.mjs";
|
|
4
|
-
import { c as ResolvedTenant, o as RepositoryPlugins, s as ResolvedConfig } from "./config-
|
|
4
|
+
import { c as ResolvedTenant, o as RepositoryPlugins, s as ResolvedConfig } from "./config-BdSyAdnF.mjs";
|
|
5
5
|
import { LoyaltyModels } from "./models/index.mjs";
|
|
6
6
|
import { EventTransport } from "@classytic/primitives/events";
|
|
7
7
|
import { ClientSession, Model } from "mongoose";
|
|
8
8
|
import { PluginType, Repository } from "@classytic/mongokit";
|
|
9
9
|
import { OutboxStore } from "@classytic/primitives/outbox";
|
|
10
|
+
import { ApprovalChain } from "@classytic/primitives/approval";
|
|
10
11
|
|
|
11
12
|
//#region src/domain/ports/unit-of-work.port.d.ts
|
|
12
13
|
interface UnitOfWork {
|
|
@@ -64,6 +65,7 @@ declare class PointTransactionRepository extends Repository<PointTransaction> {
|
|
|
64
65
|
constructor(model: Model<PointTransaction>, plugins?: PluginType[]);
|
|
65
66
|
/** Session-bound outbox save + transport publish (PACKAGE_RULES §5.5 + P8). */
|
|
66
67
|
private _dispatch;
|
|
68
|
+
private get _dispatchDeps();
|
|
67
69
|
/**
|
|
68
70
|
* Auto-inject tenant id from options into doc payload. Matches
|
|
69
71
|
* `@classytic/order` / `@classytic/promo` — see MemberRepository.
|
|
@@ -192,6 +194,18 @@ interface Redemption {
|
|
|
192
194
|
*/
|
|
193
195
|
initialBalance?: number;
|
|
194
196
|
remainingBalance?: number;
|
|
197
|
+
/**
|
|
198
|
+
* Optional multi-step approval chain (P7 canonical — same shape as
|
|
199
|
+
* `@classytic/purchase` / `@classytic/order`). High-value redemptions
|
|
200
|
+
* (gift cards above threshold, large discount vouchers) gate on
|
|
201
|
+
* `isApproved(approvals)` before `confirm()` flips the FSM. Single-tier
|
|
202
|
+
* flows skip the chain entirely.
|
|
203
|
+
*/
|
|
204
|
+
approvals?: ApprovalChain | null;
|
|
205
|
+
/** Snapshot of the matrix policy id that produced this chain (host-managed). */
|
|
206
|
+
approvalPolicyId?: string | null;
|
|
207
|
+
/** Version of the matrix policy at submit time — frozen for audit. */
|
|
208
|
+
approvalPolicyVersion?: number | null;
|
|
195
209
|
createdAt: Date;
|
|
196
210
|
updatedAt: Date;
|
|
197
211
|
}
|
|
@@ -211,6 +225,7 @@ declare class RedemptionRepository extends Repository<Redemption> {
|
|
|
211
225
|
createMany(docs: Parameters<Repository<Redemption>['createMany']>[0], options?: Parameters<Repository<Redemption>['createMany']>[1]): Promise<Redemption[]>;
|
|
212
226
|
/** Session-bound outbox save + transport publish (PACKAGE_RULES §5.5 + P8). */
|
|
213
227
|
private _dispatch;
|
|
228
|
+
private get _dispatchDeps();
|
|
214
229
|
/**
|
|
215
230
|
* Atomic compare-and-set status transition.
|
|
216
231
|
* Returns null if the current status doesn't match `expectedStatus` (CAS mismatch).
|
|
@@ -353,8 +368,8 @@ interface LoyaltyRepositories {
|
|
|
353
368
|
}
|
|
354
369
|
interface CreateRepositoriesInput {
|
|
355
370
|
models: LoyaltyModels;
|
|
356
|
-
plugins?: RepositoryPlugins;
|
|
357
|
-
tenant?: ResolvedTenant;
|
|
371
|
+
plugins?: RepositoryPlugins | undefined;
|
|
372
|
+
tenant?: ResolvedTenant | undefined;
|
|
358
373
|
config: ResolvedConfig;
|
|
359
374
|
events: EventTransport;
|
|
360
375
|
outbox?: OutboxStore | undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as LoyaltyContext } from "./index-
|
|
1
|
+
import { o as LoyaltyContext } from "./index-D20Cn4KB.mjs";
|
|
2
2
|
import { DomainEvent, DomainEvent as DomainEvent$1, EventHandler, EventHandler as EventHandler$1, EventTransport, EventTransport as EventTransport$1 } from "@classytic/primitives/events";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { a as RuleStatus, n as MemberStatus, o as RuleType, s as TransactionType } from "./index-DMK8a98J.mjs";
|
|
2
2
|
import { OperationContext } from "@classytic/primitives/context";
|
|
3
|
+
import { ApprovalChain } from "@classytic/primitives/approval";
|
|
3
4
|
|
|
4
5
|
//#region src/domain/entities/earning-rule.d.ts
|
|
5
6
|
interface EarningConditions {
|
|
@@ -124,6 +125,19 @@ interface PointTransaction {
|
|
|
124
125
|
expiredAt?: Date;
|
|
125
126
|
metadata?: Record<string, unknown>;
|
|
126
127
|
actorId: string;
|
|
128
|
+
/**
|
|
129
|
+
* Optional multi-step approval chain (P7 canonical — same shape as
|
|
130
|
+
* `@classytic/purchase` / `@classytic/order`). Manual `adjustment` /
|
|
131
|
+
* `bonus` transactions above a host-defined threshold gate on
|
|
132
|
+
* `isApproved(approvals)` before the journal entry is committed.
|
|
133
|
+
* Automatic transactions (earn from purchase, expiry sweeps) skip the
|
|
134
|
+
* chain entirely — single-tier flows leave this null.
|
|
135
|
+
*/
|
|
136
|
+
approvals?: ApprovalChain | null;
|
|
137
|
+
/** Snapshot of the matrix policy id that produced this chain (host-managed). */
|
|
138
|
+
approvalPolicyId?: string | null;
|
|
139
|
+
/** Version of the matrix policy at submit time — frozen for audit. */
|
|
140
|
+
approvalPolicyVersion?: number | null;
|
|
127
141
|
createdAt: Date;
|
|
128
142
|
}
|
|
129
143
|
//#endregion
|
|
@@ -203,6 +217,15 @@ interface LoyaltyContext extends OperationContext {
|
|
|
203
217
|
organizationId?: string;
|
|
204
218
|
/** Override program ID for multi-program setups. */
|
|
205
219
|
programId?: string;
|
|
220
|
+
/**
|
|
221
|
+
* Cooperative cancellation for long-running cron loops (point expiry,
|
|
222
|
+
* reservation cleanup, tier evaluation, override sweeps, campaign award).
|
|
223
|
+
* Checked at each loop boundary via `throwIfAborted` — aborting between
|
|
224
|
+
* iterations never rolls back an already-committed transaction, and each
|
|
225
|
+
* per-item write is its own unit of work. Threaded to mongokit round-trips
|
|
226
|
+
* by hosts that pass it; mongokit (3.16+) honours it at every op boundary.
|
|
227
|
+
*/
|
|
228
|
+
signal?: AbortSignal | undefined;
|
|
206
229
|
/** Dynamic tenant key support — e.g. `{ companyId: '...' }` — when a host
|
|
207
230
|
* configured a custom `contextKey`. */
|
|
208
231
|
[key: string]: unknown;
|
package/dist/index.d.mts
CHANGED
|
@@ -1,11 +1,159 @@
|
|
|
1
|
-
import { S as EarningRule, a as EnrollInput, b as MemberBalance, c as UpdateTierInput, d as EarningResult, g as TierEvaluation, h as RedemptionValidation, i as EarnPointsInput, l as BalanceSummary, n as CreateEarningRuleInput, o as LoyaltyContext, r as CreateTierInput, s as UpdateEarningRuleInput, t as AdjustPointsInput, v as PointTransaction, x as TierDefinition, y as Member } from "./index-
|
|
1
|
+
import { S as EarningRule, a as EnrollInput, b as MemberBalance, c as UpdateTierInput, d as EarningResult, g as TierEvaluation, h as RedemptionValidation, i as EarnPointsInput, l as BalanceSummary, n as CreateEarningRuleInput, o as LoyaltyContext, r as CreateTierInput, s as UpdateEarningRuleInput, t as AdjustPointsInput, v as PointTransaction, x as TierDefinition, y as Member } from "./index-D20Cn4KB.mjs";
|
|
2
2
|
import { t as NotificationAdapter } from "./notification.port-CJ9nXj0k.mjs";
|
|
3
|
-
import { c as ResolvedTenant, n as LoyaltyConfig, r as LoyaltyModelName, s as ResolvedConfig } from "./config-
|
|
4
|
-
import { A as PointsExpired, B as ReferralRewarded, C as MemberDeactivated, E as MemberSuspended, F as RedemptionReserved, G as TierUpgraded, H as TierGraceStarted, J as InProcessLoyaltyBusOptions, K as loyaltyEventDefinitions, L as ReferralApproved, M as PointsRedeemed, N as RedemptionConfirmed, O as PointsAdjusted, P as RedemptionReleased, R as ReferralRecorded, S as LoyaltyEventSchema, T as MemberReactivated, V as TierDowngraded, Y as createEvent, _ as TierChangedPayload, a as LoyaltyEventMap, b as LoyaltyEventDefinition, c as LoyaltyEvents, d as PointsAdjustedPayload, f as PointsEarnedPayload, g as ReferralPayload, j as PointsExpiringSoon, k as PointsEarned, l as MemberLifecyclePayload, m as RedemptionPayload, o as LoyaltyEventName, p as PointsExpiredPayload, q as InProcessLoyaltyBus, s as LoyaltyEventPayload, w as MemberEnrolled, x as LoyaltyEventPayloadOf, z as ReferralRejected } from "./index-
|
|
3
|
+
import { c as ResolvedTenant, n as LoyaltyConfig, r as LoyaltyModelName, s as ResolvedConfig } from "./config-BdSyAdnF.mjs";
|
|
4
|
+
import { A as PointsExpired, B as ReferralRewarded, C as MemberDeactivated, E as MemberSuspended, F as RedemptionReserved, G as TierUpgraded, H as TierGraceStarted, J as InProcessLoyaltyBusOptions, K as loyaltyEventDefinitions, L as ReferralApproved, M as PointsRedeemed, N as RedemptionConfirmed, O as PointsAdjusted, P as RedemptionReleased, R as ReferralRecorded, S as LoyaltyEventSchema, T as MemberReactivated, V as TierDowngraded, Y as createEvent, _ as TierChangedPayload, a as LoyaltyEventMap, b as LoyaltyEventDefinition, c as LoyaltyEvents, d as PointsAdjustedPayload, f as PointsEarnedPayload, g as ReferralPayload, j as PointsExpiringSoon, k as PointsEarned, l as MemberLifecyclePayload, m as RedemptionPayload, o as LoyaltyEventName, p as PointsExpiredPayload, q as InProcessLoyaltyBus, s as LoyaltyEventPayload, w as MemberEnrolled, x as LoyaltyEventPayloadOf, z as ReferralRejected } from "./index-CnESLipu.mjs";
|
|
5
5
|
import { LoyaltyModels } from "./models/index.mjs";
|
|
6
|
-
import { a as ReferralRepository, c as Redemption, d as MemberRepository, f as UnitOfWork, i as TierDefinitionRepository, l as EarningRuleRepository, n as LoyaltyRepositories, o as Referral, s as RedemptionRepository, u as PointTransactionRepository } from "./index-
|
|
7
|
-
import { DomainEvent, EventHandler, EventTransport, EventTransport as EventTransport$1 } from "@classytic/primitives/events";
|
|
6
|
+
import { a as ReferralRepository, c as Redemption, d as MemberRepository, f as UnitOfWork, i as TierDefinitionRepository, l as EarningRuleRepository, n as LoyaltyRepositories, o as Referral, s as RedemptionRepository, u as PointTransactionRepository } from "./index-BN96EDac.mjs";
|
|
7
|
+
import { DomainEvent, DomainEvent as DomainEvent$1, EventHandler, EventTransport, EventTransport as EventTransport$1 } from "@classytic/primitives/events";
|
|
8
|
+
import { OutboxStore } from "@classytic/primitives/outbox";
|
|
9
|
+
import { HttpError } from "@classytic/repo-core/errors";
|
|
8
10
|
|
|
11
|
+
//#region src/domain/errors/base.d.ts
|
|
12
|
+
/**
|
|
13
|
+
* Base for every loyalty throwable. Implements `HttpError` from
|
|
14
|
+
* `@classytic/repo-core/errors` (the org-canonical throwable contract) so arc
|
|
15
|
+
* catches and serializes them to `ErrorContract` via `toErrorContract()` with
|
|
16
|
+
* the right HTTP `status` and a stable, machine-readable `code`.
|
|
17
|
+
*
|
|
18
|
+
* Codes are hierarchical + lowercase per PACKAGE_RULES ("Errors and wire
|
|
19
|
+
* envelopes"): `loyalty.<entity>.<problem>`. The `status` field (not the old
|
|
20
|
+
* `httpStatus`) is what `HttpError` / arc read — a clean break, no alias.
|
|
21
|
+
*/
|
|
22
|
+
declare abstract class LoyaltyError extends Error implements HttpError {
|
|
23
|
+
abstract readonly code: string;
|
|
24
|
+
abstract readonly status: number;
|
|
25
|
+
constructor(message: string);
|
|
26
|
+
toJSON(): {
|
|
27
|
+
code: string;
|
|
28
|
+
message: string;
|
|
29
|
+
name: string;
|
|
30
|
+
status: number;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/domain/errors/domain-errors.d.ts
|
|
35
|
+
declare class InsufficientPointsError extends LoyaltyError {
|
|
36
|
+
readonly code = "loyalty.points.insufficient";
|
|
37
|
+
readonly status = 400;
|
|
38
|
+
constructor(available: number, requested: number);
|
|
39
|
+
}
|
|
40
|
+
declare class MemberNotFoundError extends LoyaltyError {
|
|
41
|
+
readonly code = "loyalty.member.not_found";
|
|
42
|
+
readonly status = 404;
|
|
43
|
+
constructor(id: string);
|
|
44
|
+
}
|
|
45
|
+
declare class MemberAlreadyEnrolledError extends LoyaltyError {
|
|
46
|
+
readonly code = "loyalty.member.already_enrolled";
|
|
47
|
+
readonly status = 409;
|
|
48
|
+
constructor(externalId: string);
|
|
49
|
+
}
|
|
50
|
+
declare class MemberInactiveError extends LoyaltyError {
|
|
51
|
+
readonly code = "loyalty.member.inactive";
|
|
52
|
+
readonly status = 422;
|
|
53
|
+
constructor(id: string);
|
|
54
|
+
}
|
|
55
|
+
declare class RuleNotFoundError extends LoyaltyError {
|
|
56
|
+
readonly code = "loyalty.earning_rule.not_found";
|
|
57
|
+
readonly status = 404;
|
|
58
|
+
constructor(id: string);
|
|
59
|
+
}
|
|
60
|
+
declare class TierNotFoundError extends LoyaltyError {
|
|
61
|
+
readonly code = "loyalty.tier.not_found";
|
|
62
|
+
readonly status = 404;
|
|
63
|
+
constructor(id: string);
|
|
64
|
+
}
|
|
65
|
+
declare class RedemptionNotFoundError extends LoyaltyError {
|
|
66
|
+
readonly code = "loyalty.redemption.not_found";
|
|
67
|
+
readonly status = 404;
|
|
68
|
+
constructor(id: string);
|
|
69
|
+
}
|
|
70
|
+
declare class RedemptionExpiredError extends LoyaltyError {
|
|
71
|
+
readonly code = "loyalty.redemption.expired";
|
|
72
|
+
readonly status = 410;
|
|
73
|
+
constructor(id: string);
|
|
74
|
+
}
|
|
75
|
+
declare class ReferralNotFoundError extends LoyaltyError {
|
|
76
|
+
readonly code = "loyalty.referral.not_found";
|
|
77
|
+
readonly status = 404;
|
|
78
|
+
constructor(id: string);
|
|
79
|
+
}
|
|
80
|
+
declare class DuplicateReferralError extends LoyaltyError {
|
|
81
|
+
readonly code = "loyalty.referral.duplicate";
|
|
82
|
+
readonly status = 409;
|
|
83
|
+
constructor(refereeId: string);
|
|
84
|
+
}
|
|
85
|
+
declare class ReferralLimitExceededError extends LoyaltyError {
|
|
86
|
+
readonly code = "loyalty.referral.limit_exceeded";
|
|
87
|
+
readonly status = 429;
|
|
88
|
+
constructor(limit: number, periodDays: number);
|
|
89
|
+
}
|
|
90
|
+
declare class SelfReferralError extends LoyaltyError {
|
|
91
|
+
readonly code = "loyalty.referral.self";
|
|
92
|
+
readonly status = 422;
|
|
93
|
+
constructor();
|
|
94
|
+
}
|
|
95
|
+
declare class CircularReferralError extends LoyaltyError {
|
|
96
|
+
readonly code = "loyalty.referral.circular";
|
|
97
|
+
readonly status = 422;
|
|
98
|
+
constructor();
|
|
99
|
+
}
|
|
100
|
+
declare class ValidationError extends LoyaltyError {
|
|
101
|
+
readonly code = "loyalty.validation.invalid";
|
|
102
|
+
readonly status = 400;
|
|
103
|
+
}
|
|
104
|
+
declare class TenantIsolationError extends LoyaltyError {
|
|
105
|
+
readonly code = "loyalty.tenant.isolation";
|
|
106
|
+
readonly status = 403;
|
|
107
|
+
constructor();
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* A host passed `ctx.session` (it owns the outer transaction) but did NOT
|
|
111
|
+
* attach a `[PENDING_EVENTS]` queue. Publishing to in-process subscribers
|
|
112
|
+
* BEFORE the host's commit would leak ghost events on rollback, and the
|
|
113
|
+
* outbox save would not be coordinated with the host's unit of work.
|
|
114
|
+
*
|
|
115
|
+
* Two safe patterns: (1) let loyalty own the transaction — call the verb
|
|
116
|
+
* WITHOUT `ctx.session`; or (2) attach a fresh `[PENDING_EVENTS]` array via
|
|
117
|
+
* `{ ...ctx, [PENDING_EVENTS]: queue }` and call `flushPendingLoyaltyEvents`
|
|
118
|
+
* after your transaction commits. Both are exported from the package root.
|
|
119
|
+
*/
|
|
120
|
+
declare class UnmanagedSessionError extends LoyaltyError {
|
|
121
|
+
readonly code = "loyalty.transaction.unmanaged_session";
|
|
122
|
+
readonly status = 400;
|
|
123
|
+
constructor();
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The backend doesn't declare the runtime capabilities loyalty's
|
|
127
|
+
* transactional verbs require (earn / adjust / reserve / confirm / release /
|
|
128
|
+
* reverse all run inside `withTransaction`). MongoDB must run as a replica set
|
|
129
|
+
* or sharded cluster. Thrown at engine creation — fail loud at boot, not with
|
|
130
|
+
* a cryptic driver error on the first transactional call.
|
|
131
|
+
*/
|
|
132
|
+
declare class LoyaltyCapabilityError extends LoyaltyError {
|
|
133
|
+
readonly code = "loyalty.engine.missing_capabilities";
|
|
134
|
+
readonly status = 500;
|
|
135
|
+
constructor(missing: readonly string[]);
|
|
136
|
+
}
|
|
137
|
+
//#endregion
|
|
138
|
+
//#region src/events/dispatch.d.ts
|
|
139
|
+
/**
|
|
140
|
+
* Internal key under which the post-commit event-publish queue is threaded
|
|
141
|
+
* through the context. When set, `dispatchLoyaltyEvent` APPENDS to this queue
|
|
142
|
+
* instead of publishing immediately — the verb that owns the transaction
|
|
143
|
+
* flushes it after commit. A `Symbol.for` so it never collides with
|
|
144
|
+
* host-supplied context fields and never appears in the public type.
|
|
145
|
+
*/
|
|
146
|
+
declare const PENDING_EVENTS: unique symbol;
|
|
147
|
+
/**
|
|
148
|
+
* Drains a queue of deferred events (the one attached under `[PENDING_EVENTS]`)
|
|
149
|
+
* through the transport, swallowing per-event errors. Called by transactional
|
|
150
|
+
* verbs AFTER `withTransaction` commits, and exported so hosts that own the
|
|
151
|
+
* outer transaction can flush after their own commit.
|
|
152
|
+
*/
|
|
153
|
+
declare function flushPendingLoyaltyEvents(events: EventTransport$1 | undefined, queue: DomainEvent$1[], logger?: {
|
|
154
|
+
error(message: string, ...args: unknown[]): void;
|
|
155
|
+
} | undefined): Promise<void>;
|
|
156
|
+
//#endregion
|
|
9
157
|
//#region src/index.d.ts
|
|
10
158
|
declare function resolveConfig(config: LoyaltyConfig): ResolvedConfig;
|
|
11
159
|
/**
|
|
@@ -34,4 +182,4 @@ interface LoyaltyEngine {
|
|
|
34
182
|
}
|
|
35
183
|
declare function createLoyaltyEngine(config: LoyaltyConfig): LoyaltyEngine;
|
|
36
184
|
//#endregion
|
|
37
|
-
export { type AdjustPointsInput, type BalanceSummary, type CreateEarningRuleInput, type CreateTierInput, type DomainEvent, type EarnPointsInput, type EarningResult, type EarningRule, EarningRuleRepository, type EnrollInput, type EventHandler, type EventTransport, InProcessLoyaltyBus, type InProcessLoyaltyBusOptions, type LoyaltyConfig, type LoyaltyContext, LoyaltyEngine, type LoyaltyEventDefinition, type LoyaltyEventMap, type LoyaltyEventName, type LoyaltyEventPayload, type LoyaltyEventPayloadOf, type LoyaltyEventSchema, LoyaltyEvents, type LoyaltyModelName, type LoyaltyModels, type LoyaltyRepositories, type Member, type MemberBalance, MemberDeactivated, MemberEnrolled, type MemberLifecyclePayload, MemberReactivated, MemberRepository, MemberSuspended, type NotificationAdapter, type PointTransaction, PointTransactionRepository, PointsAdjusted, type PointsAdjustedPayload, PointsEarned, type PointsEarnedPayload, PointsExpired, type PointsExpiredPayload, PointsExpiringSoon, PointsRedeemed, type Redemption, RedemptionConfirmed, type RedemptionPayload, RedemptionReleased, RedemptionRepository, RedemptionReserved, type RedemptionValidation, type Referral, ReferralApproved, type ReferralPayload, ReferralRecorded, ReferralRejected, ReferralRepository, ReferralRewarded, type ResolvedConfig, type ResolvedTenant, type TierChangedPayload, type TierDefinition, TierDefinitionRepository, TierDowngraded, type TierEvaluation, TierGraceStarted, TierUpgraded, type UnitOfWork, type UpdateEarningRuleInput, type UpdateTierInput, createEvent, createLoyaltyEngine, loyaltyEventDefinitions, resolveConfig };
|
|
185
|
+
export { type AdjustPointsInput, type BalanceSummary, CircularReferralError, type CreateEarningRuleInput, type CreateTierInput, type DomainEvent, DuplicateReferralError, type EarnPointsInput, type EarningResult, type EarningRule, EarningRuleRepository, type EnrollInput, type EventHandler, type EventTransport, InProcessLoyaltyBus, type InProcessLoyaltyBusOptions, InsufficientPointsError, LoyaltyCapabilityError, type LoyaltyConfig, type LoyaltyContext, LoyaltyEngine, type LoyaltyEventDefinition, type LoyaltyEventMap, type LoyaltyEventName, type LoyaltyEventPayload, type LoyaltyEventPayloadOf, type LoyaltyEventSchema, LoyaltyEvents, type LoyaltyModelName, type LoyaltyModels, type LoyaltyRepositories, type Member, MemberAlreadyEnrolledError, type MemberBalance, MemberDeactivated, MemberEnrolled, MemberInactiveError, type MemberLifecyclePayload, MemberNotFoundError, MemberReactivated, MemberRepository, MemberSuspended, type NotificationAdapter, PENDING_EVENTS, type PointTransaction, PointTransactionRepository, PointsAdjusted, type PointsAdjustedPayload, PointsEarned, type PointsEarnedPayload, PointsExpired, type PointsExpiredPayload, PointsExpiringSoon, PointsRedeemed, type Redemption, RedemptionConfirmed, RedemptionExpiredError, RedemptionNotFoundError, type RedemptionPayload, RedemptionReleased, RedemptionRepository, RedemptionReserved, type RedemptionValidation, type Referral, ReferralApproved, ReferralLimitExceededError, ReferralNotFoundError, type ReferralPayload, ReferralRecorded, ReferralRejected, ReferralRepository, ReferralRewarded, type ResolvedConfig, type ResolvedTenant, RuleNotFoundError, SelfReferralError, TenantIsolationError, type TierChangedPayload, type TierDefinition, TierDefinitionRepository, TierDowngraded, type TierEvaluation, TierGraceStarted, TierNotFoundError, TierUpgraded, type UnitOfWork, UnmanagedSessionError, type UpdateEarningRuleInput, type UpdateTierInput, ValidationError, createEvent, createLoyaltyEngine, flushPendingLoyaltyEvents, loyaltyEventDefinitions, resolveConfig };
|