@classytic/loyalty 0.4.0 → 0.5.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 +231 -215
- package/dist/index.mjs +1 -1
- package/dist/models/index.d.mts +24 -24
- package/dist/repositories/index.mjs +1 -1
- package/dist/{repositories-DEwCbLCh.mjs → repositories-CjWfUlrA.mjs} +4 -3
- package/package.json +116 -116
package/CHANGELOG.md
CHANGED
|
@@ -1,215 +1,231 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
4
|
-
adhering to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
|
-
|
|
6
|
-
## [0.
|
|
7
|
-
|
|
8
|
-
### Changed (
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
## [0.4.0] — 2026-07-
|
|
23
|
-
|
|
24
|
-
###
|
|
25
|
-
|
|
26
|
-
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
- **
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
- **
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
- **
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
###
|
|
139
|
-
|
|
140
|
-
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
transactional
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
-
|
|
198
|
-
|
|
199
|
-
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
###
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
4
|
+
adhering to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
|
+
|
|
6
|
+
## [0.5.0] — unpublished (2026-07-15)
|
|
7
|
+
|
|
8
|
+
### Changed — mongokit 3.22 adopted (peer floors: mongokit >=3.22.0, repo-core >=0.13.0, primitives >=0.11.0)
|
|
9
|
+
|
|
10
|
+
- **Points ledger fence enforced** — `PointTransaction` rows are the
|
|
11
|
+
balance audit trail; `appendOnlyPlugin` now refuses every mutation
|
|
12
|
+
path (405 `APPEND_ONLY_VIOLATION`). Previously nothing enforced it —
|
|
13
|
+
a stray `update()` could rewrite an earn/burn row and silently
|
|
14
|
+
desync the audit trail from member balances. The expiry sweeper's
|
|
15
|
+
`expiredAt` stamp is the one engine-owned lifecycle write and passes
|
|
16
|
+
the audited `bypassAppendOnly` escape.
|
|
17
|
+
- No FSM boilerplate existed (no assert+claim sites, no
|
|
18
|
+
read-modify-write history) — loyalty was already clean there; this
|
|
19
|
+
release is the fence + kit refresh (clean registry install, no
|
|
20
|
+
legacy flags).
|
|
21
|
+
|
|
22
|
+
## [0.4.0] — addendum (2026-07-05)
|
|
23
|
+
|
|
24
|
+
### Changed (behavioral)
|
|
25
|
+
|
|
26
|
+
- **§P8.1 relay-only session guard** — `UnmanagedSessionError` now throws
|
|
27
|
+
only when a host session arrives with NEITHER a `[PENDING_EVENTS]`
|
|
28
|
+
queue NOR an outbox. Session + outbox + no queue is legal
|
|
29
|
+
durable-relay-only mode: the row commits with the host transaction, the
|
|
30
|
+
relay delivers post-commit, the immediate transport publish is skipped.
|
|
31
|
+
(Previously the guard was stricter than spec and threw even with an
|
|
32
|
+
outbox wired.) Aligns loyalty with invoice / catalog / order /
|
|
33
|
+
purchase / transfer / promo / payrun.
|
|
34
|
+
- **Stack floors raised** — peers `@classytic/mongokit >=3.18.0`,
|
|
35
|
+
`@classytic/repo-core >=0.7.0`; full suite verified against those
|
|
36
|
+
versions.
|
|
37
|
+
|
|
38
|
+
## [0.4.0] — 2026-07-03
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- **`evaluateAction` is now idempotent per recurrence key.** The order path
|
|
43
|
+
dedupes with `${orderId}:${ruleId}`, but the action path (signup /
|
|
44
|
+
referral / birthday / review) never set an `idempotencyKey` — a retried
|
|
45
|
+
sweep or double-fired event double-awarded points. When `referenceId` is
|
|
46
|
+
supplied, each rule award now carries `${referenceId}:${ruleId}`; callers
|
|
47
|
+
encode the recurrence period in `referenceId` (e.g.
|
|
48
|
+
`birthday:2026:<memberId>`) and the unique partial index on
|
|
49
|
+
`idempotencyKey` becomes the authoritative once-only guard.
|
|
50
|
+
- **Action rules honor `conditions` now.** `evaluateAction` skipped
|
|
51
|
+
`matchesConditions` entirely, silently ignoring admin-authored
|
|
52
|
+
`tiers` / `dateRange` / `dayOfWeek` on action-type rules ("only gold
|
|
53
|
+
members get birthday points" did nothing). Conditions now apply;
|
|
54
|
+
`minOrderAmount` is skipped for actions (no order to measure).
|
|
55
|
+
- **`allowNonTransactional` now actually works at runtime.** The flag
|
|
56
|
+
silenced the boot capability gate but `MongoUnitOfWork.withTransaction`
|
|
57
|
+
still opened a real transaction unconditionally — on standalone Mongo
|
|
58
|
+
every `earnPoints`/`redeem` unit failed with "does not support retryable
|
|
59
|
+
writes" despite the documented escape hatch. The unit of work now
|
|
60
|
+
delegates to mongokit's `withTransaction` with `allowFallback` so the
|
|
61
|
+
transactions-unsupported error re-runs the unit on a plain session
|
|
62
|
+
(partial-write risk accepted, exactly what the flag opts into).
|
|
63
|
+
- Day arithmetic (tier-downgrade grace expiry, referral rate-limit window)
|
|
64
|
+
now uses `addDays` from `@classytic/primitives/calendar` — exact 24h × n
|
|
65
|
+
durations instead of local `setDate`, which shifted with the deploy
|
|
66
|
+
machine's `TZ` env. Primitives peer bumped to `>=0.9.0`.
|
|
67
|
+
|
|
68
|
+
## [0.3.0] — 2026-06-13
|
|
69
|
+
|
|
70
|
+
Stack upgrade to mongokit 3.16 / repo-core 0.6 / primitives 0.7.2 plus
|
|
71
|
+
verified correctness + PACKAGE_RULES compliance fixes. No new features.
|
|
72
|
+
|
|
73
|
+
### Fixed
|
|
74
|
+
|
|
75
|
+
- **Stack actually resolves the new floor now (CRITICAL).** The committed
|
|
76
|
+
lockfile silently served `@classytic/mongokit@3.13.0` +
|
|
77
|
+
`@classytic/repo-core@0.4.0` despite the `^3.13.3` / `^0.4.2` pins, so the
|
|
78
|
+
whole test suite ran against the OLD stack. Clean reinstall + peer/dev bumps
|
|
79
|
+
pin the resolved tree to mongokit **3.16.0**, repo-core **0.6.0**, primitives
|
|
80
|
+
**0.7.2** (verified with `npm ls`).
|
|
81
|
+
- **§P8 outbox is now session-bound on the transactional verbs.** `earnPoints`,
|
|
82
|
+
`adjustPoints`, `reserve`, `confirm`, `release`, and `reverseConfirmed`
|
|
83
|
+
previously dispatched the event AFTER `withTransaction` committed, so
|
|
84
|
+
`outbox.save` ran OUTSIDE the unit of work — the business doc could commit
|
|
85
|
+
while the durable event row was lost (or vice-versa). Dispatch now runs
|
|
86
|
+
INSIDE the transaction (outbox row commits atomically with the write) and the
|
|
87
|
+
transport publish is deferred to a post-commit flush (`runLoyaltyUnit` +
|
|
88
|
+
`[PENDING_EVENTS]`), so in-process subscribers never see a state that may roll
|
|
89
|
+
back. A new integration test asserts the save is session-bound and that a
|
|
90
|
+
failing `outbox.save` rolls the `$inc` balance write back.
|
|
91
|
+
- **Redemption slot no longer permanently blocked after release.** The
|
|
92
|
+
`{ ownerType, ownerId }` unique index spanned ALL statuses, so a released /
|
|
93
|
+
reversed reservation E11000-blocked any retry on the same order (payment
|
|
94
|
+
retry, cart re-attempt) forever. It is now a **partial** unique index scoped
|
|
95
|
+
to active statuses (`reserved`, `confirmed`) — terminal states free the slot
|
|
96
|
+
(PACKAGE_RULES rule 32/38/39). Re-reserve-after-release and second-active-
|
|
97
|
+
reserve-blocked tests added.
|
|
98
|
+
|
|
99
|
+
### Added
|
|
100
|
+
|
|
101
|
+
- **`UnmanagedSessionError` guard (§P8).** When a host passes `ctx.session`
|
|
102
|
+
(it owns the outer transaction) without attaching a `[PENDING_EVENTS]` queue,
|
|
103
|
+
dispatch throws BEFORE any side effect instead of publishing / saving outside
|
|
104
|
+
the host's unit of work (which would leak ghost events on rollback).
|
|
105
|
+
`PENDING_EVENTS` and `flushPendingLoyaltyEvents` are exported for hosts that
|
|
106
|
+
manage their own transaction.
|
|
107
|
+
- **Capability boot gate.** `createLoyaltyEngine()` asserts the backend declares
|
|
108
|
+
the `transactions` capability (repo-core 0.6 / mongokit 3.16+) at engine
|
|
109
|
+
creation — fail loud at boot rather than with a cryptic driver error on the
|
|
110
|
+
first transactional call. `allowNonTransactional: true` opts out for local-dev
|
|
111
|
+
on standalone Mongo. New `LoyaltyCapabilityError`.
|
|
112
|
+
- **Cooperative cancellation in cron loops.** `processExpirations`, `awardBatch`,
|
|
113
|
+
`cleanupExpired` (redemption + referral), `evaluateAll`, and
|
|
114
|
+
`sweepExpiredOverrides` check `ctx.signal` via `throwIfAborted` between items —
|
|
115
|
+
abortable batches; each per-item write is its own committed unit of work, so
|
|
116
|
+
aborting never rolls back already-processed rows. `LoyaltyContext` gained
|
|
117
|
+
`signal?: AbortSignal`.
|
|
118
|
+
|
|
119
|
+
### Changed
|
|
120
|
+
|
|
121
|
+
- **Domain errors implement `HttpError`** from `@classytic/repo-core/errors`
|
|
122
|
+
(was a bespoke `httpStatus` shape). Clean break: `httpStatus` → `status`,
|
|
123
|
+
and codes are now hierarchical + lowercase (`loyalty.<entity>.<problem>`,
|
|
124
|
+
e.g. `loyalty.member.not_found`, `loyalty.points.insufficient`) so arc
|
|
125
|
+
serializes them to `ErrorContract` correctly. No deprecation alias.
|
|
126
|
+
- **Peer-dep ranges raised** — `@classytic/mongokit >=3.16.0`,
|
|
127
|
+
`@classytic/repo-core >=0.6.0` (`>=` style). devDeps bumped to match and
|
|
128
|
+
primitives devDep pinned to `^0.7.2`.
|
|
129
|
+
- **`exactOptionalPropertyTypes: true`** enabled in tsconfig (mandatory per
|
|
130
|
+
PACKAGE_RULES Build Hygiene / P10); fixed the three pre-existing optional-vs-
|
|
131
|
+
undefined violations it surfaced.
|
|
132
|
+
- **tsconfig no longer emits sourcemaps or declaration maps**
|
|
133
|
+
(`sourceMap: false`, `declarationMap: false`) — both leaked source. tsdown
|
|
134
|
+
was already correct; tsconfig now matches.
|
|
135
|
+
|
|
136
|
+
## [0.2.3] — 2026-05-08
|
|
137
|
+
|
|
138
|
+
### Fixed
|
|
139
|
+
|
|
140
|
+
- **§P8 dispatch — `outbox.save` failures now propagate.** [src/events/dispatch.ts](src/events/dispatch.ts)
|
|
141
|
+
previously caught `outbox.save` errors and swallowed them silently. Per
|
|
142
|
+
PACKAGE_RULES §P8 the outbox row is the durability anchor for the
|
|
143
|
+
transactional-outbox pattern: if the save fails, the caller's transaction
|
|
144
|
+
MUST roll back so the business doc and the event row land atomically.
|
|
145
|
+
Swallowing the failure meant a parent doc could commit while the event
|
|
146
|
+
vanished, with no relay row to recover from. Fix re-throws after logging.
|
|
147
|
+
`events.publish` failures continue to be swallowed (correctly, per §P8 —
|
|
148
|
+
the host's outbox relay re-publishes from the durable row), but now log
|
|
149
|
+
before swallowing so failures are observable.
|
|
150
|
+
- **`DispatchDeps.logger` field added.** Optional. Both arms of the
|
|
151
|
+
dispatch helper log through it when set, surfacing failures in the host's
|
|
152
|
+
observability stack without coupling the package to a specific logger.
|
|
153
|
+
|
|
154
|
+
### Changed
|
|
155
|
+
|
|
156
|
+
- **Peer-dep ranges tightened** — `@classytic/mongokit` `>=3.13.3`
|
|
157
|
+
(was `>=3.13.0`) and `@classytic/repo-core` `>=0.4.2` (was `>=0.4.0`).
|
|
158
|
+
3.13.3 preserves `errorLabels` on `MongoServerError` so concurrent
|
|
159
|
+
transactional writes (the `claim()`-based redemption + tier-eval paths)
|
|
160
|
+
retry correctly under contention; 0.4.2 adds the typed `FindAllOptions`
|
|
161
|
+
shape with `limit?` for callers that need a bounded non-paginated read.
|
|
162
|
+
|
|
163
|
+
### Internal
|
|
164
|
+
|
|
165
|
+
- Replaced 4 `arr.find()!` non-null assertions in [src/index.ts](src/index.ts)
|
|
166
|
+
+ scenario tests with explicit narrowing — TS now proves what the runtime
|
|
167
|
+
shape already guaranteed.
|
|
168
|
+
|
|
169
|
+
## [0.2.0]
|
|
170
|
+
|
|
171
|
+
Structural rewrite on top of the 0.1.0 shape. The public API is now a
|
|
172
|
+
smaller, sharper surface organized around seven subpath exports
|
|
173
|
+
(`/domain/enums`, `/models`, `/repositories`, `/events`, `/types`,
|
|
174
|
+
`/notifications`, plus the root engine factory). Legacy barrel + service
|
|
175
|
+
layers were removed; hosts wire business flows directly against the
|
|
176
|
+
repositories and the engine factory.
|
|
177
|
+
|
|
178
|
+
### Changed
|
|
179
|
+
|
|
180
|
+
- **Domain restructure.** Dropped the `src/domain/ports/` + `src/services/`
|
|
181
|
+
layer in favor of thin repositories that expose exactly the operations
|
|
182
|
+
hosts need — `earn`, `redeem`, `adjust`, `expire`, referral credit,
|
|
183
|
+
tier recomputation. No more `earning.service` / `redemption.service` /
|
|
184
|
+
`tier.service` / `referral.service` / `ledger.service` / `member.service`
|
|
185
|
+
/ `member-guard` indirections; the equivalent logic lives on the
|
|
186
|
+
repositories where it's naturally atomic.
|
|
187
|
+
- **Events subsystem simplified.** Removed `events/event-bus.ts` +
|
|
188
|
+
`events/event-types.ts` (both are internal indirection that the engine
|
|
189
|
+
didn't need). Event payloads are now colocated with the repository
|
|
190
|
+
that emits them.
|
|
191
|
+
- **Repositories updated to mongokit 3.11 + repo-core 0.2 conventions.**
|
|
192
|
+
Every repo extends `Repository<T>` directly, uses the tenant plugin via
|
|
193
|
+
`injectTenant`, and atomic state changes go through CAS guards.
|
|
194
|
+
- **Peer deps** unchanged in spirit, tightened in range:
|
|
195
|
+
- `@classytic/mongokit >= 3.11`
|
|
196
|
+
- `@classytic/primitives >= 0.1`
|
|
197
|
+
- `@classytic/repo-core >= 0.2`
|
|
198
|
+
- `mongoose >= 9.4`, `zod >= 4`
|
|
199
|
+
- **DevDeps:** `@classytic/primitives` devDep moved from `file:` to `>=0.1.0`
|
|
200
|
+
(now that primitives ships on npm); all `@classytic/*` dev ranges
|
|
201
|
+
standardized on `>=`.
|
|
202
|
+
|
|
203
|
+
### Removed
|
|
204
|
+
|
|
205
|
+
- `src/services/*` — logic folded into repositories.
|
|
206
|
+
- `src/domain/ports/*` barrels and per-port files — structural types
|
|
207
|
+
now live with the repositories they describe.
|
|
208
|
+
- Event-bus indirection — repositories emit via the engine's configured
|
|
209
|
+
transport directly.
|
|
210
|
+
|
|
211
|
+
### Added
|
|
212
|
+
|
|
213
|
+
- Subpath export `/notifications` — notification port + built-in
|
|
214
|
+
lifecycle payloads (tier up/down, points expiring, referral credit).
|
|
215
|
+
- Test restructure — 257 tests across unit + integration projects; the
|
|
216
|
+
deleted legacy suites were collapsed into focused scenario files that
|
|
217
|
+
exercise the new public API.
|
|
218
|
+
|
|
219
|
+
### Peer compatibility
|
|
220
|
+
|
|
221
|
+
Anyone on 0.1.0 wiring against the old `*.service.ts` modules will need
|
|
222
|
+
to migrate to the repository methods. Signatures are similar — earn /
|
|
223
|
+
redeem / adjust remain the nouns — but the call sites move from
|
|
224
|
+
`services.earning.earnPoints(...)` to `repositories.pointTransaction.earn(...)`
|
|
225
|
+
and similar. No behavior changes in the ledger semantics.
|
|
226
|
+
|
|
227
|
+
## [0.1.0]
|
|
228
|
+
|
|
229
|
+
Initial release. Loyalty points, tiers, earning rules, referrals, and
|
|
230
|
+
redemption engine for MongoDB. Tenant-scoped, event-emitting, atomic
|
|
231
|
+
point-ledger with CAS guards.
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as DEFAULT_TENANT_FIELD, r as DEFAULT_TENANT_TYPE } from "./constants-DkQYkOK1.mjs";
|
|
2
|
-
import { C as TenantIsolationError, E as ValidationError, S as SelfReferralError, T as UnmanagedSessionError, _ as RedemptionExpiredError, a as PointTransactionRepository, b as ReferralNotFoundError, c as flushPendingLoyaltyEvents, d as DuplicateReferralError, f as InsufficientPointsError, g as MemberNotFoundError, h as MemberInactiveError, i as RedemptionRepository, l as EarningRuleRepository, m as MemberAlreadyEnrolledError, n as TierDefinitionRepository, o as MemberRepository, p as LoyaltyCapabilityError, r as ReferralRepository, s as PENDING_EVENTS, t as createRepositories, u as CircularReferralError, v as RedemptionNotFoundError, w as TierNotFoundError, x as RuleNotFoundError, y as ReferralLimitExceededError } from "./repositories-
|
|
2
|
+
import { C as TenantIsolationError, E as ValidationError, S as SelfReferralError, T as UnmanagedSessionError, _ as RedemptionExpiredError, a as PointTransactionRepository, b as ReferralNotFoundError, c as flushPendingLoyaltyEvents, d as DuplicateReferralError, f as InsufficientPointsError, g as MemberNotFoundError, h as MemberInactiveError, i as RedemptionRepository, l as EarningRuleRepository, m as MemberAlreadyEnrolledError, n as TierDefinitionRepository, o as MemberRepository, p as LoyaltyCapabilityError, r as ReferralRepository, s as PENDING_EVENTS, t as createRepositories, u as CircularReferralError, v as RedemptionNotFoundError, w as TierNotFoundError, x as RuleNotFoundError, y as ReferralLimitExceededError } from "./repositories-CjWfUlrA.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-DN5_p6T0.mjs";
|
|
4
4
|
import { n as createModels } from "./models-Ddh62lgO.mjs";
|
|
5
5
|
import { n as createEvent, t as LoyaltyEvents } from "./loyalty-events-C38JA3cU.mjs";
|
package/dist/models/index.d.mts
CHANGED
|
@@ -86,9 +86,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
|
|
|
86
86
|
_id: _$mongoose.Types.ObjectId;
|
|
87
87
|
} & {
|
|
88
88
|
__v: number;
|
|
89
|
-
}, "id"> & {
|
|
89
|
+
}, "id"> & _$mongoose.HydratedDocumentOverrides<{
|
|
90
90
|
id: string;
|
|
91
|
-
}
|
|
91
|
+
}>, _$mongoose.Schema<any, Model<any, any, any, any, any, any, any>, {}, {}, {}, {}, {
|
|
92
92
|
timestamps: true;
|
|
93
93
|
}, {
|
|
94
94
|
programId: string;
|
|
@@ -167,9 +167,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
|
|
|
167
167
|
_id: _$mongoose.Types.ObjectId;
|
|
168
168
|
} & {
|
|
169
169
|
__v: number;
|
|
170
|
-
}, "id"> & {
|
|
170
|
+
}, "id"> & _$mongoose.HydratedDocumentOverrides<{
|
|
171
171
|
id: string;
|
|
172
|
-
}
|
|
172
|
+
}>, unknown, {
|
|
173
173
|
programId: string;
|
|
174
174
|
externalId: string;
|
|
175
175
|
externalType: string;
|
|
@@ -298,9 +298,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
|
|
|
298
298
|
_id: _$mongoose.Types.ObjectId;
|
|
299
299
|
} & {
|
|
300
300
|
__v: number;
|
|
301
|
-
}, "id"> & {
|
|
301
|
+
}, "id"> & _$mongoose.HydratedDocumentOverrides<{
|
|
302
302
|
id: string;
|
|
303
|
-
}
|
|
303
|
+
}>, _$mongoose.Schema<any, Model<any, any, any, any, any, any, any>, {}, {}, {}, {}, {
|
|
304
304
|
timestamps: {
|
|
305
305
|
createdAt: true;
|
|
306
306
|
updatedAt: false;
|
|
@@ -373,9 +373,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
|
|
|
373
373
|
_id: _$mongoose.Types.ObjectId;
|
|
374
374
|
} & {
|
|
375
375
|
__v: number;
|
|
376
|
-
}, "id"> & {
|
|
376
|
+
}, "id"> & _$mongoose.HydratedDocumentOverrides<{
|
|
377
377
|
id: string;
|
|
378
|
-
}
|
|
378
|
+
}>, unknown, {
|
|
379
379
|
programId: string;
|
|
380
380
|
type: "earn" | "redeem" | "adjust" | "expire" | "transfer" | "referral_bonus";
|
|
381
381
|
description: string;
|
|
@@ -519,9 +519,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
|
|
|
519
519
|
_id: _$mongoose.Types.ObjectId;
|
|
520
520
|
} & {
|
|
521
521
|
__v: number;
|
|
522
|
-
}, "id"> & {
|
|
522
|
+
}, "id"> & _$mongoose.HydratedDocumentOverrides<{
|
|
523
523
|
id: string;
|
|
524
|
-
}
|
|
524
|
+
}>, _$mongoose.Schema<any, Model<any, any, any, any, any, any, any>, {}, {}, {}, {}, {
|
|
525
525
|
timestamps: true;
|
|
526
526
|
}, {
|
|
527
527
|
programId: string;
|
|
@@ -618,9 +618,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
|
|
|
618
618
|
_id: _$mongoose.Types.ObjectId;
|
|
619
619
|
} & {
|
|
620
620
|
__v: number;
|
|
621
|
-
}, "id"> & {
|
|
621
|
+
}, "id"> & _$mongoose.HydratedDocumentOverrides<{
|
|
622
622
|
id: string;
|
|
623
|
-
}
|
|
623
|
+
}>, unknown, {
|
|
624
624
|
programId: string;
|
|
625
625
|
type: "order" | "action" | "category" | "tier_bonus";
|
|
626
626
|
name: string;
|
|
@@ -764,9 +764,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
|
|
|
764
764
|
_id: _$mongoose.Types.ObjectId;
|
|
765
765
|
} & {
|
|
766
766
|
__v: number;
|
|
767
|
-
}, "id"> & {
|
|
767
|
+
}, "id"> & _$mongoose.HydratedDocumentOverrides<{
|
|
768
768
|
id: string;
|
|
769
|
-
}
|
|
769
|
+
}>, _$mongoose.Schema<any, Model<any, any, any, any, any, any, any>, {}, {}, {}, {}, {
|
|
770
770
|
timestamps: true;
|
|
771
771
|
}, {
|
|
772
772
|
programId: string;
|
|
@@ -839,9 +839,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
|
|
|
839
839
|
_id: _$mongoose.Types.ObjectId;
|
|
840
840
|
} & {
|
|
841
841
|
__v: number;
|
|
842
|
-
}, "id"> & {
|
|
842
|
+
}, "id"> & _$mongoose.HydratedDocumentOverrides<{
|
|
843
843
|
id: string;
|
|
844
|
-
}
|
|
844
|
+
}>, unknown, {
|
|
845
845
|
programId: string;
|
|
846
846
|
name: string;
|
|
847
847
|
rank: number;
|
|
@@ -972,9 +972,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
|
|
|
972
972
|
_id: _$mongoose.Types.ObjectId;
|
|
973
973
|
} & {
|
|
974
974
|
__v: number;
|
|
975
|
-
}, "id"> & {
|
|
975
|
+
}, "id"> & _$mongoose.HydratedDocumentOverrides<{
|
|
976
976
|
id: string;
|
|
977
|
-
}
|
|
977
|
+
}>, _$mongoose.Schema<any, Model<any, any, any, any, any, any, any>, {}, {}, {}, {}, {
|
|
978
978
|
timestamps: true;
|
|
979
979
|
}, {
|
|
980
980
|
programId: string;
|
|
@@ -1050,9 +1050,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
|
|
|
1050
1050
|
_id: _$mongoose.Types.ObjectId;
|
|
1051
1051
|
} & {
|
|
1052
1052
|
__v: number;
|
|
1053
|
-
}, "id"> & {
|
|
1053
|
+
}, "id"> & _$mongoose.HydratedDocumentOverrides<{
|
|
1054
1054
|
id: string;
|
|
1055
|
-
}
|
|
1055
|
+
}>, unknown, {
|
|
1056
1056
|
programId: string;
|
|
1057
1057
|
status: "expired" | "reserved" | "confirmed" | "released" | "reversed";
|
|
1058
1058
|
memberId: _$mongoose.Types.ObjectId;
|
|
@@ -1164,9 +1164,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
|
|
|
1164
1164
|
_id: _$mongoose.Types.ObjectId;
|
|
1165
1165
|
} & {
|
|
1166
1166
|
__v: number;
|
|
1167
|
-
}, "id"> & {
|
|
1167
|
+
}, "id"> & _$mongoose.HydratedDocumentOverrides<{
|
|
1168
1168
|
id: string;
|
|
1169
|
-
}
|
|
1169
|
+
}>, _$mongoose.Schema<any, Model<any, any, any, any, any, any, any>, {}, {}, {}, {}, {
|
|
1170
1170
|
timestamps: true;
|
|
1171
1171
|
}, {
|
|
1172
1172
|
programId: string;
|
|
@@ -1221,9 +1221,9 @@ declare function createModels(connection: Connection, tenant: ResolvedTenant, in
|
|
|
1221
1221
|
_id: _$mongoose.Types.ObjectId;
|
|
1222
1222
|
} & {
|
|
1223
1223
|
__v: number;
|
|
1224
|
-
}, "id"> & {
|
|
1224
|
+
}, "id"> & _$mongoose.HydratedDocumentOverrides<{
|
|
1225
1225
|
id: string;
|
|
1226
|
-
}
|
|
1226
|
+
}>, unknown, {
|
|
1227
1227
|
programId: string;
|
|
1228
1228
|
status: "expired" | "pending" | "approved" | "rejected";
|
|
1229
1229
|
referralCode: string;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as PointTransactionRepository, i as RedemptionRepository, l as EarningRuleRepository, n as TierDefinitionRepository, o as MemberRepository, r as ReferralRepository, t as createRepositories } from "../repositories-
|
|
1
|
+
import { a as PointTransactionRepository, i as RedemptionRepository, l as EarningRuleRepository, n as TierDefinitionRepository, o as MemberRepository, r as ReferralRepository, t as createRepositories } from "../repositories-CjWfUlrA.mjs";
|
|
2
2
|
export { EarningRuleRepository, MemberRepository, PointTransactionRepository, RedemptionRepository, ReferralRepository, TierDefinitionRepository, createRepositories };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as createEvent, t as LoyaltyEvents } from "./loyalty-events-C38JA3cU.mjs";
|
|
2
|
-
import { Repository, multiTenantPlugin } from "@classytic/mongokit";
|
|
2
|
+
import { Repository, appendOnlyPlugin, multiTenantPlugin } from "@classytic/mongokit";
|
|
3
3
|
import { localTimeParts } from "@classytic/primitives/timezone";
|
|
4
4
|
import { addDays, isoWeekday } from "@classytic/primitives/calendar";
|
|
5
5
|
import { randomBytes } from "node:crypto";
|
|
@@ -709,7 +709,7 @@ var PointTransactionRepository = class extends Repository {
|
|
|
709
709
|
unitOfWork;
|
|
710
710
|
config;
|
|
711
711
|
constructor(model, plugins = []) {
|
|
712
|
-
super(model, plugins);
|
|
712
|
+
super(model, [...plugins, appendOnlyPlugin()]);
|
|
713
713
|
}
|
|
714
714
|
/** Session-bound outbox save + transport publish (PACKAGE_RULES §5.5 + P8). */
|
|
715
715
|
async _dispatch(event, ctx) {
|
|
@@ -1001,7 +1001,8 @@ var PointTransactionRepository = class extends Repository {
|
|
|
1001
1001
|
});
|
|
1002
1002
|
await this.update(tx._id, { expiredAt: now }, {
|
|
1003
1003
|
session,
|
|
1004
|
-
...tc
|
|
1004
|
+
...tc,
|
|
1005
|
+
bypassAppendOnly: true
|
|
1005
1006
|
});
|
|
1006
1007
|
await this.memberRepo.updateBalance(tx.memberId, {
|
|
1007
1008
|
current: -tx.points,
|
package/package.json
CHANGED
|
@@ -1,116 +1,116 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@classytic/loyalty",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Loyalty points, tiers, earning rules, referrals, and redemption engine for MongoDB",
|
|
5
|
-
"author": "Classytic",
|
|
6
|
-
"homepage": "https://www.npmjs.com/package/@classytic/loyalty",
|
|
7
|
-
"bugs": {
|
|
8
|
-
"url": "https://github.com/classytic/loyalty/issues"
|
|
9
|
-
},
|
|
10
|
-
"repository": {
|
|
11
|
-
"type": "git",
|
|
12
|
-
"url": "git+https://github.com/classytic/loyalty.git"
|
|
13
|
-
},
|
|
14
|
-
"publishConfig": {
|
|
15
|
-
"access": "public"
|
|
16
|
-
},
|
|
17
|
-
"type": "module",
|
|
18
|
-
"sideEffects": false,
|
|
19
|
-
"engines": {
|
|
20
|
-
"node": ">=22"
|
|
21
|
-
},
|
|
22
|
-
"exports": {
|
|
23
|
-
".": {
|
|
24
|
-
"types": "./dist/index.d.mts",
|
|
25
|
-
"default": "./dist/index.mjs"
|
|
26
|
-
},
|
|
27
|
-
"./domain/enums": {
|
|
28
|
-
"types": "./dist/domain/enums/index.d.mts",
|
|
29
|
-
"default": "./dist/domain/enums/index.mjs"
|
|
30
|
-
},
|
|
31
|
-
"./models": {
|
|
32
|
-
"types": "./dist/models/index.d.mts",
|
|
33
|
-
"default": "./dist/models/index.mjs"
|
|
34
|
-
},
|
|
35
|
-
"./repositories": {
|
|
36
|
-
"types": "./dist/repositories/index.d.mts",
|
|
37
|
-
"default": "./dist/repositories/index.mjs"
|
|
38
|
-
},
|
|
39
|
-
"./events": {
|
|
40
|
-
"types": "./dist/events/index.d.mts",
|
|
41
|
-
"default": "./dist/events/index.mjs"
|
|
42
|
-
},
|
|
43
|
-
"./types": {
|
|
44
|
-
"types": "./dist/types/index.d.mts",
|
|
45
|
-
"default": "./dist/types/index.mjs"
|
|
46
|
-
},
|
|
47
|
-
"./notifications": {
|
|
48
|
-
"types": "./dist/notifications/index.d.mts",
|
|
49
|
-
"default": "./dist/notifications/index.mjs"
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
"main": "./dist/index.mjs",
|
|
53
|
-
"types": "./dist/index.d.mts",
|
|
54
|
-
"files": [
|
|
55
|
-
"dist",
|
|
56
|
-
"README.md",
|
|
57
|
-
"CHANGELOG.md",
|
|
58
|
-
"LICENSE"
|
|
59
|
-
],
|
|
60
|
-
"scripts": {
|
|
61
|
-
"build": "tsdown",
|
|
62
|
-
"dev": "tsdown --watch",
|
|
63
|
-
"test": "vitest run --project unit --project integration",
|
|
64
|
-
"test:unit": "vitest run --project unit",
|
|
65
|
-
"test:integration": "vitest run --project integration",
|
|
66
|
-
"test:scenarios": "vitest run --project scenarios",
|
|
67
|
-
"test:all": "vitest run",
|
|
68
|
-
"test:watch": "vitest --project unit --project integration",
|
|
69
|
-
"test:coverage": "vitest run --coverage",
|
|
70
|
-
"typecheck": "tsc --noEmit",
|
|
71
|
-
"lint": "biome check src/ tests/",
|
|
72
|
-
"lint:fix": "biome check --write src/ tests/",
|
|
73
|
-
"format": "biome format --write src/ tests/",
|
|
74
|
-
"check": "biome check --write --unsafe src/ tests/",
|
|
75
|
-
"prepublishOnly": "npm run lint && npm run typecheck && npm run test && npm run build",
|
|
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"
|
|
79
|
-
},
|
|
80
|
-
"peerDependencies": {
|
|
81
|
-
"@classytic/mongokit": ">=3.
|
|
82
|
-
"@classytic/primitives": ">=0.
|
|
83
|
-
"@classytic/repo-core": ">=0.
|
|
84
|
-
"mongoose": ">=9.4.1",
|
|
85
|
-
"zod": ">=4.0.0"
|
|
86
|
-
},
|
|
87
|
-
"devDependencies": {
|
|
88
|
-
"@biomejs/biome": "^2.4.9",
|
|
89
|
-
"@classytic/dev-tools": "^0.2.0",
|
|
90
|
-
"@classytic/mongokit": "^3.
|
|
91
|
-
"@classytic/primitives": "^0.
|
|
92
|
-
"@classytic/repo-core": "^0.
|
|
93
|
-
"@types/node": "^25.5.0",
|
|
94
|
-
"@vitest/coverage-v8": "^3.2.4",
|
|
95
|
-
"knip": "^6.3.0",
|
|
96
|
-
"mongodb-memory-server": "^10.2.3",
|
|
97
|
-
"mongoose": "^9.4.1",
|
|
98
|
-
"tsdown": "^0.21.5",
|
|
99
|
-
"typescript": "^5.7.0",
|
|
100
|
-
"vitest": "^3.0.0",
|
|
101
|
-
"zod": "^4.3.6"
|
|
102
|
-
},
|
|
103
|
-
"keywords": [
|
|
104
|
-
"loyalty",
|
|
105
|
-
"points",
|
|
106
|
-
"rewards",
|
|
107
|
-
"tiers",
|
|
108
|
-
"referral",
|
|
109
|
-
"membership",
|
|
110
|
-
"earning-rules",
|
|
111
|
-
"redemption",
|
|
112
|
-
"mongodb",
|
|
113
|
-
"mongoose"
|
|
114
|
-
],
|
|
115
|
-
"license": "MIT"
|
|
116
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@classytic/loyalty",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Loyalty points, tiers, earning rules, referrals, and redemption engine for MongoDB",
|
|
5
|
+
"author": "Classytic",
|
|
6
|
+
"homepage": "https://www.npmjs.com/package/@classytic/loyalty",
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://github.com/classytic/loyalty/issues"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/classytic/loyalty.git"
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"type": "module",
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=22"
|
|
21
|
+
},
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./dist/index.d.mts",
|
|
25
|
+
"default": "./dist/index.mjs"
|
|
26
|
+
},
|
|
27
|
+
"./domain/enums": {
|
|
28
|
+
"types": "./dist/domain/enums/index.d.mts",
|
|
29
|
+
"default": "./dist/domain/enums/index.mjs"
|
|
30
|
+
},
|
|
31
|
+
"./models": {
|
|
32
|
+
"types": "./dist/models/index.d.mts",
|
|
33
|
+
"default": "./dist/models/index.mjs"
|
|
34
|
+
},
|
|
35
|
+
"./repositories": {
|
|
36
|
+
"types": "./dist/repositories/index.d.mts",
|
|
37
|
+
"default": "./dist/repositories/index.mjs"
|
|
38
|
+
},
|
|
39
|
+
"./events": {
|
|
40
|
+
"types": "./dist/events/index.d.mts",
|
|
41
|
+
"default": "./dist/events/index.mjs"
|
|
42
|
+
},
|
|
43
|
+
"./types": {
|
|
44
|
+
"types": "./dist/types/index.d.mts",
|
|
45
|
+
"default": "./dist/types/index.mjs"
|
|
46
|
+
},
|
|
47
|
+
"./notifications": {
|
|
48
|
+
"types": "./dist/notifications/index.d.mts",
|
|
49
|
+
"default": "./dist/notifications/index.mjs"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"main": "./dist/index.mjs",
|
|
53
|
+
"types": "./dist/index.d.mts",
|
|
54
|
+
"files": [
|
|
55
|
+
"dist",
|
|
56
|
+
"README.md",
|
|
57
|
+
"CHANGELOG.md",
|
|
58
|
+
"LICENSE"
|
|
59
|
+
],
|
|
60
|
+
"scripts": {
|
|
61
|
+
"build": "tsdown",
|
|
62
|
+
"dev": "tsdown --watch",
|
|
63
|
+
"test": "vitest run --project unit --project integration",
|
|
64
|
+
"test:unit": "vitest run --project unit",
|
|
65
|
+
"test:integration": "vitest run --project integration",
|
|
66
|
+
"test:scenarios": "vitest run --project scenarios",
|
|
67
|
+
"test:all": "vitest run",
|
|
68
|
+
"test:watch": "vitest --project unit --project integration",
|
|
69
|
+
"test:coverage": "vitest run --coverage",
|
|
70
|
+
"typecheck": "tsc --noEmit",
|
|
71
|
+
"lint": "biome check src/ tests/",
|
|
72
|
+
"lint:fix": "biome check --write src/ tests/",
|
|
73
|
+
"format": "biome format --write src/ tests/",
|
|
74
|
+
"check": "biome check --write --unsafe src/ tests/",
|
|
75
|
+
"prepublishOnly": "npm run lint && npm run typecheck && npm run test && npm run build",
|
|
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"
|
|
79
|
+
},
|
|
80
|
+
"peerDependencies": {
|
|
81
|
+
"@classytic/mongokit": ">=3.22.0",
|
|
82
|
+
"@classytic/primitives": ">=0.11.0",
|
|
83
|
+
"@classytic/repo-core": ">=0.13.0",
|
|
84
|
+
"mongoose": ">=9.4.1",
|
|
85
|
+
"zod": ">=4.0.0"
|
|
86
|
+
},
|
|
87
|
+
"devDependencies": {
|
|
88
|
+
"@biomejs/biome": "^2.4.9",
|
|
89
|
+
"@classytic/dev-tools": "^0.2.0",
|
|
90
|
+
"@classytic/mongokit": "^3.22.0",
|
|
91
|
+
"@classytic/primitives": "^0.11.0",
|
|
92
|
+
"@classytic/repo-core": "^0.13.0",
|
|
93
|
+
"@types/node": "^25.5.0",
|
|
94
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
95
|
+
"knip": "^6.3.0",
|
|
96
|
+
"mongodb-memory-server": "^10.2.3",
|
|
97
|
+
"mongoose": "^9.4.1",
|
|
98
|
+
"tsdown": "^0.21.5",
|
|
99
|
+
"typescript": "^5.7.0",
|
|
100
|
+
"vitest": "^3.0.0",
|
|
101
|
+
"zod": "^4.3.6"
|
|
102
|
+
},
|
|
103
|
+
"keywords": [
|
|
104
|
+
"loyalty",
|
|
105
|
+
"points",
|
|
106
|
+
"rewards",
|
|
107
|
+
"tiers",
|
|
108
|
+
"referral",
|
|
109
|
+
"membership",
|
|
110
|
+
"earning-rules",
|
|
111
|
+
"redemption",
|
|
112
|
+
"mongodb",
|
|
113
|
+
"mongoose"
|
|
114
|
+
],
|
|
115
|
+
"license": "MIT"
|
|
116
|
+
}
|