@saasicat/nest 1.0.0-rc.2 → 1.0.0-rc.21

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.
Files changed (154) hide show
  1. package/README.md +44 -8
  2. package/dist/.build-stamp +1 -1
  3. package/dist/_entries.cjs +10214 -4796
  4. package/dist/admin/index.d.cts +5 -4
  5. package/dist/admin/index.d.ts +5 -4
  6. package/dist/admin/index.js +13 -9
  7. package/dist/{admin-resources.module-CpvzcfCc.d.cts → admin-resources.module-B4XNvdH-.d.cts} +1 -2
  8. package/dist/{admin-resources.module-DF5_Q7RD.d.ts → admin-resources.module-CWqdI9BV.d.ts} +1 -2
  9. package/dist/{admin-stats.service-TYiSUwIR.d.ts → admin-stats.service-CsLNt19Z.d.ts} +11 -7
  10. package/dist/{admin-stats.service-DfKupywR.d.cts → admin-stats.service-DrZrU8IJ.d.cts} +11 -7
  11. package/dist/{aggregation-B3CD0v_d.d.cts → aggregation-ChpZDh0g.d.ts} +39 -6
  12. package/dist/{aggregation-BFzW07DE.d.ts → aggregation-OpnhwzF5.d.cts} +39 -6
  13. package/dist/billing/index.d.cts +448 -108
  14. package/dist/billing/index.d.ts +448 -108
  15. package/dist/billing/index.js +108 -41
  16. package/dist/catalog/index.d.cts +74 -45
  17. package/dist/catalog/index.d.ts +74 -45
  18. package/dist/catalog/index.js +21 -13
  19. package/dist/catalog.module-C9O7BxRq.d.ts +180 -0
  20. package/dist/catalog.module-T-I5Ricz.d.cts +180 -0
  21. package/dist/checkout-offer/index.d.cts +17 -22
  22. package/dist/checkout-offer/index.d.ts +17 -22
  23. package/dist/checkout-offer/index.js +21 -7
  24. package/dist/checkout-offer.module-DFhGy6JO.d.cts +58 -0
  25. package/dist/checkout-offer.module-x7SP_dX4.d.ts +58 -0
  26. package/dist/checkout-offer.service-BeHgYikI.d.ts +242 -0
  27. package/dist/checkout-offer.service-CCVxG9b2.d.cts +242 -0
  28. package/dist/chunk-34XVPR6P.js +222 -0
  29. package/dist/{chunk-AOQJEYOL.js → chunk-3QVGA6JX.js} +3 -1
  30. package/dist/chunk-3RLWML2S.js +896 -0
  31. package/dist/{chunk-TXC3LHHB.js → chunk-4SDSYV4A.js} +21 -13
  32. package/dist/chunk-55QEDAMX.js +946 -0
  33. package/dist/chunk-5CKC7O52.js +140 -0
  34. package/dist/chunk-6PJE7EWO.js +185 -0
  35. package/dist/chunk-7NJKVC5X.js +353 -0
  36. package/dist/{chunk-RUNZ3X4C.js → chunk-AOS2JPDD.js} +1263 -185
  37. package/dist/{chunk-WOVQPXV4.js → chunk-CQ2ZZMTD.js} +702 -725
  38. package/dist/{chunk-NA6O63B7.js → chunk-F5X66HF5.js} +5 -5
  39. package/dist/chunk-FEQZCWUL.js +174 -0
  40. package/dist/{chunk-VIJ5NJWC.js → chunk-G6EZWECL.js} +107 -31
  41. package/dist/chunk-G7RQO2XO.js +0 -0
  42. package/dist/{chunk-N3L3AICU.js → chunk-GPQWGA6B.js} +5 -6
  43. package/dist/chunk-GZTL64QK.js +7 -0
  44. package/dist/{chunk-SABTXESR.js → chunk-J7NJRK3K.js} +17 -1
  45. package/dist/{chunk-SEPN52AD.js → chunk-JBCC6C3M.js} +566 -307
  46. package/dist/{chunk-I7GTA3RX.js → chunk-JQRA724W.js} +1 -1
  47. package/dist/{chunk-WXYJHZCN.js → chunk-JVAEKTJ4.js} +2 -14
  48. package/dist/chunk-KPEMTBOP.js +26 -0
  49. package/dist/chunk-LF6J4YYN.js +301 -0
  50. package/dist/{chunk-LTT736P3.js → chunk-LLYVYRGJ.js} +228 -387
  51. package/dist/{chunk-6Z7JR4EW.js → chunk-M47BNEY2.js} +1897 -633
  52. package/dist/{chunk-KFT5AIIH.js → chunk-NDLC5GYK.js} +206 -24
  53. package/dist/{chunk-O2J2HDXA.js → chunk-OOTEXP47.js} +3 -8
  54. package/dist/chunk-QPVDCKYS.js +702 -0
  55. package/dist/{chunk-NHVDCYK5.js → chunk-R5YCJBHY.js} +71 -106
  56. package/dist/{chunk-XBYAFEOR.js → chunk-S33SO5XX.js} +1 -1
  57. package/dist/chunk-SZ7RFPXA.js +10 -0
  58. package/dist/chunk-TBBZWZQT.js +98 -0
  59. package/dist/chunk-TOEFN7DN.js +295 -0
  60. package/dist/{chunk-7ZEGFL42.js → chunk-WHJSYKNC.js} +6 -6
  61. package/dist/chunk-WOEJ6K7M.js +55 -0
  62. package/dist/{chunk-AU3OOREM.js → chunk-XSSWYPP5.js} +16 -3
  63. package/dist/contract-line-item-money-B0Z3Ogur.d.cts +43 -0
  64. package/dist/contract-line-item-money-B0Z3Ogur.d.ts +43 -0
  65. package/dist/discovery/index.d.cts +2 -2
  66. package/dist/discovery/index.d.ts +2 -2
  67. package/dist/discovery/index.js +6 -6
  68. package/dist/{discovery.scanner-9cMqF95o.d.cts → discovery.scanner-DXKc6JkV.d.cts} +1 -1
  69. package/dist/{discovery.scanner-9cMqF95o.d.ts → discovery.scanner-DXKc6JkV.d.ts} +1 -1
  70. package/dist/{enforce-quota.interceptor-Df0e3OWe.d.cts → enforce-quota.interceptor-BFyVdWe8.d.cts} +6 -6
  71. package/dist/{enforce-quota.interceptor-DW3etVyI.d.ts → enforce-quota.interceptor-GaBXboGx.d.ts} +6 -6
  72. package/dist/entitlement/index.d.cts +27 -4
  73. package/dist/entitlement/index.d.ts +27 -4
  74. package/dist/entitlement/index.js +13 -8
  75. package/dist/index.d.cts +209 -28
  76. package/dist/index.d.ts +209 -28
  77. package/dist/index.js +285 -163
  78. package/dist/issuer-identity.check-CBO0vcsq.d.cts +197 -0
  79. package/dist/issuer-identity.check-Cz9CEMkZ.d.ts +197 -0
  80. package/dist/{module-options-COSEb0VW.d.ts → module-options-DmQ3G4sZ.d.ts} +135 -41
  81. package/dist/{module-options-BG3MzQva.d.cts → module-options-qAOwinHR.d.cts} +135 -41
  82. package/dist/payment-callback.service-CR84Xw1Z.d.cts +125 -0
  83. package/dist/payment-callback.service-CR84Xw1Z.d.ts +125 -0
  84. package/dist/payments/index.cjs +4 -0
  85. package/dist/payments/index.d.cts +187 -0
  86. package/dist/payments/index.d.ts +187 -0
  87. package/dist/payments/index.js +141 -0
  88. package/dist/payments.module-BdUq9ot_.d.cts +57 -0
  89. package/dist/payments.module-CQGv_VG7.d.ts +57 -0
  90. package/dist/plan-catalog-source-DWe-BGY1.d.cts +21 -0
  91. package/dist/plan-catalog-source-DWe-BGY1.d.ts +21 -0
  92. package/dist/{plan-resolution-Cgo_TR1H.d.cts → plan-resolution-CwUy_OqC.d.cts} +14 -0
  93. package/dist/{plan-resolution-Cgo_TR1H.d.ts → plan-resolution-CwUy_OqC.d.ts} +14 -0
  94. package/dist/{plan-versions.service-DChhbK6h.d.ts → plan-versions.service-C0N-Em2W.d.ts} +38 -64
  95. package/dist/{plan-versions.service-TPEtbN0r.d.cts → plan-versions.service-DARLh-qI.d.cts} +38 -64
  96. package/dist/platform/index.d.cts +52 -23
  97. package/dist/platform/index.d.ts +52 -23
  98. package/dist/platform/index.js +57 -36
  99. package/dist/promo/index.d.cts +23 -8
  100. package/dist/promo/index.d.ts +23 -8
  101. package/dist/promo/index.js +15 -7
  102. package/dist/{promo.module-DtvIycpt.d.cts → promo.module-CKkECmU8.d.cts} +9 -3
  103. package/dist/{promo.module-Z8h1PNX-.d.ts → promo.module-DuIJdK3U.d.ts} +9 -3
  104. package/dist/promo.service-BYBu6dy3.d.cts +215 -0
  105. package/dist/promo.service-CFjfF0Xm.d.ts +215 -0
  106. package/dist/registration/index.d.cts +104 -47
  107. package/dist/registration/index.d.ts +104 -47
  108. package/dist/registration/index.js +15 -9
  109. package/dist/subscriber/index.cjs +4 -0
  110. package/dist/subscriber/index.d.cts +28 -0
  111. package/dist/subscriber/index.d.ts +28 -0
  112. package/dist/subscriber/index.js +16 -0
  113. package/dist/subscriber.service-5W6Gi-1B.d.cts +68 -0
  114. package/dist/subscriber.service-5W6Gi-1B.d.ts +68 -0
  115. package/dist/subscription-contract/index.d.cts +4 -2
  116. package/dist/subscription-contract/index.d.ts +4 -2
  117. package/dist/subscription-contract/index.js +13 -5
  118. package/dist/{subscription-contract.module-fDFdQ0jm.d.ts → subscription-contract.module-BfIRpc1o.d.ts} +8 -2
  119. package/dist/{subscription-contract.module-CEK8HS5-.d.cts → subscription-contract.module-DTMuUJYd.d.cts} +8 -2
  120. package/dist/{subscription-contract.service-hD87MKyg.d.cts → subscription-contract.service-CPZrTM9C.d.ts} +32 -3
  121. package/dist/{subscription-contract.service-hD87MKyg.d.ts → subscription-contract.service-CfNApLuP.d.cts} +32 -3
  122. package/dist/tenant-billing.controller-BLqbsONe.d.ts +812 -0
  123. package/dist/tenant-billing.controller-DKbAqTbM.d.cts +812 -0
  124. package/dist/tenant-billing.module-ClbjzocC.d.cts +364 -0
  125. package/dist/tenant-billing.module-DohgJJ2m.d.ts +364 -0
  126. package/dist/tenant-billing.tokens-G-1xOlMr.d.cts +129 -0
  127. package/dist/tenant-billing.tokens-G-1xOlMr.d.ts +129 -0
  128. package/dist/testing/index.d.cts +44 -17
  129. package/dist/testing/index.d.ts +44 -17
  130. package/dist/testing/index.js +189 -40
  131. package/package.json +24 -4
  132. package/dist/catalog.module-BDDO6iwq.d.cts +0 -103
  133. package/dist/catalog.module-BTnMsE6u.d.ts +0 -103
  134. package/dist/checkout-offer.module-CMtiQJTT.d.ts +0 -39
  135. package/dist/checkout-offer.module-CVpWrzbe.d.cts +0 -39
  136. package/dist/checkout-offer.service-BLOv2HOo.d.cts +0 -49
  137. package/dist/checkout-offer.service-BLOv2HOo.d.ts +0 -49
  138. package/dist/chunk-57V6ZTI6.js +0 -22
  139. package/dist/chunk-BSK6YBLI.js +0 -87
  140. package/dist/chunk-DQKCK7DX.js +0 -217
  141. package/dist/chunk-VXEYLNIB.js +0 -632
  142. package/dist/chunk-WUDYIPYH.js +0 -715
  143. package/dist/define-saasicat-DROBe-b1.d.ts +0 -69
  144. package/dist/define-saasicat-vGBCkFys.d.cts +0 -69
  145. package/dist/promo.service-BJbKAmw3.d.cts +0 -112
  146. package/dist/promo.service-BJbKAmw3.d.ts +0 -112
  147. package/dist/tenant-billing.controller-BQ67mlmI.d.ts +0 -438
  148. package/dist/tenant-billing.controller-DS8kB8a7.d.cts +0 -438
  149. package/dist/tenant-billing.module-BznC3XtW.d.cts +0 -299
  150. package/dist/tenant-billing.module-wUbsQSFG.d.ts +0 -299
  151. /package/dist/{chunk-2FR6ZL7R.js → chunk-BHZOH2DY.js} +0 -0
  152. /package/dist/{chunk-2SLTRKXC.js → chunk-DFOW3JVO.js} +0 -0
  153. /package/dist/{chunk-DBZGV3HC.js → chunk-DU56UKEH.js} +0 -0
  154. /package/dist/{chunk-OYNHOY45.js → chunk-GAUEVIAE.js} +0 -0
@@ -1,715 +0,0 @@
1
- import {
2
- appendImplicitDiscountLineItem
3
- } from "./chunk-BSK6YBLI.js";
4
- import {
5
- SaaSiCatPublicRoute
6
- } from "./chunk-YSC46BGM.js";
7
- import {
8
- BUNDLE_REPOSITORY_TOKEN,
9
- CATALOG_ENTRY_REPOSITORY_TOKEN,
10
- PLAN_REPOSITORY_TOKEN
11
- } from "./chunk-AOQJEYOL.js";
12
- import {
13
- asProvider
14
- } from "./chunk-SABTXESR.js";
15
- import {
16
- __name
17
- } from "./chunk-SHUYVCID.js";
18
-
19
- // src/checkout-offer/checkout-offer.tokens.ts
20
- var CHECKOUT_OFFER_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/nest/CheckoutOfferRepository");
21
-
22
- // src/checkout-offer/checkout-offer.service.ts
23
- import { ConflictException, Inject, Injectable, NotFoundException, Optional, UnprocessableEntityException } from "@nestjs/common";
24
- import { CONTRACT_ERROR_CODES, buildFeatureRequiresIndex, collectUnsatisfiedRequires, startOfUtcDay } from "@saasicat/core";
25
- function _ts_decorate(decorators, target, key, desc) {
26
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
27
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
28
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
29
- return c > 3 && r && Object.defineProperty(target, key, r), r;
30
- }
31
- __name(_ts_decorate, "_ts_decorate");
32
- function _ts_metadata(k, v) {
33
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
34
- }
35
- __name(_ts_metadata, "_ts_metadata");
36
- function _ts_param(paramIndex, decorator) {
37
- return function(target, key) {
38
- decorator(target, key, paramIndex);
39
- };
40
- }
41
- __name(_ts_param, "_ts_param");
42
- var CheckoutOfferService = class {
43
- static {
44
- __name(this, "CheckoutOfferService");
45
- }
46
- repo;
47
- bundles;
48
- plans;
49
- catalogEntries;
50
- constructor(repo, bundles = null, plans = null, catalogEntries = null) {
51
- this.repo = repo;
52
- this.bundles = bundles;
53
- this.plans = plans;
54
- this.catalogEntries = catalogEntries;
55
- }
56
- list(filter) {
57
- return this.repo.list(filter);
58
- }
59
- async getById(id) {
60
- const row = await this.repo.findById(id);
61
- if (!row) {
62
- throw new NotFoundException({
63
- code: CONTRACT_ERROR_CODES.CHECKOUT_OFFER_NOT_FOUND,
64
- message: `CheckoutOffer '${id}' not found`,
65
- params: {
66
- offerId: id
67
- }
68
- });
69
- }
70
- return row;
71
- }
72
- async create(data) {
73
- const normalized = this.normalizeCreateData(data);
74
- await this.assertFeatureRequiresSatisfied({
75
- projectKey: normalized.projectKey,
76
- planKey: normalized.planKey,
77
- planVersionId: normalized.planVersionId ?? null,
78
- bundleVersionIds: normalized.bundleVersionIds ?? [],
79
- lineItems: normalized.lineItems ?? []
80
- });
81
- return this.repo.create(normalized);
82
- }
83
- /** Customization in onboarding — only while the offer is `open`. */
84
- async update(id, data) {
85
- const existing = await this.getById(id);
86
- this.assertOpen(existing, "changed");
87
- const next = this.normalizeUpdateData(existing, data);
88
- await this.assertFeatureRequiresSatisfied({
89
- projectKey: existing.projectKey,
90
- planKey: existing.planKey,
91
- planVersionId: existing.planVersionId,
92
- bundleVersionIds: next.bundleVersionIds ?? [],
93
- lineItems: next.lineItems ?? []
94
- });
95
- return this.repo.update(id, next);
96
- }
97
- /**
98
- * Freezes the offer (`status = 'consumed'`). Returns the final
99
- * snapshot — the caller (registration/billing) writes it
100
- * as `Subscription.packageSnapshot`.
101
- */
102
- async consume(id) {
103
- const existing = await this.getById(id);
104
- this.assertOpen(existing, "consumed");
105
- await this.assertBundleVersionsStillBookable(existing);
106
- return this.repo.consume(id);
107
- }
108
- /**
109
- * #35 P6 — server-side requires validation: the dependencies of all
110
- * features (plan ∪ selected bundles) must be covered within the
111
- * selection, otherwise an offer would be created whose features cannot
112
- * work at the tenant (an app-specific validateModuleDependencies check is
113
- * thereby replaced on the platform side). The requires source are the
114
- * curated FeatureCatalogEntries; without CatalogEntryRepository it is
115
- * skipped (graceful — no requires data available).
116
- */
117
- async assertFeatureRequiresSatisfied(input) {
118
- if (!this.catalogEntries) return;
119
- const entries = await this.catalogEntries.listFeatures({
120
- projectKey: input.projectKey
121
- });
122
- const requiresIndex = buildFeatureRequiresIndex(entries);
123
- if (requiresIndex.size === 0) return;
124
- const selected = /* @__PURE__ */ new Set([
125
- ...await this.resolvePlanFeatures(input),
126
- ...await this.resolveBundleFeatures(input)
127
- ]);
128
- const missingRequires = collectUnsatisfiedRequires([
129
- ...selected
130
- ], requiresIndex);
131
- if (missingRequires.length > 0) {
132
- throw new UnprocessableEntityException({
133
- code: CONTRACT_ERROR_CODES.CHECKOUT_OFFER_FEATURE_DEPENDENCY_UNSATISFIED,
134
- message: `The selected plan does not cover all feature dependencies: [${missingRequires.join(", ")}] are missing from the plan + selected bundles.`,
135
- missingRequires
136
- });
137
- }
138
- }
139
- /**
140
- * Plan features preferably from the server SSOT (PlanRepository); fallback
141
- * is the frozen featuresSnapshot of the plan LineItem (adapter without
142
- * PlanRepository wiring).
143
- */
144
- async resolvePlanFeatures(input) {
145
- if (this.plans) {
146
- const version = input.planVersionId ? await this.plans.findVersionById?.(input.planVersionId) : null;
147
- const resolved = version ?? await this.plans.findActivePlanVersion?.(input.planKey) ?? await this.plans.findLatestLivePlanVersion?.(input.planKey);
148
- if (resolved) return resolved.features ?? [];
149
- }
150
- return input.lineItems.filter((item) => item.kind === "plan").flatMap((item) => item.featuresSnapshot ?? []);
151
- }
152
- /** Bundle features from the BundleVersions; fallback featuresSnapshot. */
153
- async resolveBundleFeatures(input) {
154
- if (this.bundles) {
155
- const features = [];
156
- let allResolved = true;
157
- for (const bundleVersionId of input.bundleVersionIds) {
158
- const version = await this.bundles.findVersionById(bundleVersionId);
159
- if (!version) {
160
- allResolved = false;
161
- break;
162
- }
163
- features.push(...version.features ?? []);
164
- }
165
- if (allResolved) return features;
166
- }
167
- return input.lineItems.filter((item) => item.kind === "bundle").flatMap((item) => item.featuresSnapshot ?? []);
168
- }
169
- async assertBundleVersionsStillBookable(offer) {
170
- if (!this.bundles || !offer.bundleVersionIds || offer.bundleVersionIds.length === 0) {
171
- return;
172
- }
173
- const now = Date.now();
174
- const violations = [];
175
- for (const bundleVersionId of offer.bundleVersionIds) {
176
- const version = await this.bundles.findVersionById(bundleVersionId);
177
- if (!version) {
178
- violations.push({
179
- bundleVersionId,
180
- reason: "missing"
181
- });
182
- continue;
183
- }
184
- if (version.publishedAt === null) {
185
- violations.push({
186
- bundleVersionId,
187
- reason: "not_published"
188
- });
189
- continue;
190
- }
191
- if (version.supersededAt !== null) {
192
- violations.push({
193
- bundleVersionId,
194
- reason: "superseded"
195
- });
196
- continue;
197
- }
198
- if (this.dateIsAfterNow(version.validFrom, now)) {
199
- violations.push({
200
- bundleVersionId,
201
- reason: "not_yet_valid"
202
- });
203
- continue;
204
- }
205
- if (this.isValidUntilExpired(version.validUntil, now)) {
206
- violations.push({
207
- bundleVersionId,
208
- reason: "expired"
209
- });
210
- }
211
- }
212
- if (violations.length > 0) {
213
- throw new UnprocessableEntityException({
214
- code: CONTRACT_ERROR_CODES.CHECKOUT_OFFER_BUNDLE_VERSION_NOT_BOOKABLE,
215
- message: "At least one bundle version from the checkout offer is no longer bookable.",
216
- violations
217
- });
218
- }
219
- }
220
- normalizeCreateData(data) {
221
- const lineItems = this.resolveLineItems({
222
- planKey: data.planKey,
223
- billingCycle: data.billingCycle,
224
- priceBreakdown: data.priceBreakdown,
225
- lineItems: data.lineItems,
226
- bundleVersionIds: data.bundleVersionIds ?? [],
227
- promotionSnapshots: data.promotionSnapshots ?? [],
228
- promoCodeSnapshot: data.promoCodeSnapshot ?? null
229
- });
230
- return {
231
- ...data,
232
- bundles: data.bundles ?? [],
233
- bundleVersionIds: data.bundleVersionIds ?? [],
234
- lineItems,
235
- promotionSnapshots: data.promotionSnapshots ?? [],
236
- promoCodeSnapshot: data.promoCodeSnapshot ?? null,
237
- locale: data.locale ?? "de",
238
- validUntil: data.validUntil ?? null
239
- };
240
- }
241
- normalizeUpdateData(existing, data) {
242
- const priceBreakdown = data.priceBreakdown ?? existing.priceBreakdown;
243
- const billingCycle = data.billingCycle ?? existing.billingCycle;
244
- const bundleVersionIds = data.bundleVersionIds ?? existing.bundleVersionIds ?? [];
245
- const lineItems = this.resolveLineItems({
246
- planKey: existing.planKey,
247
- billingCycle,
248
- priceBreakdown,
249
- lineItems: data.lineItems ?? existing.lineItems,
250
- bundleVersionIds,
251
- promotionSnapshots: data.promotionSnapshots ?? existing.promotionSnapshots ?? [],
252
- promoCodeSnapshot: data.promoCodeSnapshot !== void 0 ? data.promoCodeSnapshot : existing.promoCodeSnapshot ?? null
253
- });
254
- return {
255
- ...data,
256
- bundleVersionIds,
257
- lineItems,
258
- promotionSnapshots: data.promotionSnapshots ?? existing.promotionSnapshots ?? [],
259
- promoCodeSnapshot: data.promoCodeSnapshot !== void 0 ? data.promoCodeSnapshot : existing.promoCodeSnapshot ?? null
260
- };
261
- }
262
- resolveLineItems(input) {
263
- const lineItems = input.lineItems && input.lineItems.length > 0 ? input.lineItems : [
264
- this.defaultPlanLineItem(input.planKey, input.billingCycle, input.priceBreakdown)
265
- ];
266
- const hasPlan = lineItems.some((item) => item.kind === "plan");
267
- if (!hasPlan) {
268
- throw new UnprocessableEntityException({
269
- code: CONTRACT_ERROR_CODES.CHECKOUT_OFFER_PLAN_LINE_ITEM_REQUIRED,
270
- message: "A checkout offer requires a frozen plan line item."
271
- });
272
- }
273
- const missingBundleVersionIds = input.bundleVersionIds.filter((versionId) => !lineItems.some((item) => item.kind === "bundle" && item.sourceVersionId === versionId));
274
- if (missingBundleVersionIds.length > 0) {
275
- throw new UnprocessableEntityException({
276
- code: CONTRACT_ERROR_CODES.CHECKOUT_OFFER_BUNDLE_LINE_ITEMS_REQUIRED,
277
- message: "Every selected bundle version requires a frozen bundle line item.",
278
- bundleVersionIds: missingBundleVersionIds
279
- });
280
- }
281
- return appendImplicitDiscountLineItem({
282
- billingCycle: input.billingCycle,
283
- priceBreakdown: input.priceBreakdown,
284
- lineItems,
285
- promotionSnapshots: input.promotionSnapshots ?? [],
286
- promoCodeSnapshot: input.promoCodeSnapshot ?? null
287
- });
288
- }
289
- defaultPlanLineItem(planKey, billingCycle, priceBreakdown) {
290
- const priceNet = priceBreakdown.planNet;
291
- return {
292
- kind: "plan",
293
- sourceKey: planKey,
294
- sourceVersionId: null,
295
- titleSnapshot: planKey,
296
- descriptionSnapshot: null,
297
- quantity: 1,
298
- unit: null,
299
- priceNet,
300
- priceGross: Math.round(priceNet * (1 + priceBreakdown.vatRate) * 100) / 100,
301
- billingCycle,
302
- featuresSnapshot: [],
303
- quotaEffectsSnapshot: {},
304
- metadata: null
305
- };
306
- }
307
- assertOpen(existing, action) {
308
- if (existing.status === "consumed") {
309
- throw new ConflictException({
310
- code: CONTRACT_ERROR_CODES.CHECKOUT_OFFER_ALREADY_CONSUMED,
311
- message: `Checkout offer '${existing.id}' has already been consumed and cannot be ${action}`,
312
- params: {
313
- offerId: existing.id,
314
- action
315
- }
316
- });
317
- }
318
- if (existing.status === "expired") {
319
- throw new ConflictException({
320
- code: CONTRACT_ERROR_CODES.CHECKOUT_OFFER_EXPIRED,
321
- message: `Checkout offer '${existing.id}' has expired and cannot be ${action}`,
322
- params: {
323
- offerId: existing.id,
324
- action,
325
- validUntil: existing.validUntil ?? null
326
- }
327
- });
328
- }
329
- if (this.isExpired(existing)) {
330
- throw new ConflictException({
331
- code: CONTRACT_ERROR_CODES.CHECKOUT_OFFER_EXPIRED,
332
- message: `CheckoutOffer '${existing.id}' has expired and cannot be ${action}`,
333
- params: {
334
- offerId: existing.id,
335
- action,
336
- validUntil: existing.validUntil ?? null
337
- }
338
- });
339
- }
340
- }
341
- isExpired(row) {
342
- return this.isValidUntilExpired(row.validUntil, Date.now());
343
- }
344
- dateIsAfterNow(value, now) {
345
- if (!value) return false;
346
- const time = new Date(value).getTime();
347
- return Number.isNaN(time) || time > now;
348
- }
349
- // `validUntil` is day-inclusive (day date, UTC midnight): expired
350
- // only from the following day, i.e. when validUntil < startOfDay(now). Symmetric
351
- // to the catalog resolver (buildActivePlanVersionWhere).
352
- isValidUntilExpired(value, nowMs) {
353
- if (!value) return false;
354
- const validUntil = new Date(value).getTime();
355
- if (Number.isNaN(validUntil)) return true;
356
- return validUntil < startOfUtcDay(new Date(nowMs)).getTime();
357
- }
358
- };
359
- CheckoutOfferService = _ts_decorate([
360
- Injectable(),
361
- _ts_param(0, Inject(CHECKOUT_OFFER_REPOSITORY_TOKEN)),
362
- _ts_param(1, Optional()),
363
- _ts_param(1, Inject(BUNDLE_REPOSITORY_TOKEN)),
364
- _ts_param(2, Optional()),
365
- _ts_param(2, Inject(PLAN_REPOSITORY_TOKEN)),
366
- _ts_param(3, Optional()),
367
- _ts_param(3, Inject(CATALOG_ENTRY_REPOSITORY_TOKEN)),
368
- _ts_metadata("design:type", Function),
369
- _ts_metadata("design:paramtypes", [
370
- typeof CheckoutOfferRepository === "undefined" ? Object : CheckoutOfferRepository,
371
- Object,
372
- Object,
373
- Object
374
- ])
375
- ], CheckoutOfferService);
376
-
377
- // src/checkout-offer/dto/checkout-offer.dto.ts
378
- import { IsArray, IsDateString, IsDefined, IsIn, IsObject, IsOptional, IsString, Matches, MaxLength } from "class-validator";
379
- function _ts_decorate2(decorators, target, key, desc) {
380
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
381
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
382
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
383
- return c > 3 && r && Object.defineProperty(target, key, r), r;
384
- }
385
- __name(_ts_decorate2, "_ts_decorate");
386
- function _ts_metadata2(k, v) {
387
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
388
- }
389
- __name(_ts_metadata2, "_ts_metadata");
390
- var PROJECT_KEY_PATTERN = /^[a-z][a-z0-9-]*$/;
391
- var CYCLES = [
392
- "monthly",
393
- "yearly"
394
- ];
395
- var CreateCheckoutOfferDto = class {
396
- static {
397
- __name(this, "CreateCheckoutOfferDto");
398
- }
399
- projectKey;
400
- planKey;
401
- planVersionId;
402
- billingCycle;
403
- promotionId;
404
- promoCode;
405
- bundles;
406
- bundleVersionIds;
407
- priceBreakdown;
408
- lineItems;
409
- promotionSnapshots;
410
- promoCodeSnapshot;
411
- locale;
412
- validUntil;
413
- };
414
- _ts_decorate2([
415
- IsString(),
416
- Matches(PROJECT_KEY_PATTERN, {
417
- message: "projectKey must be kebab-case"
418
- }),
419
- MaxLength(64),
420
- _ts_metadata2("design:type", String)
421
- ], CreateCheckoutOfferDto.prototype, "projectKey", void 0);
422
- _ts_decorate2([
423
- IsString(),
424
- MaxLength(64),
425
- _ts_metadata2("design:type", String)
426
- ], CreateCheckoutOfferDto.prototype, "planKey", void 0);
427
- _ts_decorate2([
428
- IsOptional(),
429
- IsString(),
430
- _ts_metadata2("design:type", Object)
431
- ], CreateCheckoutOfferDto.prototype, "planVersionId", void 0);
432
- _ts_decorate2([
433
- IsString(),
434
- IsIn(CYCLES),
435
- _ts_metadata2("design:type", String)
436
- ], CreateCheckoutOfferDto.prototype, "billingCycle", void 0);
437
- _ts_decorate2([
438
- IsOptional(),
439
- IsString(),
440
- _ts_metadata2("design:type", Object)
441
- ], CreateCheckoutOfferDto.prototype, "promotionId", void 0);
442
- _ts_decorate2([
443
- IsOptional(),
444
- IsString(),
445
- MaxLength(64),
446
- _ts_metadata2("design:type", Object)
447
- ], CreateCheckoutOfferDto.prototype, "promoCode", void 0);
448
- _ts_decorate2([
449
- IsOptional(),
450
- IsArray(),
451
- IsString({
452
- each: true
453
- }),
454
- _ts_metadata2("design:type", Array)
455
- ], CreateCheckoutOfferDto.prototype, "bundles", void 0);
456
- _ts_decorate2([
457
- IsOptional(),
458
- IsArray(),
459
- IsString({
460
- each: true
461
- }),
462
- _ts_metadata2("design:type", Array)
463
- ], CreateCheckoutOfferDto.prototype, "bundleVersionIds", void 0);
464
- _ts_decorate2([
465
- IsDefined(),
466
- IsObject(),
467
- _ts_metadata2("design:type", typeof CheckoutOfferPriceBreakdown === "undefined" ? Object : CheckoutOfferPriceBreakdown)
468
- ], CreateCheckoutOfferDto.prototype, "priceBreakdown", void 0);
469
- _ts_decorate2([
470
- IsOptional(),
471
- IsArray(),
472
- _ts_metadata2("design:type", Array)
473
- ], CreateCheckoutOfferDto.prototype, "lineItems", void 0);
474
- _ts_decorate2([
475
- IsOptional(),
476
- IsArray(),
477
- _ts_metadata2("design:type", Array)
478
- ], CreateCheckoutOfferDto.prototype, "promotionSnapshots", void 0);
479
- _ts_decorate2([
480
- IsOptional(),
481
- IsObject(),
482
- _ts_metadata2("design:type", Object)
483
- ], CreateCheckoutOfferDto.prototype, "promoCodeSnapshot", void 0);
484
- _ts_decorate2([
485
- IsOptional(),
486
- IsString(),
487
- MaxLength(8),
488
- _ts_metadata2("design:type", String)
489
- ], CreateCheckoutOfferDto.prototype, "locale", void 0);
490
- _ts_decorate2([
491
- IsOptional(),
492
- IsDateString(),
493
- _ts_metadata2("design:type", Object)
494
- ], CreateCheckoutOfferDto.prototype, "validUntil", void 0);
495
- var UpdateCheckoutOfferDto = class {
496
- static {
497
- __name(this, "UpdateCheckoutOfferDto");
498
- }
499
- billingCycle;
500
- promotionId;
501
- promoCode;
502
- bundles;
503
- bundleVersionIds;
504
- priceBreakdown;
505
- lineItems;
506
- promotionSnapshots;
507
- promoCodeSnapshot;
508
- locale;
509
- validUntil;
510
- };
511
- _ts_decorate2([
512
- IsOptional(),
513
- IsString(),
514
- IsIn(CYCLES),
515
- _ts_metadata2("design:type", String)
516
- ], UpdateCheckoutOfferDto.prototype, "billingCycle", void 0);
517
- _ts_decorate2([
518
- IsOptional(),
519
- IsString(),
520
- _ts_metadata2("design:type", Object)
521
- ], UpdateCheckoutOfferDto.prototype, "promotionId", void 0);
522
- _ts_decorate2([
523
- IsOptional(),
524
- IsString(),
525
- MaxLength(64),
526
- _ts_metadata2("design:type", Object)
527
- ], UpdateCheckoutOfferDto.prototype, "promoCode", void 0);
528
- _ts_decorate2([
529
- IsOptional(),
530
- IsArray(),
531
- IsString({
532
- each: true
533
- }),
534
- _ts_metadata2("design:type", Array)
535
- ], UpdateCheckoutOfferDto.prototype, "bundles", void 0);
536
- _ts_decorate2([
537
- IsOptional(),
538
- IsArray(),
539
- IsString({
540
- each: true
541
- }),
542
- _ts_metadata2("design:type", Array)
543
- ], UpdateCheckoutOfferDto.prototype, "bundleVersionIds", void 0);
544
- _ts_decorate2([
545
- IsOptional(),
546
- IsObject(),
547
- _ts_metadata2("design:type", typeof CheckoutOfferPriceBreakdown === "undefined" ? Object : CheckoutOfferPriceBreakdown)
548
- ], UpdateCheckoutOfferDto.prototype, "priceBreakdown", void 0);
549
- _ts_decorate2([
550
- IsOptional(),
551
- IsArray(),
552
- _ts_metadata2("design:type", Array)
553
- ], UpdateCheckoutOfferDto.prototype, "lineItems", void 0);
554
- _ts_decorate2([
555
- IsOptional(),
556
- IsArray(),
557
- _ts_metadata2("design:type", Array)
558
- ], UpdateCheckoutOfferDto.prototype, "promotionSnapshots", void 0);
559
- _ts_decorate2([
560
- IsOptional(),
561
- IsObject(),
562
- _ts_metadata2("design:type", Object)
563
- ], UpdateCheckoutOfferDto.prototype, "promoCodeSnapshot", void 0);
564
- _ts_decorate2([
565
- IsOptional(),
566
- IsString(),
567
- MaxLength(8),
568
- _ts_metadata2("design:type", String)
569
- ], UpdateCheckoutOfferDto.prototype, "locale", void 0);
570
- _ts_decorate2([
571
- IsOptional(),
572
- IsDateString(),
573
- _ts_metadata2("design:type", Object)
574
- ], UpdateCheckoutOfferDto.prototype, "validUntil", void 0);
575
-
576
- // src/checkout-offer/checkout-offer.controller.ts
577
- import { Body, Controller, Get, Inject as Inject2, Param, ParseUUIDPipe, Patch, Post, UseGuards } from "@nestjs/common";
578
- function _ts_decorate3(decorators, target, key, desc) {
579
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
580
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
581
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
582
- return c > 3 && r && Object.defineProperty(target, key, r), r;
583
- }
584
- __name(_ts_decorate3, "_ts_decorate");
585
- function _ts_metadata3(k, v) {
586
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
587
- }
588
- __name(_ts_metadata3, "_ts_metadata");
589
- function _ts_param2(paramIndex, decorator) {
590
- return function(target, key) {
591
- decorator(target, key, paramIndex);
592
- };
593
- }
594
- __name(_ts_param2, "_ts_param");
595
- function buildCheckoutOfferController(guards) {
596
- let GeneratedCheckoutOfferController = class GeneratedCheckoutOfferController {
597
- static {
598
- __name(this, "GeneratedCheckoutOfferController");
599
- }
600
- service;
601
- constructor(service) {
602
- this.service = service;
603
- }
604
- /** The website creates an offer when a package is clicked. */
605
- create(dto) {
606
- return this.service.create(dto);
607
- }
608
- /** Onboarding reads the pre-selected offer (`?offer=<id>`). */
609
- getById(id) {
610
- return this.service.getById(id);
611
- }
612
- /** Onboarding customization — as long as the offer is `open`. */
613
- update(id, dto) {
614
- return this.service.update(id, dto);
615
- }
616
- };
617
- _ts_decorate3([
618
- Post(),
619
- _ts_param2(0, Body()),
620
- _ts_metadata3("design:type", Function),
621
- _ts_metadata3("design:paramtypes", [
622
- typeof CreateCheckoutOfferDto === "undefined" ? Object : CreateCheckoutOfferDto
623
- ]),
624
- _ts_metadata3("design:returntype", void 0)
625
- ], GeneratedCheckoutOfferController.prototype, "create", null);
626
- _ts_decorate3([
627
- Get(":id"),
628
- _ts_param2(0, Param("id", new ParseUUIDPipe())),
629
- _ts_metadata3("design:type", Function),
630
- _ts_metadata3("design:paramtypes", [
631
- String
632
- ]),
633
- _ts_metadata3("design:returntype", void 0)
634
- ], GeneratedCheckoutOfferController.prototype, "getById", null);
635
- _ts_decorate3([
636
- Patch(":id"),
637
- _ts_param2(0, Param("id", new ParseUUIDPipe())),
638
- _ts_param2(1, Body()),
639
- _ts_metadata3("design:type", Function),
640
- _ts_metadata3("design:paramtypes", [
641
- String,
642
- typeof UpdateCheckoutOfferDto === "undefined" ? Object : UpdateCheckoutOfferDto
643
- ]),
644
- _ts_metadata3("design:returntype", void 0)
645
- ], GeneratedCheckoutOfferController.prototype, "update", null);
646
- GeneratedCheckoutOfferController = _ts_decorate3([
647
- Controller("public/checkout-offer"),
648
- SaaSiCatPublicRoute(),
649
- UseGuards(...guards),
650
- _ts_param2(0, Inject2(CheckoutOfferService)),
651
- _ts_metadata3("design:type", Function),
652
- _ts_metadata3("design:paramtypes", [
653
- typeof CheckoutOfferService === "undefined" ? Object : CheckoutOfferService
654
- ])
655
- ], GeneratedCheckoutOfferController);
656
- return GeneratedCheckoutOfferController;
657
- }
658
- __name(buildCheckoutOfferController, "buildCheckoutOfferController");
659
-
660
- // src/checkout-offer/checkout-offer.module.ts
661
- import { Module } from "@nestjs/common";
662
- function _ts_decorate4(decorators, target, key, desc) {
663
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
664
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
665
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
666
- return c > 3 && r && Object.defineProperty(target, key, r), r;
667
- }
668
- __name(_ts_decorate4, "_ts_decorate");
669
- var CheckoutOfferModule = class _CheckoutOfferModule {
670
- static {
671
- __name(this, "CheckoutOfferModule");
672
- }
673
- static forRoot(options) {
674
- const controllers = [];
675
- if (options.controller) {
676
- controllers.push(buildCheckoutOfferController(options.controller.guards));
677
- }
678
- return {
679
- module: _CheckoutOfferModule,
680
- global: options.global ?? false,
681
- imports: options.imports ?? [],
682
- controllers,
683
- providers: [
684
- asProvider(CHECKOUT_OFFER_REPOSITORY_TOKEN, options.checkoutOfferRepository),
685
- ...options.bundleRepository ? [
686
- asProvider(BUNDLE_REPOSITORY_TOKEN, options.bundleRepository)
687
- ] : [],
688
- ...options.planRepository ? [
689
- asProvider(PLAN_REPOSITORY_TOKEN, options.planRepository)
690
- ] : [],
691
- ...options.catalogEntryRepository ? [
692
- asProvider(CATALOG_ENTRY_REPOSITORY_TOKEN, options.catalogEntryRepository)
693
- ] : [],
694
- CheckoutOfferService,
695
- ...options.extraProviders ?? []
696
- ],
697
- exports: [
698
- CheckoutOfferService,
699
- CHECKOUT_OFFER_REPOSITORY_TOKEN
700
- ]
701
- };
702
- }
703
- };
704
- CheckoutOfferModule = _ts_decorate4([
705
- Module({})
706
- ], CheckoutOfferModule);
707
-
708
- export {
709
- CHECKOUT_OFFER_REPOSITORY_TOKEN,
710
- CheckoutOfferService,
711
- CreateCheckoutOfferDto,
712
- UpdateCheckoutOfferDto,
713
- buildCheckoutOfferController,
714
- CheckoutOfferModule
715
- };