@classytic/loyalty 0.2.0 → 0.2.2

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/README.md CHANGED
@@ -139,6 +139,13 @@ import type {
139
139
  } from '@classytic/loyalty';
140
140
  ```
141
141
 
142
+ ## Migration Notes
143
+
144
+ Earning rates now live in earning rules, not host membership config fields such
145
+ as `PlatformConfig.membership.pointsPerAmount` or `amountPerPoint`. See
146
+ [docs/config-engine-migration.md](docs/config-engine-migration.md) for the
147
+ `LoyaltyConfig` to engine boundary and migration checklist.
148
+
142
149
  ## Events
143
150
 
144
151
  ```typescript
@@ -1,8 +1,8 @@
1
- import { t as NotificationAdapter } from "./notification.port-9B5xlzxQ.mjs";
1
+ import { t as NotificationAdapter } from "./notification.port-CJ9nXj0k.mjs";
2
2
  import { EventTransport } from "@classytic/primitives/events";
3
3
  import { Connection } from "mongoose";
4
4
  import { OutboxStore } from "@classytic/primitives/outbox";
5
- import { TenantConfig, TenantFieldType } from "@classytic/primitives/tenant";
5
+ import { TenantConfig, TenantFieldType } from "@classytic/repo-core/tenant";
6
6
 
7
7
  //#region src/types/config.d.ts
8
8
  type LoyaltyModelName = 'Member' | 'PointTransaction' | 'EarningRule' | 'TierDefinition' | 'Redemption' | 'Referral';
@@ -1,2 +1,2 @@
1
- import { A as TierUpgraded, B as PointsEarnedPayload, C as ReferralRecorded, D as TierGraceStarted, E as TierDowngraded, F as LoyaltyEventPayload, G as TierChangedPayload, H as RedemptionPayload, I as LoyaltyEvents, J as InProcessLoyaltyBus, K as TierOverrideLifecyclePayload, L as MemberLifecyclePayload, M as CampaignAwardedPayload, N as LoyaltyEventMap, O as TierOverrideCleared, P as LoyaltyEventName, R as MultiTierReferralAwardedPayload, S as ReferralApproved, T as ReferralRewarded, U as RedemptionReversedPayload, V as PointsExpiredPayload, W as ReferralPayload, Y as InProcessLoyaltyBusOptions, _ as PointsRedeemed, a as LoyaltyEventDefinition, b as RedemptionReserved, c as MemberDeactivated, d as MemberSuspended, f as MultiTierReferralAwarded, g as PointsExpiringSoon, h as PointsExpired, i as CampaignAwarded, j as loyaltyEventDefinitions, k as TierOverrideExpired, l as MemberEnrolled, m as PointsEarned, n as EventHandler, o as LoyaltyEventPayloadOf, p as PointsAdjusted, q as createEvent, r as EventTransport, s as LoyaltyEventSchema, t as DomainEvent, u as MemberReactivated, v as RedemptionConfirmed, w as ReferralRejected, x as RedemptionReversed, y as RedemptionReleased, z as PointsAdjustedPayload } from "../index-BmqGuj38.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-xLLJ7jLy.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,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-BjzHSLBn.mjs";
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-BtZOdiPw.mjs";
2
2
  import { n as createEvent, t as LoyaltyEvents } from "../loyalty-events-Cyt9TwX6.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 };
@@ -34,39 +34,6 @@ var InProcessLoyaltyBus = class {
34
34
  };
35
35
  //#endregion
36
36
  //#region src/events/loyalty-event-catalog.ts
37
- /**
38
- * Loyalty event catalog — Zod-source-of-truth definitions for every
39
- * `loyalty.*` event.
40
- *
41
- * Each definition exposes:
42
- * - `.zodSchema` — source of truth, used by host code's `.safeParse()`
43
- * - `.schema` — JSON Schema derived via `z.toJSONSchema()`, consumed
44
- * by Arc's EventRegistry + OpenAPI plugin
45
- * - `.create(...)` — DomainEvent envelope builder, structurally compatible
46
- * with `@classytic/arc`'s `EventDefinitionOutput`
47
- *
48
- * Structurally compatible with Arc 2.10's `EventRegistry` — hosts register
49
- * `loyaltyEventDefinitions` directly, no adapter code. Loyalty does NOT
50
- * import from `@classytic/arc` (PACKAGE_RULES §11); compatibility is
51
- * purely structural.
52
- *
53
- * Payload schemas mirror the typed interfaces in
54
- * [loyalty-events.ts](./loyalty-events.ts). See PACKAGE_RULES §18.5 for
55
- * the pattern.
56
- *
57
- * @example Wiring into an Arc app
58
- * ```ts
59
- * import { createEventRegistry } from '@classytic/arc/events';
60
- * import { loyaltyEventDefinitions } from '@classytic/loyalty/events';
61
- *
62
- * const registry = createEventRegistry();
63
- * for (const def of loyaltyEventDefinitions) registry.register(def);
64
- *
65
- * const app = await createApp({
66
- * arcPlugins: { events: { registry, validateMode: 'reject' } },
67
- * });
68
- * ```
69
- */
70
37
  function defineLoyaltyEvent(input) {
71
38
  const { name, version = 1, description, zodSchema } = input;
72
39
  const def = {
@@ -245,6 +245,14 @@ interface CreateEarningRuleInput {
245
245
  reward: EarningReward;
246
246
  startsAt?: Date | string;
247
247
  endsAt?: Date | string;
248
+ /**
249
+ * Loyalty program this rule belongs to.
250
+ *
251
+ * Optional for repository-created rules: `EarningRuleRepository.create(...)`
252
+ * and `createMany(...)` fill this from `LoyaltyConfig.program.id`, matching
253
+ * the default used by `MemberRepository.enroll(...)`. Pass explicitly only
254
+ * when creating a rule for a different program.
255
+ */
248
256
  programId?: string;
249
257
  metadata?: Record<string, unknown>;
250
258
  }
@@ -1,7 +1,7 @@
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-2izQQext.mjs";
3
- import { t as NotificationAdapter } from "./notification.port-9B5xlzxQ.mjs";
4
- import { c as ResolvedTenant, o as RepositoryPlugins, s as ResolvedConfig } from "./config-JYTMP1F7.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";
3
+ import { t as NotificationAdapter } from "./notification.port-CJ9nXj0k.mjs";
4
+ import { c as ResolvedTenant, o as RepositoryPlugins, s as ResolvedConfig } from "./config-96Sl6oNJ.mjs";
5
5
  import { LoyaltyModels } from "./models/index.mjs";
6
6
  import { EventTransport } from "@classytic/primitives/events";
7
7
  import { ClientSession, Model } from "mongoose";
@@ -134,7 +134,12 @@ declare class EarningRuleRepository extends Repository<EarningRule> {
134
134
  txRepo: PointTransactionRepository;
135
135
  config: ResolvedConfig;
136
136
  constructor(model: Model<EarningRule>, plugins?: PluginType[]);
137
- /** Auto-inject tenant id from options into doc payload. See MemberRepository. */
137
+ private applyCreateDefaults;
138
+ /**
139
+ * Auto-injects `programId` from `LoyaltyConfig.program.id` and the tenant id
140
+ * from repo options when omitted. This keeps custom-program engines aligned
141
+ * with `MemberRepository.enroll(...)`, which uses the same config default.
142
+ */
138
143
  create(data: Parameters<Repository<EarningRule>['create']>[0], options?: Parameters<Repository<EarningRule>['create']>[1]): Promise<EarningRule>;
139
144
  createMany(docs: Parameters<Repository<EarningRule>['createMany']>[0], options?: Parameters<Repository<EarningRule>['createMany']>[1]): Promise<EarningRule[]>;
140
145
  evaluateOrder(input: {
@@ -1,7 +1,10 @@
1
- import { o as LoyaltyContext } from "./index-2izQQext.mjs";
1
+ import { o as LoyaltyContext } from "./index-Bo5YQ_fu.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
 
5
+ //#region src/events/helpers.d.ts
6
+ declare function createEvent$1<T>(type: string, payload: T, ctx?: LoyaltyContext, meta?: Partial<DomainEvent['meta']>): DomainEvent<T>;
7
+ //#endregion
5
8
  //#region src/events/in-process-bus.d.ts
6
9
  interface InProcessLoyaltyBusOptions {
7
10
  /** Logger for handler errors (default: console). */
@@ -19,169 +22,6 @@ declare class InProcessLoyaltyBus implements EventTransport {
19
22
  close(): Promise<void>;
20
23
  }
21
24
  //#endregion
22
- //#region src/events/helpers.d.ts
23
- declare function createEvent$1<T>(type: string, payload: T, ctx?: LoyaltyContext, meta?: Partial<DomainEvent['meta']>): DomainEvent<T>;
24
- //#endregion
25
- //#region src/events/loyalty-events.d.ts
26
- /**
27
- * Loyalty event name + payload map.
28
- *
29
- * Minimal surface. Loyalty emits events for its own lifecycle transitions
30
- * only — member, points ledger, tier, redemption, and referral.
31
- *
32
- * For registration with an arc-compatible event registry, see
33
- * `loyalty-event-catalog.ts` in this directory — it exposes each event as
34
- * a `LoyaltyEventDefinition` with a Zod payload schema + JSON Schema
35
- * derived via `z.toJSONSchema()` (PACKAGE_RULES §18.5).
36
- */
37
- declare const LoyaltyEvents: {
38
- readonly MEMBER_ENROLLED: "loyalty.member.enrolled";
39
- readonly MEMBER_DEACTIVATED: "loyalty.member.deactivated";
40
- readonly MEMBER_REACTIVATED: "loyalty.member.reactivated";
41
- readonly MEMBER_SUSPENDED: "loyalty.member.suspended";
42
- readonly POINTS_EARNED: "loyalty.points.earned";
43
- readonly POINTS_REDEEMED: "loyalty.points.redeemed";
44
- readonly POINTS_ADJUSTED: "loyalty.points.adjusted";
45
- readonly POINTS_EXPIRED: "loyalty.points.expired";
46
- readonly POINTS_EXPIRING_SOON: "loyalty.points.expiring_soon";
47
- readonly TIER_UPGRADED: "loyalty.tier.upgraded";
48
- readonly TIER_DOWNGRADED: "loyalty.tier.downgraded";
49
- readonly TIER_GRACE_STARTED: "loyalty.tier.grace_started";
50
- readonly TIER_OVERRIDE_CLEARED: "loyalty.tier.override_cleared";
51
- readonly TIER_OVERRIDE_EXPIRED: "loyalty.tier.override_expired";
52
- readonly REDEMPTION_RESERVED: "loyalty.redemption.reserved";
53
- readonly REDEMPTION_CONFIRMED: "loyalty.redemption.confirmed";
54
- readonly REDEMPTION_RELEASED: "loyalty.redemption.released";
55
- readonly REDEMPTION_REVERSED: "loyalty.redemption.reversed";
56
- readonly CAMPAIGN_AWARDED: "loyalty.campaign.awarded";
57
- readonly REFERRAL_RECORDED: "loyalty.referral.recorded";
58
- readonly REFERRAL_APPROVED: "loyalty.referral.approved";
59
- readonly REFERRAL_REJECTED: "loyalty.referral.rejected";
60
- readonly REFERRAL_REWARDED: "loyalty.referral.rewarded";
61
- readonly MULTI_TIER_REFERRAL_AWARDED: "loyalty.referral.multi_tier_awarded";
62
- };
63
- type LoyaltyEventName = (typeof LoyaltyEvents)[keyof typeof LoyaltyEvents];
64
- interface MemberLifecyclePayload {
65
- memberId: string;
66
- externalId: string;
67
- externalType: string;
68
- status: string;
69
- }
70
- interface PointsEarnedPayload {
71
- memberId: string;
72
- externalId: string;
73
- externalType: string;
74
- points: number;
75
- balanceAfter: number;
76
- transactionId: string;
77
- earningRuleName?: string;
78
- referenceType?: string;
79
- referenceId?: string;
80
- }
81
- interface PointsAdjustedPayload {
82
- memberId: string;
83
- externalId: string;
84
- points: number;
85
- balanceAfter: number;
86
- transactionId: string;
87
- description: string;
88
- }
89
- interface PointsExpiredPayload {
90
- membersAffected: number;
91
- pointsExpired: number;
92
- transactionCount: number;
93
- }
94
- interface TierChangedPayload {
95
- memberId: string;
96
- externalId: string;
97
- previousTier: string | null;
98
- newTier: string | null;
99
- lifetimePoints: number;
100
- }
101
- interface RedemptionPayload {
102
- memberId: string;
103
- externalId: string;
104
- redemptionId: string;
105
- points: number;
106
- discountAmount: number;
107
- ownerType: string;
108
- ownerId: string;
109
- }
110
- interface ReferralPayload {
111
- referralId: string;
112
- referrerId: string;
113
- refereeId: string;
114
- referralCode: string;
115
- status: string;
116
- referrerRewardPoints?: number;
117
- refereeRewardPoints?: number;
118
- }
119
- interface TierOverrideLifecyclePayload {
120
- memberId: string;
121
- externalId: string;
122
- tier: string;
123
- reason?: string;
124
- /** ISO string — Dates can't be JSON-schema'd. Present only on TTL-sweep path. */
125
- expiredAt?: string;
126
- }
127
- interface RedemptionReversedPayload {
128
- memberId: string;
129
- externalId: string;
130
- redemptionId: string;
131
- /** Points credited back to the member by the reversal. */
132
- pointsCredited: number;
133
- /** Original confirmed points on the redemption. */
134
- originalPoints: number;
135
- proportional: boolean;
136
- reason?: string;
137
- transactionId: string;
138
- }
139
- interface CampaignAwardedPayload {
140
- campaignId: string;
141
- memberCount: number;
142
- awarded: number;
143
- skipped: number;
144
- totalPoints: number;
145
- }
146
- interface MultiTierReferralAwardedPayload {
147
- referralId: string;
148
- refereeId: string;
149
- /** Awards keyed by ancestor depth (1 = direct referrer). */
150
- awards: Array<{
151
- memberId: string;
152
- depth: number;
153
- points: number;
154
- transactionId: string;
155
- }>;
156
- }
157
- interface LoyaltyEventMap {
158
- [LoyaltyEvents.MEMBER_ENROLLED]: MemberLifecyclePayload;
159
- [LoyaltyEvents.MEMBER_DEACTIVATED]: MemberLifecyclePayload;
160
- [LoyaltyEvents.MEMBER_REACTIVATED]: MemberLifecyclePayload;
161
- [LoyaltyEvents.MEMBER_SUSPENDED]: MemberLifecyclePayload;
162
- [LoyaltyEvents.POINTS_EARNED]: PointsEarnedPayload;
163
- [LoyaltyEvents.POINTS_REDEEMED]: PointsAdjustedPayload;
164
- [LoyaltyEvents.POINTS_ADJUSTED]: PointsAdjustedPayload;
165
- [LoyaltyEvents.POINTS_EXPIRED]: PointsExpiredPayload;
166
- [LoyaltyEvents.POINTS_EXPIRING_SOON]: PointsExpiredPayload;
167
- [LoyaltyEvents.TIER_UPGRADED]: TierChangedPayload;
168
- [LoyaltyEvents.TIER_DOWNGRADED]: TierChangedPayload;
169
- [LoyaltyEvents.TIER_GRACE_STARTED]: TierChangedPayload;
170
- [LoyaltyEvents.TIER_OVERRIDE_CLEARED]: TierOverrideLifecyclePayload;
171
- [LoyaltyEvents.TIER_OVERRIDE_EXPIRED]: TierOverrideLifecyclePayload;
172
- [LoyaltyEvents.REDEMPTION_RESERVED]: RedemptionPayload;
173
- [LoyaltyEvents.REDEMPTION_CONFIRMED]: RedemptionPayload;
174
- [LoyaltyEvents.REDEMPTION_RELEASED]: RedemptionPayload;
175
- [LoyaltyEvents.REDEMPTION_REVERSED]: RedemptionReversedPayload;
176
- [LoyaltyEvents.CAMPAIGN_AWARDED]: CampaignAwardedPayload;
177
- [LoyaltyEvents.REFERRAL_RECORDED]: ReferralPayload;
178
- [LoyaltyEvents.REFERRAL_APPROVED]: ReferralPayload;
179
- [LoyaltyEvents.REFERRAL_REJECTED]: ReferralPayload;
180
- [LoyaltyEvents.REFERRAL_REWARDED]: ReferralPayload;
181
- [LoyaltyEvents.MULTI_TIER_REFERRAL_AWARDED]: MultiTierReferralAwardedPayload;
182
- }
183
- type LoyaltyEventPayload<E extends LoyaltyEventName> = LoyaltyEventMap[E];
184
- //#endregion
185
25
  //#region src/events/loyalty-event-catalog.d.ts
186
26
  interface LoyaltyEventSchema {
187
27
  type: 'object';
@@ -397,4 +237,164 @@ declare const MultiTierReferralAwarded: LoyaltyEventDefinition<z.ZodObject<{
397
237
  */
398
238
  declare const loyaltyEventDefinitions: ReadonlyArray<LoyaltyEventDefinition>;
399
239
  //#endregion
400
- export { TierUpgraded as A, PointsEarnedPayload as B, ReferralRecorded as C, TierGraceStarted as D, TierDowngraded as E, LoyaltyEventPayload as F, TierChangedPayload as G, RedemptionPayload as H, LoyaltyEvents as I, InProcessLoyaltyBus as J, TierOverrideLifecyclePayload as K, MemberLifecyclePayload as L, CampaignAwardedPayload as M, LoyaltyEventMap as N, TierOverrideCleared as O, LoyaltyEventName as P, MultiTierReferralAwardedPayload as R, ReferralApproved as S, ReferralRewarded as T, RedemptionReversedPayload as U, PointsExpiredPayload as V, ReferralPayload as W, InProcessLoyaltyBusOptions as Y, PointsRedeemed as _, LoyaltyEventDefinition as a, RedemptionReserved as b, MemberDeactivated as c, MemberSuspended as d, MultiTierReferralAwarded as f, PointsExpiringSoon as g, PointsExpired as h, CampaignAwarded as i, loyaltyEventDefinitions as j, TierOverrideExpired as k, MemberEnrolled as l, PointsEarned as m, EventHandler$1 as n, LoyaltyEventPayloadOf as o, PointsAdjusted as p, createEvent$1 as q, EventTransport$1 as r, LoyaltyEventSchema as s, DomainEvent$1 as t, MemberReactivated as u, RedemptionConfirmed as v, ReferralRejected as w, RedemptionReversed as x, RedemptionReleased as y, PointsAdjustedPayload as z };
240
+ //#region src/events/loyalty-events.d.ts
241
+ /**
242
+ * Loyalty event name + payload map.
243
+ *
244
+ * Minimal surface. Loyalty emits events for its own lifecycle transitions
245
+ * only — member, points ledger, tier, redemption, and referral.
246
+ *
247
+ * For registration with an arc-compatible event registry, see
248
+ * `loyalty-event-catalog.ts` in this directory — it exposes each event as
249
+ * a `LoyaltyEventDefinition` with a Zod payload schema + JSON Schema
250
+ * derived via `z.toJSONSchema()` (PACKAGE_RULES §18.5).
251
+ */
252
+ declare const LoyaltyEvents: {
253
+ readonly MEMBER_ENROLLED: "loyalty.member.enrolled";
254
+ readonly MEMBER_DEACTIVATED: "loyalty.member.deactivated";
255
+ readonly MEMBER_REACTIVATED: "loyalty.member.reactivated";
256
+ readonly MEMBER_SUSPENDED: "loyalty.member.suspended";
257
+ readonly POINTS_EARNED: "loyalty.points.earned";
258
+ readonly POINTS_REDEEMED: "loyalty.points.redeemed";
259
+ readonly POINTS_ADJUSTED: "loyalty.points.adjusted";
260
+ readonly POINTS_EXPIRED: "loyalty.points.expired";
261
+ readonly POINTS_EXPIRING_SOON: "loyalty.points.expiring_soon";
262
+ readonly TIER_UPGRADED: "loyalty.tier.upgraded";
263
+ readonly TIER_DOWNGRADED: "loyalty.tier.downgraded";
264
+ readonly TIER_GRACE_STARTED: "loyalty.tier.grace_started";
265
+ readonly TIER_OVERRIDE_CLEARED: "loyalty.tier.override_cleared";
266
+ readonly TIER_OVERRIDE_EXPIRED: "loyalty.tier.override_expired";
267
+ readonly REDEMPTION_RESERVED: "loyalty.redemption.reserved";
268
+ readonly REDEMPTION_CONFIRMED: "loyalty.redemption.confirmed";
269
+ readonly REDEMPTION_RELEASED: "loyalty.redemption.released";
270
+ readonly REDEMPTION_REVERSED: "loyalty.redemption.reversed";
271
+ readonly CAMPAIGN_AWARDED: "loyalty.campaign.awarded";
272
+ readonly REFERRAL_RECORDED: "loyalty.referral.recorded";
273
+ readonly REFERRAL_APPROVED: "loyalty.referral.approved";
274
+ readonly REFERRAL_REJECTED: "loyalty.referral.rejected";
275
+ readonly REFERRAL_REWARDED: "loyalty.referral.rewarded";
276
+ readonly MULTI_TIER_REFERRAL_AWARDED: "loyalty.referral.multi_tier_awarded";
277
+ };
278
+ type LoyaltyEventName = (typeof LoyaltyEvents)[keyof typeof LoyaltyEvents];
279
+ interface MemberLifecyclePayload {
280
+ memberId: string;
281
+ externalId: string;
282
+ externalType: string;
283
+ status: string;
284
+ }
285
+ interface PointsEarnedPayload {
286
+ memberId: string;
287
+ externalId: string;
288
+ externalType: string;
289
+ points: number;
290
+ balanceAfter: number;
291
+ transactionId: string;
292
+ earningRuleName?: string;
293
+ referenceType?: string;
294
+ referenceId?: string;
295
+ }
296
+ interface PointsAdjustedPayload {
297
+ memberId: string;
298
+ externalId: string;
299
+ points: number;
300
+ balanceAfter: number;
301
+ transactionId: string;
302
+ description: string;
303
+ }
304
+ interface PointsExpiredPayload {
305
+ membersAffected: number;
306
+ pointsExpired: number;
307
+ transactionCount: number;
308
+ }
309
+ interface TierChangedPayload {
310
+ memberId: string;
311
+ externalId: string;
312
+ previousTier: string | null;
313
+ newTier: string | null;
314
+ lifetimePoints: number;
315
+ }
316
+ interface RedemptionPayload {
317
+ memberId: string;
318
+ externalId: string;
319
+ redemptionId: string;
320
+ points: number;
321
+ discountAmount: number;
322
+ ownerType: string;
323
+ ownerId: string;
324
+ }
325
+ interface ReferralPayload {
326
+ referralId: string;
327
+ referrerId: string;
328
+ refereeId: string;
329
+ referralCode: string;
330
+ status: string;
331
+ referrerRewardPoints?: number;
332
+ refereeRewardPoints?: number;
333
+ }
334
+ interface TierOverrideLifecyclePayload {
335
+ memberId: string;
336
+ externalId: string;
337
+ tier: string;
338
+ reason?: string;
339
+ /** ISO string — Dates can't be JSON-schema'd. Present only on TTL-sweep path. */
340
+ expiredAt?: string;
341
+ }
342
+ interface RedemptionReversedPayload {
343
+ memberId: string;
344
+ externalId: string;
345
+ redemptionId: string;
346
+ /** Points credited back to the member by the reversal. */
347
+ pointsCredited: number;
348
+ /** Original confirmed points on the redemption. */
349
+ originalPoints: number;
350
+ proportional: boolean;
351
+ reason?: string;
352
+ transactionId: string;
353
+ }
354
+ interface CampaignAwardedPayload {
355
+ campaignId: string;
356
+ memberCount: number;
357
+ awarded: number;
358
+ skipped: number;
359
+ totalPoints: number;
360
+ }
361
+ interface MultiTierReferralAwardedPayload {
362
+ referralId: string;
363
+ refereeId: string;
364
+ /** Awards keyed by ancestor depth (1 = direct referrer). */
365
+ awards: Array<{
366
+ memberId: string;
367
+ depth: number;
368
+ points: number;
369
+ transactionId: string;
370
+ }>;
371
+ }
372
+ interface LoyaltyEventMap {
373
+ [LoyaltyEvents.MEMBER_ENROLLED]: MemberLifecyclePayload;
374
+ [LoyaltyEvents.MEMBER_DEACTIVATED]: MemberLifecyclePayload;
375
+ [LoyaltyEvents.MEMBER_REACTIVATED]: MemberLifecyclePayload;
376
+ [LoyaltyEvents.MEMBER_SUSPENDED]: MemberLifecyclePayload;
377
+ [LoyaltyEvents.POINTS_EARNED]: PointsEarnedPayload;
378
+ [LoyaltyEvents.POINTS_REDEEMED]: PointsAdjustedPayload;
379
+ [LoyaltyEvents.POINTS_ADJUSTED]: PointsAdjustedPayload;
380
+ [LoyaltyEvents.POINTS_EXPIRED]: PointsExpiredPayload;
381
+ [LoyaltyEvents.POINTS_EXPIRING_SOON]: PointsExpiredPayload;
382
+ [LoyaltyEvents.TIER_UPGRADED]: TierChangedPayload;
383
+ [LoyaltyEvents.TIER_DOWNGRADED]: TierChangedPayload;
384
+ [LoyaltyEvents.TIER_GRACE_STARTED]: TierChangedPayload;
385
+ [LoyaltyEvents.TIER_OVERRIDE_CLEARED]: TierOverrideLifecyclePayload;
386
+ [LoyaltyEvents.TIER_OVERRIDE_EXPIRED]: TierOverrideLifecyclePayload;
387
+ [LoyaltyEvents.REDEMPTION_RESERVED]: RedemptionPayload;
388
+ [LoyaltyEvents.REDEMPTION_CONFIRMED]: RedemptionPayload;
389
+ [LoyaltyEvents.REDEMPTION_RELEASED]: RedemptionPayload;
390
+ [LoyaltyEvents.REDEMPTION_REVERSED]: RedemptionReversedPayload;
391
+ [LoyaltyEvents.CAMPAIGN_AWARDED]: CampaignAwardedPayload;
392
+ [LoyaltyEvents.REFERRAL_RECORDED]: ReferralPayload;
393
+ [LoyaltyEvents.REFERRAL_APPROVED]: ReferralPayload;
394
+ [LoyaltyEvents.REFERRAL_REJECTED]: ReferralPayload;
395
+ [LoyaltyEvents.REFERRAL_REWARDED]: ReferralPayload;
396
+ [LoyaltyEvents.MULTI_TIER_REFERRAL_AWARDED]: MultiTierReferralAwardedPayload;
397
+ }
398
+ type LoyaltyEventPayload<E extends LoyaltyEventName> = LoyaltyEventMap[E];
399
+ //#endregion
400
+ export { PointsExpired as A, ReferralRewarded as B, MemberDeactivated as C, MultiTierReferralAwarded as D, MemberSuspended as E, RedemptionReserved as F, TierUpgraded as G, TierGraceStarted as H, RedemptionReversed as I, InProcessLoyaltyBusOptions as J, loyaltyEventDefinitions as K, ReferralApproved as L, PointsRedeemed as M, RedemptionConfirmed as N, PointsAdjusted as O, RedemptionReleased as P, ReferralRecorded as R, LoyaltyEventSchema as S, MemberReactivated as T, TierOverrideCleared as U, TierDowngraded as V, TierOverrideExpired as W, createEvent$1 as Y, TierChangedPayload as _, LoyaltyEventMap as a, LoyaltyEventDefinition as b, LoyaltyEvents as c, PointsAdjustedPayload as d, PointsEarnedPayload as f, ReferralPayload as g, RedemptionReversedPayload as h, CampaignAwardedPayload as i, PointsExpiringSoon as j, PointsEarned as k, MemberLifecyclePayload as l, RedemptionPayload as m, EventHandler$1 as n, LoyaltyEventName as o, PointsExpiredPayload as p, InProcessLoyaltyBus as q, EventTransport$1 as r, LoyaltyEventPayload as s, DomainEvent$1 as t, MultiTierReferralAwardedPayload as u, TierOverrideLifecyclePayload as v, MemberEnrolled as w, LoyaltyEventPayloadOf as x, CampaignAwarded as y, ReferralRejected as z };
package/dist/index.d.mts CHANGED
@@ -1,9 +1,9 @@
1
- import { A as TierUpgraded, B as PointsEarnedPayload, C as ReferralRecorded, D as TierGraceStarted, E as TierDowngraded, F as LoyaltyEventPayload, G as TierChangedPayload, H as RedemptionPayload, I as LoyaltyEvents, J as InProcessLoyaltyBus, L as MemberLifecyclePayload, N as LoyaltyEventMap, P as LoyaltyEventName, S as ReferralApproved, T as ReferralRewarded, V as PointsExpiredPayload, W as ReferralPayload, Y as InProcessLoyaltyBusOptions, _ as PointsRedeemed, a as LoyaltyEventDefinition, b as RedemptionReserved, c as MemberDeactivated, d as MemberSuspended, g as PointsExpiringSoon, h as PointsExpired, j as loyaltyEventDefinitions, l as MemberEnrolled, m as PointsEarned, o as LoyaltyEventPayloadOf, p as PointsAdjusted, q as createEvent, s as LoyaltyEventSchema, u as MemberReactivated, v as RedemptionConfirmed, w as ReferralRejected, y as RedemptionReleased, z as PointsAdjustedPayload } from "./index-BmqGuj38.mjs";
2
- 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-2izQQext.mjs";
3
- import { t as NotificationAdapter } from "./notification.port-9B5xlzxQ.mjs";
4
- import { c as ResolvedTenant, n as LoyaltyConfig, r as LoyaltyModelName, s as ResolvedConfig } from "./config-JYTMP1F7.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-Bo5YQ_fu.mjs";
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-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";
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-qI1WvtNg.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";
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,6 +1,6 @@
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-ObGUgcSH.mjs";
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-BjzHSLBn.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";
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
4
  import { n as createModels } from "./models-uhh9c-7k.mjs";
5
5
  import { n as createEvent, t as LoyaltyEvents } from "./loyalty-events-Cyt9TwX6.mjs";
6
6
  //#region src/index.ts
@@ -1,4 +1,4 @@
1
- import { c as ResolvedTenant, i as ModelName, r as LoyaltyModelName, t as IndexDefinition } from "../config-JYTMP1F7.mjs";
1
+ import { c as ResolvedTenant, i as ModelName, r as LoyaltyModelName, t as IndexDefinition } from "../config-96Sl6oNJ.mjs";
2
2
  import * as _$mongoose from "mongoose";
3
3
  import { Connection, Model } from "mongoose";
4
4
 
@@ -1,4 +1,4 @@
1
- import { t as NotificationAdapter } from "../notification.port-9B5xlzxQ.mjs";
1
+ import { t as NotificationAdapter } from "../notification.port-CJ9nXj0k.mjs";
2
2
 
3
3
  //#region src/notifications/index.d.ts
4
4
  interface NotificationsAdapterConfig {
@@ -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-qI1WvtNg.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-CMSNfGos.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-ObGUgcSH.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-CZlhTYVp.mjs";
2
2
  export { EarningRuleRepository, MemberRepository, PointTransactionRepository, RedemptionRepository, ReferralRepository, TierDefinitionRepository, createRepositories };
@@ -153,16 +153,22 @@ function tenantCtx(tenant, tenantId) {
153
153
  * Copy the tenant id from the repo options into the write payload.
154
154
  *
155
155
  * Called from repository `create` / `createMany` overrides so the document
156
- * persists with the correct `organizationId` even when the host has opted
157
- * OUT of the auto-wired `multiTenantPlugin`. When the payload already has
158
- * the field (e.g. arc's `BaseController.create` pre-writes it), this is a
159
- * no-op.
156
+ * persists with the correct tenant id even when the host has opted OUT
157
+ * of the auto-wired `multiTenantPlugin`. The options object is populated
158
+ * by `tenantCtx()` which writes under `tenant.contextKey`, so the read
159
+ * here MUST use `contextKey` — not `tenantField`. They coincide in the
160
+ * default config (`organizationId === organizationId`) which masked the
161
+ * mismatch; with a divergent contextKey the copy silently never fired
162
+ * and writes landed without a tenant on the doc.
163
+ *
164
+ * When the payload already has the field (e.g. arc's `BaseController.create`
165
+ * pre-writes it under `tenantField`), this is a no-op.
160
166
  */
161
167
  function injectTenantIntoData(data, options, tenant) {
162
168
  const field = tenant.tenantField;
163
169
  if (!field) return;
164
170
  if (data[field] != null) return;
165
- const value = options?.[field];
171
+ const value = options?.[tenant.contextKey] ?? options?.[field];
166
172
  if (value != null) data[field] = value;
167
173
  }
168
174
  //#endregion
@@ -187,14 +193,22 @@ var EarningRuleRepository = class extends Repository {
187
193
  constructor(model, plugins = []) {
188
194
  super(model, plugins);
189
195
  }
190
- /** Auto-inject tenant id from options into doc payload. See MemberRepository. */
191
- async create(data, options) {
196
+ applyCreateDefaults(data, options) {
197
+ data.programId ??= this.config.programId;
192
198
  injectTenantIntoData(data, options, this.config.tenant);
199
+ }
200
+ /**
201
+ * Auto-injects `programId` from `LoyaltyConfig.program.id` and the tenant id
202
+ * from repo options when omitted. This keeps custom-program engines aligned
203
+ * with `MemberRepository.enroll(...)`, which uses the same config default.
204
+ */
205
+ async create(data, options) {
206
+ this.applyCreateDefaults(data, options);
193
207
  return super.create(data, options);
194
208
  }
195
209
  async createMany(docs, options) {
196
210
  const opts = options;
197
- for (const doc of docs) injectTenantIntoData(doc, opts, this.config.tenant);
211
+ for (const doc of docs) this.applyCreateDefaults(doc, opts);
198
212
  return super.createMany(docs, options);
199
213
  }
200
214
  async evaluateOrder(input, ctx) {
@@ -340,14 +354,10 @@ async function dispatchLoyaltyEvent(deps, event, ctx) {
340
354
  const saveOptions = {};
341
355
  if (ctx.session !== void 0) saveOptions.session = ctx.session;
342
356
  await deps.outbox.save(event, saveOptions);
343
- } catch (err) {
344
- console.error(`[loyalty] outbox.save failed for ${event.type}:`, err);
345
- }
357
+ } catch (_err) {}
346
358
  if (deps.events) try {
347
359
  await deps.events.publish(event);
348
- } catch (err) {
349
- console.error(`[loyalty] events.publish failed for ${event.type}:`, err);
350
- }
360
+ } catch (_err) {}
351
361
  }
352
362
  //#endregion
353
363
  //#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-2izQQext.mjs";
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-JYTMP1F7.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-Bo5YQ_fu.mjs";
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.0",
3
+ "version": "0.2.2",
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",
@@ -73,20 +73,23 @@
73
73
  "format": "biome format --write src/ tests/",
74
74
  "check": "biome check --write --unsafe src/ tests/",
75
75
  "prepublishOnly": "npm run lint && npm run typecheck && npm run test && npm run build",
76
- "release": "npm run build && npm run typecheck && npm publish --access public"
76
+ "push": "classytic-push",
77
+ "release:tag": "node -e \"require('child_process').execSync('npm run push -- v'+require('./package.json').version,{stdio:'inherit'})\"",
78
+ "release": "npm run push -- main && npm run release:tag && npm publish"
77
79
  },
78
80
  "peerDependencies": {
79
- "@classytic/mongokit": ">=3.11.0",
80
- "@classytic/primitives": ">=0.1.0",
81
- "@classytic/repo-core": ">=0.2.0",
81
+ "@classytic/mongokit": ">=3.13.0",
82
+ "@classytic/primitives": ">=0.5.0",
83
+ "@classytic/repo-core": ">=0.4.0",
82
84
  "mongoose": ">=9.4.1",
83
85
  "zod": ">=4.0.0"
84
86
  },
85
87
  "devDependencies": {
86
88
  "@biomejs/biome": "^2.4.9",
87
- "@classytic/mongokit": ">=3.11.0",
88
- "@classytic/primitives": ">=0.1.0",
89
- "@classytic/repo-core": ">=0.2.0",
89
+ "@classytic/dev-tools": "^0.2.0",
90
+ "@classytic/mongokit": ">=3.13.0",
91
+ "@classytic/primitives": ">=0.5.0",
92
+ "@classytic/repo-core": ">=0.4.0",
90
93
  "@types/node": "^25.5.0",
91
94
  "@vitest/coverage-v8": "^3.2.4",
92
95
  "knip": "^6.3.0",