@saasicat/nest 0.13.0 → 0.15.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.
Files changed (63) hide show
  1. package/dist/_entries.cjs +1529 -1345
  2. package/dist/admin/index.d.cts +8 -6
  3. package/dist/admin/index.d.ts +8 -6
  4. package/dist/admin/index.js +13 -12
  5. package/dist/{admin-resources.module-Cms8MY6h.d.ts → admin-resources.module-BY5r_1MG.d.ts} +29 -3
  6. package/dist/{admin-resources.module-BZN_yBFj.d.cts → admin-resources.module-DGgGSdzN.d.cts} +29 -3
  7. package/dist/{admin-stats.module-15ac5KY3.d.ts → admin-stats.service-Db2exT_d.d.ts} +2 -23
  8. package/dist/{admin-stats.module-v4SVvWzj.d.cts → admin-stats.service-HUZIU448.d.cts} +2 -23
  9. package/dist/{aggregation-CfI0bOsQ.d.cts → aggregation-SKfXu09x.d.cts} +35 -3
  10. package/dist/{aggregation-BibpEO0t.d.ts → aggregation-o0LMenvl.d.ts} +35 -3
  11. package/dist/billing/index.d.cts +2 -2
  12. package/dist/billing/index.d.ts +2 -2
  13. package/dist/billing/index.js +4 -3
  14. package/dist/catalog/index.js +3 -2
  15. package/dist/checkout-offer/index.d.cts +2 -1
  16. package/dist/checkout-offer/index.d.ts +2 -1
  17. package/dist/checkout-offer/index.js +2 -1
  18. package/dist/checkout-offer.module-Ca3_TieR.d.ts +39 -0
  19. package/dist/checkout-offer.module-DmlpPevf.d.cts +39 -0
  20. package/dist/{checkout-offer.module-Y0hDOVrq.d.ts → checkout-offer.service-BX51dljL.d.cts} +1 -37
  21. package/dist/{checkout-offer.module-ZYF5IsDY.d.cts → checkout-offer.service-BX51dljL.d.ts} +1 -37
  22. package/dist/{chunk-2PNX2QL2.js → chunk-32EMXPU2.js} +1 -0
  23. package/dist/{chunk-DVFPKVLO.js → chunk-5KH2O4SF.js} +16 -4
  24. package/dist/{chunk-TXDH4FUA.js → chunk-7RRWI5LH.js} +1 -1
  25. package/dist/chunk-EWPF57AG.js +13 -0
  26. package/dist/{chunk-2WNM5THV.js → chunk-EXOLOJFZ.js} +58 -15
  27. package/dist/{chunk-HBFU5AN3.js → chunk-GJBXOCRZ.js} +4 -0
  28. package/dist/{chunk-IRWZC3GC.js → chunk-GLLCB4BP.js} +4 -0
  29. package/dist/{chunk-33TP26MR.js → chunk-P3DP6GJE.js} +4 -0
  30. package/dist/{chunk-FVT7KTUI.js → chunk-PLJEAUFU.js} +275 -61
  31. package/dist/{chunk-J3BMIR4W.js → chunk-YITTE646.js} +463 -83
  32. package/dist/chunk-YSC46BGM.js +21 -0
  33. package/dist/define-saasicat-CeDBX9ji.d.cts +46 -0
  34. package/dist/define-saasicat-Rc0rXrYG.d.ts +46 -0
  35. package/dist/enforce-quota.interceptor-CmbWX39L.d.ts +60 -0
  36. package/dist/enforce-quota.interceptor-DMw-bPyk.d.cts +60 -0
  37. package/dist/entitlement/index.d.cts +1 -1
  38. package/dist/entitlement/index.d.ts +1 -1
  39. package/dist/entitlement/index.js +7 -1
  40. package/dist/index.d.cts +12 -10
  41. package/dist/index.d.ts +12 -10
  42. package/dist/index.js +40 -19
  43. package/dist/platform/index.d.cts +13 -17
  44. package/dist/platform/index.d.ts +13 -17
  45. package/dist/platform/index.js +25 -26
  46. package/dist/promo/index.js +2 -1
  47. package/dist/{enforce-quota.interceptor-Dse3jSpU.d.cts → saas-platform.module-D3rUTiho.d.cts} +69 -62
  48. package/dist/{enforce-quota.interceptor-DMEvL-bE.d.ts → saas-platform.module-DLhoqKNA.d.ts} +69 -62
  49. package/dist/subscription-contract/index.d.cts +1 -1
  50. package/dist/subscription-contract/index.d.ts +1 -1
  51. package/dist/subscription-contract/index.js +1 -1
  52. package/dist/{subscription-contract.module-CG9fPKAr.d.cts → subscription-contract.module-B2uNvUJL.d.cts} +3 -1
  53. package/dist/{subscription-contract.module-CVebZzHR.d.ts → subscription-contract.module-edyhBr4n.d.ts} +3 -1
  54. package/dist/{subscription-bundle-preview.service-DBbrE_cw.d.ts → tenant-billing.controller-BwZRnJ3q.d.ts} +106 -106
  55. package/dist/{subscription-bundle-preview.service-BkJXHAeP.d.cts → tenant-billing.controller-CG4CaFUP.d.cts} +106 -106
  56. package/dist/testing/index.d.cts +6 -3
  57. package/dist/testing/index.d.ts +6 -3
  58. package/dist/testing/index.js +9 -7
  59. package/package.json +3 -3
  60. package/dist/chunk-LZHLGUQI.js +0 -190
  61. package/dist/chunk-WJ6BMDL3.js +0 -313
  62. package/dist/setup.service-BPwJ7My_.d.cts +0 -45
  63. package/dist/setup.service-D2mrdDDB.d.ts +0 -45
@@ -1,6 +1,4 @@
1
1
  import { CheckoutOfferRepository, BundleRepository, PlanRepository, CatalogEntryRepository, CheckoutOfferFilter, CheckoutOfferRow, CreateCheckoutOfferData, UpdateCheckoutOfferData } from '@saasicat/types';
2
- import { Type, CanActivate, DynamicModule, ForwardReference, Provider } from '@nestjs/common';
3
- import { P as ProviderSpec } from './di-CcNeq9v-.js';
4
2
 
5
3
  declare class CheckoutOfferService {
6
4
  private readonly repo;
@@ -48,38 +46,4 @@ declare class CheckoutOfferService {
48
46
  private isValidUntilExpired;
49
47
  }
50
48
 
51
- interface CheckoutOfferControllerConfig {
52
- /** Class-level guards; `[]` for auth-free public endpoints. */
53
- guards: Array<Type<CanActivate>>;
54
- }
55
- interface CheckoutOfferModuleOptions {
56
- checkoutOfferRepository: ProviderSpec<CheckoutOfferRepository>;
57
- /**
58
- * Optional for V3 revalidation on consume: if set, the service checks
59
- * whether referenced bundle versions are still bookable. Also provides
60
- * the bundle features for the requires validation (#35 P6).
61
- */
62
- bundleRepository?: ProviderSpec<BundleRepository>;
63
- /**
64
- * Optional for the requires validation (#35 P6): plan features of the
65
- * chosen plan version. Without wiring, falls back to the featuresSnapshot
66
- * of the plan line item.
67
- */
68
- planRepository?: ProviderSpec<PlanRepository>;
69
- /**
70
- * Optional for the requires validation (#35 P6): requires source =
71
- * curated FeatureCatalogEntries. Without wiring, the validation is
72
- * skipped (graceful).
73
- */
74
- catalogEntryRepository?: ProviderSpec<CatalogEntryRepository>;
75
- /** Controller mount for `/public/checkout-offer`. Omitted = service only. */
76
- controller?: CheckoutOfferControllerConfig;
77
- imports?: Array<Type<unknown> | DynamicModule | Promise<DynamicModule> | ForwardReference>;
78
- extraProviders?: Provider[];
79
- global?: boolean;
80
- }
81
- declare class CheckoutOfferModule {
82
- static forRoot(options: CheckoutOfferModuleOptions): DynamicModule;
83
- }
84
-
85
- export { type CheckoutOfferControllerConfig as C, CheckoutOfferModule as a, type CheckoutOfferModuleOptions as b, CheckoutOfferService as c };
49
+ export { CheckoutOfferService as C };
@@ -1,6 +1,4 @@
1
1
  import { CheckoutOfferRepository, BundleRepository, PlanRepository, CatalogEntryRepository, CheckoutOfferFilter, CheckoutOfferRow, CreateCheckoutOfferData, UpdateCheckoutOfferData } from '@saasicat/types';
2
- import { Type, CanActivate, DynamicModule, ForwardReference, Provider } from '@nestjs/common';
3
- import { P as ProviderSpec } from './di-CcNeq9v-.cjs';
4
2
 
5
3
  declare class CheckoutOfferService {
6
4
  private readonly repo;
@@ -48,38 +46,4 @@ declare class CheckoutOfferService {
48
46
  private isValidUntilExpired;
49
47
  }
50
48
 
51
- interface CheckoutOfferControllerConfig {
52
- /** Class-level guards; `[]` for auth-free public endpoints. */
53
- guards: Array<Type<CanActivate>>;
54
- }
55
- interface CheckoutOfferModuleOptions {
56
- checkoutOfferRepository: ProviderSpec<CheckoutOfferRepository>;
57
- /**
58
- * Optional for V3 revalidation on consume: if set, the service checks
59
- * whether referenced bundle versions are still bookable. Also provides
60
- * the bundle features for the requires validation (#35 P6).
61
- */
62
- bundleRepository?: ProviderSpec<BundleRepository>;
63
- /**
64
- * Optional for the requires validation (#35 P6): plan features of the
65
- * chosen plan version. Without wiring, falls back to the featuresSnapshot
66
- * of the plan line item.
67
- */
68
- planRepository?: ProviderSpec<PlanRepository>;
69
- /**
70
- * Optional for the requires validation (#35 P6): requires source =
71
- * curated FeatureCatalogEntries. Without wiring, the validation is
72
- * skipped (graceful).
73
- */
74
- catalogEntryRepository?: ProviderSpec<CatalogEntryRepository>;
75
- /** Controller mount for `/public/checkout-offer`. Omitted = service only. */
76
- controller?: CheckoutOfferControllerConfig;
77
- imports?: Array<Type<unknown> | DynamicModule | Promise<DynamicModule> | ForwardReference>;
78
- extraProviders?: Provider[];
79
- global?: boolean;
80
- }
81
- declare class CheckoutOfferModule {
82
- static forRoot(options: CheckoutOfferModuleOptions): DynamicModule;
83
- }
84
-
85
- export { type CheckoutOfferControllerConfig as C, CheckoutOfferModule as a, type CheckoutOfferModuleOptions as b, CheckoutOfferService as c };
49
+ export { CheckoutOfferService as C };
@@ -322,6 +322,7 @@ var SubscriptionContractModule = class _SubscriptionContractModule {
322
322
  return {
323
323
  module: _SubscriptionContractModule,
324
324
  global: options.global ?? false,
325
+ imports: options.imports ?? [],
325
326
  providers: [
326
327
  asProvider(SUBSCRIPTION_CONTRACT_REPOSITORY_TOKEN, options.subscriptionContractRepository),
327
328
  SubscriptionContractService,
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-E7AJ42AB.js";
4
4
  import {
5
5
  SubscriptionContractService
6
- } from "./chunk-2PNX2QL2.js";
6
+ } from "./chunk-32EMXPU2.js";
7
7
  import {
8
8
  AUDIT_CONTEXT_RESOLVER_TOKEN,
9
9
  PENDING_PLAN_QUERY_PORT_TOKEN,
@@ -19,13 +19,16 @@ import {
19
19
  import {
20
20
  AdminAuditService
21
21
  } from "./chunk-E56W4U2P.js";
22
+ import {
23
+ SaaSiCatPublicRoute
24
+ } from "./chunk-YSC46BGM.js";
22
25
  import {
23
26
  EntitlementService,
24
27
  SUBSCRIPTION_BUNDLE_CONFIG_TOKEN,
25
28
  SUBSCRIPTION_BUNDLE_REPOSITORY_TOKEN,
26
29
  isLimitExceededError,
27
30
  toEffectiveLimitsSnapshot
28
- } from "./chunk-2WNM5THV.js";
31
+ } from "./chunk-EXOLOJFZ.js";
29
32
  import {
30
33
  PLAN_CATALOG_TOKEN,
31
34
  findPlan,
@@ -954,6 +957,7 @@ _ts_decorate7([
954
957
  ], PublicCatalogController.prototype, "listBundles", null);
955
958
  PublicCatalogController = _ts_decorate7([
956
959
  Controller2("billing"),
960
+ SaaSiCatPublicRoute(),
957
961
  _ts_param5(0, Inject5(PLAN_CATALOG_TOKEN)),
958
962
  _ts_param5(1, Inject5(FEATURE_UI_REGISTRY_TOKEN)),
959
963
  _ts_param5(2, Optional3()),
@@ -3005,7 +3009,7 @@ function _ts_param13(paramIndex, decorator) {
3005
3009
  }
3006
3010
  __name(_ts_param13, "_ts_param");
3007
3011
  function buildTenantSubscriptionBundlesController(extraGuards = []) {
3008
- let GeneratedTenantSubscriptionBundlesController = class GeneratedTenantSubscriptionBundlesController {
3012
+ let GeneratedTenantSubscriptionBundlesController = class GeneratedTenantSubscriptionBundlesController2 {
3009
3013
  static {
3010
3014
  __name(this, "GeneratedTenantSubscriptionBundlesController");
3011
3015
  }
@@ -3015,6 +3019,8 @@ function buildTenantSubscriptionBundlesController(extraGuards = []) {
3015
3019
  tenantIdResolver;
3016
3020
  contractFreeze;
3017
3021
  logger = new Logger5("TenantSubscriptionBundlesController");
3022
+ /** Keeps the "no contractFreeze hook" warning to one line per process. */
3023
+ static warnedAboutMissingFreeze = false;
3018
3024
  constructor(service, previewService, subscriptionUsage, tenantIdResolver, contractFreeze = null) {
3019
3025
  this.service = service;
3020
3026
  this.previewService = previewService;
@@ -3106,7 +3112,13 @@ function buildTenantSubscriptionBundlesController(extraGuards = []) {
3106
3112
  * only if the consumer has wired the ContractFreezePort.
3107
3113
  */
3108
3114
  async refreezeContract(tenantId, sub) {
3109
- if (!this.contractFreeze) return;
3115
+ if (!this.contractFreeze) {
3116
+ if (!GeneratedTenantSubscriptionBundlesController2.warnedAboutMissingFreeze) {
3117
+ GeneratedTenantSubscriptionBundlesController2.warnedAboutMissingFreeze = true;
3118
+ this.logger.warn("Bundle mutation without a configured contractFreeze hook \u2014 the SubscriptionContract is not re-materialized. Configure `contractFreeze` in TenantBillingModule.forRoot() if this app uses contracts.");
3119
+ }
3120
+ return;
3121
+ }
3110
3122
  try {
3111
3123
  await this.contractFreeze.freezeOnPlanChange(tenantId, sub.planVersion.planId, sub.billingCycle, /* @__PURE__ */ new Date());
3112
3124
  } catch (err) {
@@ -6,7 +6,7 @@ import {
6
6
  ADVISORY_STRICT_MODE_CODES,
7
7
  validateBundleDraft,
8
8
  validatePlanDraft
9
- } from "./chunk-33TP26MR.js";
9
+ } from "./chunk-P3DP6GJE.js";
10
10
  import {
11
11
  __name
12
12
  } from "./chunk-SHUYVCID.js";
@@ -0,0 +1,13 @@
1
+ import {
2
+ __name
3
+ } from "./chunk-SHUYVCID.js";
4
+
5
+ // src/platform/define-saasicat.ts
6
+ function defineSaaSiCat(options) {
7
+ return options;
8
+ }
9
+ __name(defineSaaSiCat, "defineSaaSiCat");
10
+
11
+ export {
12
+ defineSaaSiCat
13
+ };
@@ -76,6 +76,53 @@ function aggregateContractLineItemEntitlements(lineItems, fallbackPlan = "UNKNOW
76
76
  };
77
77
  }
78
78
  __name(aggregateContractLineItemEntitlements, "aggregateContractLineItemEntitlements");
79
+ function contractLimits(contract) {
80
+ if (contract.entitlementSnapshot) {
81
+ return {
82
+ plan: contract.entitlementSnapshot.plan,
83
+ quotas: {
84
+ ...contract.entitlementSnapshot.quotas
85
+ },
86
+ features: new Set(contract.entitlementSnapshot.features)
87
+ };
88
+ }
89
+ return aggregateContractLineItemEntitlements(contract.lineItems);
90
+ }
91
+ __name(contractLimits, "contractLimits");
92
+ function contractBundleVersionIds(contract) {
93
+ const ids = new Set(contract.originalBundleVersionIds);
94
+ for (const item of contract.lineItems) {
95
+ if (item.kind === "bundle" && item.sourceVersionId) {
96
+ ids.add(item.sourceVersionId);
97
+ }
98
+ }
99
+ return ids;
100
+ }
101
+ __name(contractBundleVersionIds, "contractBundleVersionIds");
102
+ function mergeSubscriptionBundlesIntoLimits(limits, bundles, coveredBundleVersionIds, catalog, now) {
103
+ const additional = filterActiveSubscriptionBundles(bundles, now).filter((b) => !coveredBundleVersionIds.has(b.bundleVersionId));
104
+ if (additional.length === 0) return limits;
105
+ const quotas = {
106
+ ...limits.quotas
107
+ };
108
+ for (const [key, value] of Object.entries(aggregateSubscriptionBundleQuotas(additional))) {
109
+ if (quotas[key] === -1 || value === -1) {
110
+ quotas[key] = -1;
111
+ } else {
112
+ quotas[key] = (quotas[key] ?? 0) + value;
113
+ }
114
+ }
115
+ const bundleFeatures = filterPlannedOnlyFeatures(new Set(collectSubscriptionBundleFeatures(additional)), catalog);
116
+ return {
117
+ plan: limits.plan,
118
+ quotas,
119
+ features: /* @__PURE__ */ new Set([
120
+ ...limits.features,
121
+ ...bundleFeatures
122
+ ])
123
+ };
124
+ }
125
+ __name(mergeSubscriptionBundlesIntoLimits, "mergeSubscriptionBundlesIntoLimits");
79
126
  function filterPlannedOnlyFeatures(features, catalog) {
80
127
  const out = /* @__PURE__ */ new Set();
81
128
  for (const f of features) {
@@ -348,8 +395,11 @@ var EntitlementService = class {
348
395
  * have a `SubscriptionRecord` (e.g. within a transaction).
349
396
  */
350
397
  async deriveLimits(sub, now, tx) {
351
- const contractLimits = await this.deriveLimitsFromContract(sub.tenantId, now, tx);
352
- if (contractLimits) return this.withReplacedFeatureAliases(contractLimits);
398
+ const contract = await this.findActiveContract(sub.tenantId, now, tx);
399
+ if (contract) {
400
+ const bundles = await this.loadSubscriptionBundleSnapshots(sub.id, now, tx);
401
+ return this.withReplacedFeatureAliases(mergeSubscriptionBundlesIntoLimits(contractLimits(contract), bundles, contractBundleVersionIds(contract), this.catalog, now));
402
+ }
353
403
  const effectivePlan = resolveEntitlementPlan(sub, this.resolutionConfig ?? {}, now);
354
404
  const planVersion = effectivePlan === sub.plan ? sub.planVersion : await this.findActivePlanVersionOrFallback(effectivePlan, now, tx);
355
405
  const subscriptionBundles = await this.loadSubscriptionBundleSnapshots(sub.id, now, tx);
@@ -376,20 +426,9 @@ var EntitlementService = class {
376
426
  features: expandReplacedFeatures(limits.features, this.replacedByIndex)
377
427
  };
378
428
  }
379
- async deriveLimitsFromContract(tenantId, now, tx) {
429
+ async findActiveContract(tenantId, now, tx) {
380
430
  if (!this.subscriptionContracts) return null;
381
- const contract = await this.subscriptionContracts.findActiveByTenantId(tenantId, now, tx);
382
- if (!contract) return null;
383
- if (contract.entitlementSnapshot) {
384
- return {
385
- plan: contract.entitlementSnapshot.plan,
386
- quotas: {
387
- ...contract.entitlementSnapshot.quotas
388
- },
389
- features: new Set(contract.entitlementSnapshot.features)
390
- };
391
- }
392
- return aggregateContractLineItemEntitlements(contract.lineItems);
431
+ return this.subscriptionContracts.findActiveByTenantId(tenantId, now, tx);
393
432
  }
394
433
  /**
395
434
  * Loads a subscription's active bundle bookings and resolves the
@@ -409,6 +448,7 @@ var EntitlementService = class {
409
448
  }
410
449
  return {
411
450
  bundleKey: bv.bundleKey,
451
+ bundleVersionId: booking.bundleVersionId,
412
452
  features: bv.features,
413
453
  quotas: bv.quotas,
414
454
  canceledEffectiveAt: booking.canceledEffectiveAt
@@ -575,6 +615,9 @@ export {
575
615
  aggregateSubscriptionBundleQuotas,
576
616
  collectSubscriptionBundleFeatures,
577
617
  aggregateContractLineItemEntitlements,
618
+ contractLimits,
619
+ contractBundleVersionIds,
620
+ mergeSubscriptionBundlesIntoLimits,
578
621
  filterPlannedOnlyFeatures,
579
622
  applyCustomLimits,
580
623
  aggregateLimits,
@@ -1,6 +1,9 @@
1
1
  import {
2
2
  appendImplicitDiscountLineItem
3
3
  } from "./chunk-MDIZUVIK.js";
4
+ import {
5
+ SaaSiCatPublicRoute
6
+ } from "./chunk-YSC46BGM.js";
4
7
  import {
5
8
  BUNDLE_REPOSITORY_TOKEN,
6
9
  CATALOG_ENTRY_REPOSITORY_TOKEN,
@@ -613,6 +616,7 @@ function buildCheckoutOfferController(guards) {
613
616
  ], GeneratedCheckoutOfferController.prototype, "update", null);
614
617
  GeneratedCheckoutOfferController = _ts_decorate3([
615
618
  Controller("public/checkout-offer"),
619
+ SaaSiCatPublicRoute(),
616
620
  UseGuards(...guards),
617
621
  _ts_param2(0, Inject2(CheckoutOfferService)),
618
622
  _ts_metadata3("design:type", Function),
@@ -12,6 +12,9 @@ import {
12
12
  import {
13
13
  AdminAuditService
14
14
  } from "./chunk-E56W4U2P.js";
15
+ import {
16
+ SaaSiCatPublicRoute
17
+ } from "./chunk-YSC46BGM.js";
15
18
  import {
16
19
  asProvider
17
20
  } from "./chunk-SABTXESR.js";
@@ -254,6 +257,7 @@ _ts_decorate4([
254
257
  ], PromoCodePublicController.prototype, "preview", null);
255
258
  PromoCodePublicController = _ts_decorate4([
256
259
  Controller("billing/promo"),
260
+ SaaSiCatPublicRoute(),
257
261
  _ts_param2(0, Inject2(PromoCodesService)),
258
262
  _ts_metadata3("design:type", Function),
259
263
  _ts_metadata3("design:paramtypes", [
@@ -8,6 +8,9 @@ import {
8
8
  import {
9
9
  AdminAuditService
10
10
  } from "./chunk-E56W4U2P.js";
11
+ import {
12
+ SaaSiCatPublicRoute
13
+ } from "./chunk-YSC46BGM.js";
11
14
  import {
12
15
  DiscoveryScanner
13
16
  } from "./chunk-Z3FWN3HJ.js";
@@ -2607,6 +2610,7 @@ function buildPublicMarketingCatalogController(guards, projectKey, currency, vat
2607
2610
  ], GeneratedPublicMarketingCatalogController.prototype, "getCatalog", null);
2608
2611
  GeneratedPublicMarketingCatalogController = _ts_decorate14([
2609
2612
  Controller4("public/marketing-catalog"),
2613
+ SaaSiCatPublicRoute(),
2610
2614
  UseGuards4(...guards),
2611
2615
  _ts_param11(0, Inject11(PublicMarketingCatalogService)),
2612
2616
  _ts_metadata14("design:type", Function),