@classytic/loyalty 0.2.2 → 0.2.3

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 CHANGED
@@ -3,6 +3,39 @@
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.2.3] — 2026-05-08
7
+
8
+ ### Fixed
9
+
10
+ - **§P8 dispatch — `outbox.save` failures now propagate.** [src/events/dispatch.ts](src/events/dispatch.ts)
11
+ previously caught `outbox.save` errors and swallowed them silently. Per
12
+ PACKAGE_RULES §P8 the outbox row is the durability anchor for the
13
+ transactional-outbox pattern: if the save fails, the caller's transaction
14
+ MUST roll back so the business doc and the event row land atomically.
15
+ Swallowing the failure meant a parent doc could commit while the event
16
+ vanished, with no relay row to recover from. Fix re-throws after logging.
17
+ `events.publish` failures continue to be swallowed (correctly, per §P8 —
18
+ the host's outbox relay re-publishes from the durable row), but now log
19
+ before swallowing so failures are observable.
20
+ - **`DispatchDeps.logger` field added.** Optional. Both arms of the
21
+ dispatch helper log through it when set, surfacing failures in the host's
22
+ observability stack without coupling the package to a specific logger.
23
+
24
+ ### Changed
25
+
26
+ - **Peer-dep ranges tightened** — `@classytic/mongokit` `>=3.13.3`
27
+ (was `>=3.13.0`) and `@classytic/repo-core` `>=0.4.2` (was `>=0.4.0`).
28
+ 3.13.3 preserves `errorLabels` on `MongoServerError` so concurrent
29
+ transactional writes (the `claim()`-based redemption + tier-eval paths)
30
+ retry correctly under contention; 0.4.2 adds the typed `FindAllOptions`
31
+ shape with `limit?` for callers that need a bounded non-paginated read.
32
+
33
+ ### Internal
34
+
35
+ - Replaced 4 `arr.find()!` non-null assertions in [src/index.ts](src/index.ts)
36
+ + scenario tests with explicit narrowing — TS now proves what the runtime
37
+ shape already guaranteed.
38
+
6
39
  ## [0.2.0]
7
40
 
8
41
  Structural rewrite on top of the 0.1.0 shape. The public API is now a
@@ -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-xLLJ7jLy.mjs";
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-DArT4zkB.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 };
@@ -1,5 +1,5 @@
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-Bo5YQ_fu.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-DWarXsmw.mjs";
3
3
  import { t as NotificationAdapter } from "./notification.port-CJ9nXj0k.mjs";
4
4
  import { c as ResolvedTenant, o as RepositoryPlugins, s as ResolvedConfig } from "./config-96Sl6oNJ.mjs";
5
5
  import { LoyaltyModels } from "./models/index.mjs";
@@ -7,6 +7,7 @@ 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 {
@@ -192,6 +193,18 @@ interface Redemption {
192
193
  */
193
194
  initialBalance?: number;
194
195
  remainingBalance?: number;
196
+ /**
197
+ * Optional multi-step approval chain (P7 canonical — same shape as
198
+ * `@classytic/purchase` / `@classytic/order`). High-value redemptions
199
+ * (gift cards above threshold, large discount vouchers) gate on
200
+ * `isApproved(approvals)` before `confirm()` flips the FSM. Single-tier
201
+ * flows skip the chain entirely.
202
+ */
203
+ approvals?: ApprovalChain | null;
204
+ /** Snapshot of the matrix policy id that produced this chain (host-managed). */
205
+ approvalPolicyId?: string | null;
206
+ /** Version of the matrix policy at submit time — frozen for audit. */
207
+ approvalPolicyVersion?: number | null;
195
208
  createdAt: Date;
196
209
  updatedAt: Date;
197
210
  }
@@ -1,4 +1,4 @@
1
- import { o as LoyaltyContext } from "./index-Bo5YQ_fu.mjs";
1
+ import { o as LoyaltyContext } from "./index-DWarXsmw.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
package/dist/index.d.mts CHANGED
@@ -1,9 +1,9 @@
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-Bo5YQ_fu.mjs";
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-DWarXsmw.mjs";
2
2
  import { t as NotificationAdapter } from "./notification.port-CJ9nXj0k.mjs";
3
3
  import { c as ResolvedTenant, n as LoyaltyConfig, r as LoyaltyModelName, s as ResolvedConfig } from "./config-96Sl6oNJ.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-xLLJ7jLy.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-DArT4zkB.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-CMSNfGos.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-Cl-WGBFJ.mjs";
7
7
  import { DomainEvent, EventHandler, EventTransport, EventTransport as EventTransport$1 } from "@classytic/primitives/events";
8
8
 
9
9
  //#region src/index.d.ts
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { n as DEFAULT_TENANT_FIELD, r as DEFAULT_TENANT_TYPE } from "./constants-DkQYkOK1.mjs";
2
- import { a as PointTransactionRepository, c as ValidationError, i as RedemptionRepository, n as TierDefinitionRepository, o as MemberRepository, r as ReferralRepository, s as EarningRuleRepository, t as createRepositories } from "./repositories-CZlhTYVp.mjs";
2
+ import { a as PointTransactionRepository, c as ValidationError, i as RedemptionRepository, n as TierDefinitionRepository, o as MemberRepository, r as ReferralRepository, s as EarningRuleRepository, t as createRepositories } from "./repositories-BMwidCp-.mjs";
3
3
  import { E as InProcessLoyaltyBus, T as loyaltyEventDefinitions, _ as ReferralRecorded, a as MemberSuspended, b as TierDowngraded, c as PointsEarned, d as PointsRedeemed, f as RedemptionConfirmed, g as ReferralApproved, i as MemberReactivated, l as PointsExpired, m as RedemptionReserved, n as MemberDeactivated, p as RedemptionReleased, r as MemberEnrolled, s as PointsAdjusted, u as PointsExpiringSoon, v as ReferralRejected, w as TierUpgraded, x as TierGraceStarted, y as ReferralRewarded } from "./events-BtZOdiPw.mjs";
4
- import { n as createModels } from "./models-uhh9c-7k.mjs";
4
+ import { n as createModels } from "./models-0_OQIqBy.mjs";
5
5
  import { n as createEvent, t as LoyaltyEvents } from "./loyalty-events-Cyt9TwX6.mjs";
6
6
  //#region src/index.ts
7
7
  function resolveConfig(config) {
@@ -62,11 +62,13 @@ var MongoUnitOfWork = class {
62
62
  async withTransaction(cb) {
63
63
  const session = await this.connection.startSession();
64
64
  try {
65
- let result;
65
+ const slot = { assigned: false };
66
66
  await session.withTransaction(async () => {
67
- result = await cb(session);
67
+ slot.value = await cb(session);
68
+ slot.assigned = true;
68
69
  });
69
- return result;
70
+ if (!slot.assigned) throw new Error("withTransaction: callback did not assign a result");
71
+ return slot.value;
70
72
  } finally {
71
73
  await session.endSession();
72
74
  }
@@ -243,6 +243,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
243
243
  earningRuleId?: _$mongoose.Types.ObjectId | null | undefined;
244
244
  expiresAt?: NativeDate | null | undefined;
245
245
  expiredAt?: NativeDate | null | undefined;
246
+ approvals?: any;
247
+ approvalPolicyId?: string | null | undefined;
248
+ approvalPolicyVersion?: number | null | undefined;
246
249
  createdAt: NativeDate;
247
250
  }, {}, {}, {
248
251
  id: string;
@@ -261,6 +264,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
261
264
  earningRuleId?: _$mongoose.Types.ObjectId | null | undefined;
262
265
  expiresAt?: NativeDate | null | undefined;
263
266
  expiredAt?: NativeDate | null | undefined;
267
+ approvals?: any;
268
+ approvalPolicyId?: string | null | undefined;
269
+ approvalPolicyVersion?: number | null | undefined;
264
270
  createdAt: NativeDate;
265
271
  }, {
266
272
  id: string;
@@ -284,6 +290,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
284
290
  earningRuleId?: _$mongoose.Types.ObjectId | null | undefined;
285
291
  expiresAt?: NativeDate | null | undefined;
286
292
  expiredAt?: NativeDate | null | undefined;
293
+ approvals?: any;
294
+ approvalPolicyId?: string | null | undefined;
295
+ approvalPolicyVersion?: number | null | undefined;
287
296
  createdAt: NativeDate;
288
297
  } & {
289
298
  _id: _$mongoose.Types.ObjectId;
@@ -311,6 +320,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
311
320
  earningRuleId?: _$mongoose.Types.ObjectId | null | undefined;
312
321
  expiresAt?: NativeDate | null | undefined;
313
322
  expiredAt?: NativeDate | null | undefined;
323
+ approvals?: any;
324
+ approvalPolicyId?: string | null | undefined;
325
+ approvalPolicyVersion?: number | null | undefined;
314
326
  createdAt: NativeDate;
315
327
  }, _$mongoose.Document<unknown, {}, {
316
328
  programId: string;
@@ -327,6 +339,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
327
339
  earningRuleId?: _$mongoose.Types.ObjectId | null | undefined;
328
340
  expiresAt?: NativeDate | null | undefined;
329
341
  expiredAt?: NativeDate | null | undefined;
342
+ approvals?: any;
343
+ approvalPolicyId?: string | null | undefined;
344
+ approvalPolicyVersion?: number | null | undefined;
330
345
  createdAt: NativeDate;
331
346
  }, {
332
347
  id: string;
@@ -350,6 +365,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
350
365
  earningRuleId?: _$mongoose.Types.ObjectId | null | undefined;
351
366
  expiresAt?: NativeDate | null | undefined;
352
367
  expiredAt?: NativeDate | null | undefined;
368
+ approvals?: any;
369
+ approvalPolicyId?: string | null | undefined;
370
+ approvalPolicyVersion?: number | null | undefined;
353
371
  createdAt: NativeDate;
354
372
  } & {
355
373
  _id: _$mongoose.Types.ObjectId;
@@ -372,6 +390,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
372
390
  earningRuleId?: _$mongoose.Types.ObjectId | null | undefined;
373
391
  expiresAt?: NativeDate | null | undefined;
374
392
  expiredAt?: NativeDate | null | undefined;
393
+ approvals?: any;
394
+ approvalPolicyId?: string | null | undefined;
395
+ approvalPolicyVersion?: number | null | undefined;
375
396
  createdAt: NativeDate;
376
397
  } & {
377
398
  _id: _$mongoose.Types.ObjectId;
@@ -392,6 +413,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
392
413
  earningRuleId?: _$mongoose.Types.ObjectId | null | undefined;
393
414
  expiresAt?: NativeDate | null | undefined;
394
415
  expiredAt?: NativeDate | null | undefined;
416
+ approvals?: any;
417
+ approvalPolicyId?: string | null | undefined;
418
+ approvalPolicyVersion?: number | null | undefined;
395
419
  createdAt: NativeDate;
396
420
  } & {
397
421
  _id: _$mongoose.Types.ObjectId;
@@ -883,6 +907,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
883
907
  ownerType: string;
884
908
  ownerId: string;
885
909
  expiresAt?: NativeDate | null | undefined;
910
+ approvals?: any;
911
+ approvalPolicyId?: string | null | undefined;
912
+ approvalPolicyVersion?: number | null | undefined;
886
913
  confirmedAt?: NativeDate | null | undefined;
887
914
  releasedAt?: NativeDate | null | undefined;
888
915
  reversedAt?: NativeDate | null | undefined;
@@ -904,6 +931,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
904
931
  ownerType: string;
905
932
  ownerId: string;
906
933
  expiresAt?: NativeDate | null | undefined;
934
+ approvals?: any;
935
+ approvalPolicyId?: string | null | undefined;
936
+ approvalPolicyVersion?: number | null | undefined;
907
937
  confirmedAt?: NativeDate | null | undefined;
908
938
  releasedAt?: NativeDate | null | undefined;
909
939
  reversedAt?: NativeDate | null | undefined;
@@ -927,6 +957,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
927
957
  ownerType: string;
928
958
  ownerId: string;
929
959
  expiresAt?: NativeDate | null | undefined;
960
+ approvals?: any;
961
+ approvalPolicyId?: string | null | undefined;
962
+ approvalPolicyVersion?: number | null | undefined;
930
963
  confirmedAt?: NativeDate | null | undefined;
931
964
  releasedAt?: NativeDate | null | undefined;
932
965
  reversedAt?: NativeDate | null | undefined;
@@ -954,6 +987,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
954
987
  ownerType: string;
955
988
  ownerId: string;
956
989
  expiresAt?: NativeDate | null | undefined;
990
+ approvals?: any;
991
+ approvalPolicyId?: string | null | undefined;
992
+ approvalPolicyVersion?: number | null | undefined;
957
993
  confirmedAt?: NativeDate | null | undefined;
958
994
  releasedAt?: NativeDate | null | undefined;
959
995
  reversedAt?: NativeDate | null | undefined;
@@ -973,6 +1009,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
973
1009
  ownerType: string;
974
1010
  ownerId: string;
975
1011
  expiresAt?: NativeDate | null | undefined;
1012
+ approvals?: any;
1013
+ approvalPolicyId?: string | null | undefined;
1014
+ approvalPolicyVersion?: number | null | undefined;
976
1015
  confirmedAt?: NativeDate | null | undefined;
977
1016
  releasedAt?: NativeDate | null | undefined;
978
1017
  reversedAt?: NativeDate | null | undefined;
@@ -996,6 +1035,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
996
1035
  ownerType: string;
997
1036
  ownerId: string;
998
1037
  expiresAt?: NativeDate | null | undefined;
1038
+ approvals?: any;
1039
+ approvalPolicyId?: string | null | undefined;
1040
+ approvalPolicyVersion?: number | null | undefined;
999
1041
  confirmedAt?: NativeDate | null | undefined;
1000
1042
  releasedAt?: NativeDate | null | undefined;
1001
1043
  reversedAt?: NativeDate | null | undefined;
@@ -1021,6 +1063,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
1021
1063
  ownerType: string;
1022
1064
  ownerId: string;
1023
1065
  expiresAt?: NativeDate | null | undefined;
1066
+ approvals?: any;
1067
+ approvalPolicyId?: string | null | undefined;
1068
+ approvalPolicyVersion?: number | null | undefined;
1024
1069
  confirmedAt?: NativeDate | null | undefined;
1025
1070
  releasedAt?: NativeDate | null | undefined;
1026
1071
  reversedAt?: NativeDate | null | undefined;
@@ -1046,6 +1091,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
1046
1091
  ownerType: string;
1047
1092
  ownerId: string;
1048
1093
  expiresAt?: NativeDate | null | undefined;
1094
+ approvals?: any;
1095
+ approvalPolicyId?: string | null | undefined;
1096
+ approvalPolicyVersion?: number | null | undefined;
1049
1097
  confirmedAt?: NativeDate | null | undefined;
1050
1098
  releasedAt?: NativeDate | null | undefined;
1051
1099
  reversedAt?: NativeDate | null | undefined;
@@ -1,2 +1,2 @@
1
- import { n as createModels, t as LoyaltyModelCollisionError } from "../models-uhh9c-7k.mjs";
1
+ import { n as createModels, t as LoyaltyModelCollisionError } from "../models-0_OQIqBy.mjs";
2
2
  export { LoyaltyModelCollisionError, createModels };
@@ -232,6 +232,18 @@ function createPointTransactionSchema() {
232
232
  actorId: {
233
233
  type: String,
234
234
  required: true
235
+ },
236
+ approvals: {
237
+ type: mongoose.Schema.Types.Mixed,
238
+ default: null
239
+ },
240
+ approvalPolicyId: {
241
+ type: String,
242
+ default: null
243
+ },
244
+ approvalPolicyVersion: {
245
+ type: Number,
246
+ default: null
235
247
  }
236
248
  }, { timestamps: {
237
249
  createdAt: true,
@@ -297,7 +309,19 @@ function createRedemptionSchema() {
297
309
  reversalTransactionId: { type: mongoose.Schema.Types.ObjectId },
298
310
  transactionId: { type: mongoose.Schema.Types.ObjectId },
299
311
  initialBalance: { type: Number },
300
- remainingBalance: { type: Number }
312
+ remainingBalance: { type: Number },
313
+ approvals: {
314
+ type: mongoose.Schema.Types.Mixed,
315
+ default: null
316
+ },
317
+ approvalPolicyId: {
318
+ type: String,
319
+ default: null
320
+ },
321
+ approvalPolicyVersion: {
322
+ type: Number,
323
+ default: null
324
+ }
301
325
  }, { timestamps: true });
302
326
  schema.index({
303
327
  ownerType: 1,
@@ -1,2 +1,2 @@
1
- import { a as ReferralRepository, d as MemberRepository, i as TierDefinitionRepository, l as EarningRuleRepository, n as LoyaltyRepositories, r as createRepositories, s as RedemptionRepository, t as CreateRepositoriesInput, u as PointTransactionRepository } from "../index-CMSNfGos.mjs";
1
+ import { a as ReferralRepository, d as MemberRepository, i as TierDefinitionRepository, l as EarningRuleRepository, n as LoyaltyRepositories, r as createRepositories, s as RedemptionRepository, t as CreateRepositoriesInput, u as PointTransactionRepository } from "../index-Cl-WGBFJ.mjs";
2
2
  export { CreateRepositoriesInput, EarningRuleRepository, LoyaltyRepositories, MemberRepository, PointTransactionRepository, RedemptionRepository, ReferralRepository, TierDefinitionRepository, createRepositories };
@@ -1,2 +1,2 @@
1
- import { a as PointTransactionRepository, i as RedemptionRepository, n as TierDefinitionRepository, o as MemberRepository, r as ReferralRepository, s as EarningRuleRepository, t as createRepositories } from "../repositories-CZlhTYVp.mjs";
1
+ import { a as PointTransactionRepository, i as RedemptionRepository, n as TierDefinitionRepository, o as MemberRepository, r as ReferralRepository, s as EarningRuleRepository, t as createRepositories } from "../repositories-BMwidCp-.mjs";
2
2
  export { EarningRuleRepository, MemberRepository, PointTransactionRepository, RedemptionRepository, ReferralRepository, TierDefinitionRepository, createRepositories };
@@ -354,10 +354,15 @@ async function dispatchLoyaltyEvent(deps, event, ctx) {
354
354
  const saveOptions = {};
355
355
  if (ctx.session !== void 0) saveOptions.session = ctx.session;
356
356
  await deps.outbox.save(event, saveOptions);
357
- } catch (_err) {}
357
+ } catch (err) {
358
+ deps.logger?.error(`[loyalty] outbox.save failed for ${event.type}:`, err);
359
+ throw err;
360
+ }
358
361
  if (deps.events) try {
359
362
  await deps.events.publish(event);
360
- } catch (_err) {}
363
+ } catch (err) {
364
+ deps.logger?.error(`[loyalty] events.publish failed for ${event.type}:`, err);
365
+ }
361
366
  }
362
367
  //#endregion
363
368
  //#region src/utils/code-generator.ts
@@ -1,3 +1,3 @@
1
- import { _ as TierEvaluationBatch, a as EnrollInput, c as UpdateTierInput, d as EarningResult, f as ExpirationResult, g as TierEvaluation, h as RedemptionValidation, i as EarnPointsInput, l as BalanceSummary, m as PaginatedResult, n as CreateEarningRuleInput, o as LoyaltyContext, p as ListQuery, r as CreateTierInput, s as UpdateEarningRuleInput, t as AdjustPointsInput, u as EarningBreakdown } from "../index-Bo5YQ_fu.mjs";
1
+ import { _ as TierEvaluationBatch, a as EnrollInput, c as UpdateTierInput, d as EarningResult, f as ExpirationResult, g as TierEvaluation, h as RedemptionValidation, i as EarnPointsInput, l as BalanceSummary, m as PaginatedResult, n as CreateEarningRuleInput, o as LoyaltyContext, p as ListQuery, r as CreateTierInput, s as UpdateEarningRuleInput, t as AdjustPointsInput, u as EarningBreakdown } from "../index-DWarXsmw.mjs";
2
2
  import { a as PluginType, c as ResolvedTenant, i as ModelName, l as TenantConfig, n as LoyaltyConfig, o as RepositoryPlugins, s as ResolvedConfig, t as IndexDefinition } from "../config-96Sl6oNJ.mjs";
3
3
  export { AdjustPointsInput, BalanceSummary, CreateEarningRuleInput, CreateTierInput, EarnPointsInput, EarningBreakdown, EarningResult, EnrollInput, ExpirationResult, IndexDefinition, ListQuery, LoyaltyConfig, LoyaltyContext, ModelName, PaginatedResult, PluginType, RedemptionValidation, RepositoryPlugins, ResolvedConfig, ResolvedTenant, TenantConfig, TierEvaluation, TierEvaluationBatch, UpdateEarningRuleInput, UpdateTierInput };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classytic/loyalty",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Loyalty points, tiers, earning rules, referrals, and redemption engine for MongoDB",
5
5
  "author": "Classytic",
6
6
  "homepage": "https://www.npmjs.com/package/@classytic/loyalty",
@@ -78,18 +78,18 @@
78
78
  "release": "npm run push -- main && npm run release:tag && npm publish"
79
79
  },
80
80
  "peerDependencies": {
81
- "@classytic/mongokit": ">=3.13.0",
81
+ "@classytic/mongokit": ">=3.13.3",
82
82
  "@classytic/primitives": ">=0.5.0",
83
- "@classytic/repo-core": ">=0.4.0",
83
+ "@classytic/repo-core": ">=0.4.2",
84
84
  "mongoose": ">=9.4.1",
85
85
  "zod": ">=4.0.0"
86
86
  },
87
87
  "devDependencies": {
88
88
  "@biomejs/biome": "^2.4.9",
89
89
  "@classytic/dev-tools": "^0.2.0",
90
- "@classytic/mongokit": ">=3.13.0",
90
+ "@classytic/mongokit": "^3.13.3",
91
91
  "@classytic/primitives": ">=0.5.0",
92
- "@classytic/repo-core": ">=0.4.0",
92
+ "@classytic/repo-core": "^0.4.2",
93
93
  "@types/node": "^25.5.0",
94
94
  "@vitest/coverage-v8": "^3.2.4",
95
95
  "knip": "^6.3.0",