@saasicat/nest 0.8.0 → 0.10.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 (95) hide show
  1. package/dist/_entries.cjs +17678 -0
  2. package/dist/admin/index.cjs +4 -1274
  3. package/dist/admin/index.d.cts +10 -171
  4. package/dist/admin/index.d.ts +10 -171
  5. package/dist/admin/index.js +13 -4
  6. package/dist/admin-resources.module-BZN_yBFj.d.cts +40 -0
  7. package/dist/admin-resources.module-Cms8MY6h.d.ts +40 -0
  8. package/dist/admin-stats.module-15ac5KY3.d.ts +201 -0
  9. package/dist/admin-stats.module-v4SVvWzj.d.cts +201 -0
  10. package/dist/billing/index.cjs +4 -5128
  11. package/dist/billing/index.d.cts +8 -719
  12. package/dist/billing/index.d.ts +8 -719
  13. package/dist/billing/index.js +7 -10
  14. package/dist/catalog/index.cjs +4 -5624
  15. package/dist/catalog/index.d.cts +7 -427
  16. package/dist/catalog/index.d.ts +7 -427
  17. package/dist/catalog/index.js +14 -12
  18. package/dist/catalog.module-BKObceo0.d.ts +103 -0
  19. package/dist/catalog.module-Dw2Hrbyu.d.cts +103 -0
  20. package/dist/checkout-offer/index.cjs +4 -807
  21. package/dist/checkout-offer/index.d.cts +5 -84
  22. package/dist/checkout-offer/index.d.ts +5 -84
  23. package/dist/checkout-offer/index.js +3 -2
  24. package/dist/checkout-offer.module-Y0hDOVrq.d.ts +85 -0
  25. package/dist/checkout-offer.module-ZYF5IsDY.d.cts +85 -0
  26. package/dist/{chunk-5DDCSGZC.js → chunk-33TP26MR.js} +144 -356
  27. package/dist/{chunk-CK54YKVK.js → chunk-75JABGZK.js} +16 -16
  28. package/dist/{chunk-HRU3CP57.js → chunk-BOADBIGB.js} +1 -1
  29. package/dist/{chunk-ZLUGQE5D.js → chunk-E7AJ42AB.js} +8 -8
  30. package/dist/{chunk-6LP7CDS2.js → chunk-F3VV6EXG.js} +18 -13
  31. package/dist/{chunk-PLDHI6NA.js → chunk-FVT7KTUI.js} +258 -3
  32. package/dist/{chunk-RZU4HIAN.js → chunk-GBDUH6DO.js} +26 -23
  33. package/dist/{chunk-K3OPNVFR.js → chunk-HBFU5AN3.js} +5 -9
  34. package/dist/chunk-HWGIMNWR.js +0 -0
  35. package/dist/chunk-IRWZC3GC.js +755 -0
  36. package/dist/chunk-L4KTQ6CL.js +20 -0
  37. package/dist/{chunk-T5SXXAKG.js → chunk-LZHLGUQI.js} +1 -1
  38. package/dist/{chunk-YHHE4KDA.js → chunk-PUXLPUVH.js} +351 -21
  39. package/dist/chunk-TXDH4FUA.js +211 -0
  40. package/dist/chunk-UF53DWWR.js +0 -0
  41. package/dist/{chunk-AJ3EPELS.js → chunk-V34AA7C7.js} +1 -1
  42. package/dist/chunk-WJ6BMDL3.js +313 -0
  43. package/dist/{chunk-M6CZQYPY.js → chunk-Z3FWN3HJ.js} +2 -2
  44. package/dist/discovery/index.cjs +4 -588
  45. package/dist/discovery/index.js +3 -3
  46. package/dist/{enforce-quota.interceptor-cv7r80VM.d.cts → enforce-quota.interceptor-BhkX5nq2.d.cts} +127 -12
  47. package/dist/{enforce-quota.interceptor-Dj1Kusr5.d.ts → enforce-quota.interceptor-CcqZc0Yu.d.ts} +127 -12
  48. package/dist/entitlement/index.cjs +4 -780
  49. package/dist/entitlement/index.d.cts +3 -1
  50. package/dist/entitlement/index.d.ts +3 -1
  51. package/dist/entitlement/index.js +2 -3
  52. package/dist/index.cjs +4 -14975
  53. package/dist/index.d.cts +22 -54
  54. package/dist/index.d.ts +22 -54
  55. package/dist/index.js +88 -369
  56. package/dist/module-CSN8j8Tp.d.cts +39 -0
  57. package/dist/module-DgYJur6f.d.ts +39 -0
  58. package/dist/plan-versions.service-BB36Q1bg.d.ts +329 -0
  59. package/dist/plan-versions.service-CUR2Whyr.d.cts +329 -0
  60. package/dist/platform/index.cjs +4 -2779
  61. package/dist/platform/index.d.cts +50 -8
  62. package/dist/platform/index.d.ts +50 -8
  63. package/dist/platform/index.js +116 -12
  64. package/dist/promo/index.cjs +4 -995
  65. package/dist/promo/index.d.cts +48 -46
  66. package/dist/promo/index.d.ts +48 -46
  67. package/dist/promo/index.js +10 -2
  68. package/dist/rate-limit.guard-dntqAlfZ.d.cts +19 -0
  69. package/dist/rate-limit.guard-dntqAlfZ.d.ts +19 -0
  70. package/dist/registration/index.cjs +4 -1655
  71. package/dist/registration/index.js +1 -1
  72. package/dist/setup.service-BPwJ7My_.d.cts +45 -0
  73. package/dist/setup.service-D2mrdDDB.d.ts +45 -0
  74. package/dist/subscription-bundle-preview.service-BkJXHAeP.d.cts +438 -0
  75. package/dist/subscription-bundle-preview.service-DBbrE_cw.d.ts +438 -0
  76. package/dist/subscription-bundles.module-BQmoozm5.d.ts +291 -0
  77. package/dist/subscription-bundles.module-DCpV-Pb5.d.cts +291 -0
  78. package/dist/subscription-contract/index.cjs +4 -471
  79. package/dist/subscription-contract/index.d.cts +5 -13
  80. package/dist/subscription-contract/index.d.ts +5 -13
  81. package/dist/subscription-contract.module-CG9fPKAr.d.cts +14 -0
  82. package/dist/subscription-contract.module-CVebZzHR.d.ts +14 -0
  83. package/dist/testing/index.cjs +4 -3756
  84. package/dist/testing/index.d.cts +9 -4
  85. package/dist/testing/index.d.ts +9 -4
  86. package/dist/testing/index.js +13 -7
  87. package/package.json +5 -5
  88. package/dist/admin-manifest.config-DyrQNT7M.d.cts +0 -10
  89. package/dist/admin-manifest.config-DyrQNT7M.d.ts +0 -10
  90. package/dist/admin-manifest.service-Cp6uwvmt.d.cts +0 -33
  91. package/dist/admin-manifest.service-UGgzR1x-.d.ts +0 -33
  92. package/dist/chunk-52UTQRD3.js +0 -326
  93. package/dist/chunk-N3WANKSA.js +0 -20
  94. package/dist/chunk-Q53N43LQ.js +0 -13
  95. /package/dist/{chunk-2FR6ZL7R.js → chunk-DBZGV3HC.js} +0 -0
@@ -0,0 +1,20 @@
1
+ // src/catalog/tokens.ts
2
+ var PLAN_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/PlanRepository");
3
+ var BUNDLE_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/BundleRepository");
4
+ var MARKETING_PROJECTION_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/MarketingProjectionRepository");
5
+ var CATALOG_ENTRY_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/CatalogEntryRepository");
6
+ var PROMOTION_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/PromotionRepository");
7
+ var MARKETING_SETTINGS_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/MarketingSettingsRepository");
8
+ var CATALOG_SERVICE_CONFIG_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/CatalogServiceConfig");
9
+ var FEATURE_UI_REGISTRY_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/CatalogFeatureUiRegistry");
10
+
11
+ export {
12
+ PLAN_REPOSITORY_TOKEN,
13
+ BUNDLE_REPOSITORY_TOKEN,
14
+ MARKETING_PROJECTION_REPOSITORY_TOKEN,
15
+ CATALOG_ENTRY_REPOSITORY_TOKEN,
16
+ PROMOTION_REPOSITORY_TOKEN,
17
+ MARKETING_SETTINGS_REPOSITORY_TOKEN,
18
+ CATALOG_SERVICE_CONFIG_TOKEN,
19
+ FEATURE_UI_REGISTRY_TOKEN
20
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SuperAdminGuard
3
- } from "./chunk-PLDHI6NA.js";
3
+ } from "./chunk-FVT7KTUI.js";
4
4
  import {
5
5
  asProvider
6
6
  } from "./chunk-SABTXESR.js";
@@ -1,17 +1,29 @@
1
+ import {
2
+ PromoCodesModule
3
+ } from "./chunk-IRWZC3GC.js";
1
4
  import {
2
5
  AdminManifestModule,
3
- AdminModule
4
- } from "./chunk-PLDHI6NA.js";
6
+ AdminManifestService,
7
+ AdminModule,
8
+ AdminResourcesModule,
9
+ SuperAdminGuard
10
+ } from "./chunk-FVT7KTUI.js";
11
+ import {
12
+ PublicCatalogModule,
13
+ REQUIRE_FEATURE_KEY,
14
+ SubscriptionBundleModule,
15
+ TenantBillingModule
16
+ } from "./chunk-F3VV6EXG.js";
5
17
  import {
6
- REQUIRE_FEATURE_KEY
7
- } from "./chunk-Q53N43LQ.js";
18
+ CatalogModule
19
+ } from "./chunk-33TP26MR.js";
8
20
  import {
9
21
  DiscoveryModule
10
- } from "./chunk-AJ3EPELS.js";
22
+ } from "./chunk-V34AA7C7.js";
11
23
  import {
12
24
  EntitlementModule,
13
25
  LimitExceededError
14
- } from "./chunk-RZU4HIAN.js";
26
+ } from "./chunk-GBDUH6DO.js";
15
27
  import {
16
28
  PLAN_CATALOG_TOKEN,
17
29
  PlanCatalogModule
@@ -269,6 +281,46 @@ EnforceQuotaInterceptor = _ts_decorate3([
269
281
  ])
270
282
  ], EnforceQuotaInterceptor);
271
283
 
284
+ // src/platform/quota-providers-usage-snapshot.ts
285
+ var QuotaProvidersUsageSnapshot = class {
286
+ static {
287
+ __name(this, "QuotaProvidersUsageSnapshot");
288
+ }
289
+ providers;
290
+ constructor(providers) {
291
+ this.providers = providers;
292
+ }
293
+ async snapshot(tenantId) {
294
+ const entries = await Promise.all(this.providers.map(async (provider) => [
295
+ provider.key,
296
+ await provider.count(tenantId)
297
+ ]));
298
+ return Object.fromEntries(entries);
299
+ }
300
+ };
301
+
302
+ // src/platform/subscription-plan-resolver.ts
303
+ var DEFAULT_ACTIVE_STATUSES = /* @__PURE__ */ new Set([
304
+ "ACTIVE",
305
+ "TRIAL"
306
+ ]);
307
+ var SubscriptionPlanResolver = class {
308
+ static {
309
+ __name(this, "SubscriptionPlanResolver");
310
+ }
311
+ subscriptions;
312
+ activeStatuses;
313
+ constructor(subscriptions, activeStatuses = DEFAULT_ACTIVE_STATUSES) {
314
+ this.subscriptions = subscriptions;
315
+ this.activeStatuses = activeStatuses;
316
+ }
317
+ async getPlanIdForTenant(tenantId) {
318
+ const subscription = await this.subscriptions.findByTenantId(tenantId);
319
+ if (!subscription || !this.activeStatuses.has(subscription.status)) return null;
320
+ return subscription.plan;
321
+ }
322
+ };
323
+
272
324
  // src/platform/tenant-manifest.service.ts
273
325
  import { Injectable as Injectable4 } from "@nestjs/common";
274
326
  function _ts_decorate4(decorators, target, key, desc) {
@@ -413,6 +465,75 @@ function _ts_decorate6(decorators, target, key, desc) {
413
465
  return c > 3 && r && Object.defineProperty(target, key, r), r;
414
466
  }
415
467
  __name(_ts_decorate6, "_ts_decorate");
468
+ var PLATFORM_SUBSCRIPTION_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform-nest/PlatformSubscriptionRepository");
469
+ var STANDARD_MANIFEST_REGISTRATION_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform-nest/StandardManifestRegistration");
470
+ function buildStandardManifestContribution(catalog, adminResources, promoCodes) {
471
+ const capabilities = {
472
+ "discovery.read": true
473
+ };
474
+ if (catalog && catalog.adminControllers !== false) {
475
+ Object.assign(capabilities, {
476
+ "plans.read": true,
477
+ "plans.publish": true,
478
+ "bundles.read": true,
479
+ "bundles.write": true,
480
+ "bundles.publish": true,
481
+ "marketingProjections.read": true,
482
+ "marketingProjections.write": true
483
+ });
484
+ }
485
+ const navigation = {};
486
+ if (adminResources) {
487
+ Object.assign(capabilities, {
488
+ "tenants.read": true,
489
+ "tenants.suspend": true,
490
+ "tenants.reactivate": true,
491
+ "users.read": true,
492
+ "audit.read": true,
493
+ "subscriptions.read": true
494
+ });
495
+ navigation.standardPages = {
496
+ subscriptions: {
497
+ enabled: true,
498
+ requiredCapability: "subscriptions.read"
499
+ }
500
+ };
501
+ }
502
+ if (promoCodes) {
503
+ Object.assign(capabilities, {
504
+ "promoCodes.read": true,
505
+ "promoCodes.write": true,
506
+ "promoCodes.delete": true
507
+ });
508
+ navigation.standardPages = {
509
+ ...navigation.standardPages ?? {},
510
+ promoCodes: {
511
+ enabled: true,
512
+ requiredCapability: "promoCodes.read"
513
+ }
514
+ };
515
+ }
516
+ return {
517
+ capabilities,
518
+ navigation: navigation.standardPages ? navigation : void 0
519
+ };
520
+ }
521
+ __name(buildStandardManifestContribution, "buildStandardManifestContribution");
522
+ function normalizeTenantAuthGuards(guards) {
523
+ if (!Array.isArray(guards)) return guards;
524
+ return {
525
+ useFactory: /* @__PURE__ */ __name((...instances) => instances, "useFactory"),
526
+ inject: guards
527
+ };
528
+ }
529
+ __name(normalizeTenantAuthGuards, "normalizeTenantAuthGuards");
530
+ function quotaUsageSnapshotProvider(quotaProviders) {
531
+ return {
532
+ useFactory: /* @__PURE__ */ __name((...providers) => new QuotaProvidersUsageSnapshot(providers), "useFactory"),
533
+ inject: quotaProviders
534
+ };
535
+ }
536
+ __name(quotaUsageSnapshotProvider, "quotaUsageSnapshotProvider");
416
537
  function buildMinimalManifestConfig() {
417
538
  return {
418
539
  useFactory: /* @__PURE__ */ __name((catalog) => ({
@@ -452,6 +573,12 @@ var SaasPlatformModule = class _SaasPlatformModule {
452
573
  static forRoot(options) {
453
574
  const explicit = options.adapters ?? {};
454
575
  const persistence = options.persistence;
576
+ const catalogConfig = options.catalog || null;
577
+ const tenantBillingConfig = options.tenantBilling || null;
578
+ const subscriptionBundlesConfig = options.subscriptionBundles || null;
579
+ const adminResourcesConfig = options.adminResources || null;
580
+ const promoCodesConfig = options.promoCodes || null;
581
+ const requiresFullEntitlement = Boolean(options.entitlement || tenantBillingConfig || subscriptionBundlesConfig);
455
582
  const adapters = {
456
583
  mfa: explicit.mfa ?? persistence?.core.mfa,
457
584
  audit: explicit.audit ?? persistence?.core.audit,
@@ -476,13 +603,52 @@ var SaasPlatformModule = class _SaasPlatformModule {
476
603
  if (!options.planCatalog && (!adapters.planCatalogReadSink || !options.dbCatalog)) {
477
604
  throw new Error("SaasPlatformModule.forRoot: either set `planCatalog` (quickstart YAML path) or, for DB hydration, BOTH a planCatalogReadSink (`adapters`/`persistence`) AND `dbCatalog` ({ projectKey, currency, vatRate }).");
478
605
  }
479
- if (options.entitlement) {
606
+ if (catalogConfig && !persistence?.catalog) {
607
+ throw new Error("SaasPlatformModule.forRoot: catalog is enabled, but `persistence.catalog` is missing. Use a complete persistence bundle or wire CatalogModule directly.");
608
+ }
609
+ if (adminResourcesConfig && adminResourcesConfig !== true && !adminResourcesConfig.resources && !persistence?.adminResources) {
610
+ throw new Error("SaasPlatformModule.forRoot: adminResources is enabled, but `persistence.adminResources` is missing.");
611
+ }
612
+ if (promoCodesConfig && (!persistence?.promo || !adapters.transactionRunner)) {
613
+ throw new Error("SaasPlatformModule.forRoot: promoCodes requires `persistence.promo` and a transactionRunner.");
614
+ }
615
+ if (promoCodesConfig && promoCodesConfig !== true && promoCodesConfig.includePublicController !== false && !promoCodesConfig.firstTimeCustomerCheck) {
616
+ throw new Error("SaasPlatformModule.forRoot: public promo preview requires `firstTimeCustomerCheck`.");
617
+ }
618
+ if (adminResourcesConfig === true && !persistence?.adminResources) {
619
+ throw new Error("SaasPlatformModule.forRoot: adminResources is enabled, but `persistence.adminResources` is missing.");
620
+ }
621
+ if (tenantBillingConfig && !persistence?.tenantBilling) {
622
+ const missing = [
623
+ !tenantBillingConfig.subscriptionUsagePort && "subscriptionUsagePort",
624
+ !tenantBillingConfig.subscriptionWritePort && "subscriptionWritePort"
625
+ ].filter(Boolean);
626
+ if (missing.length) {
627
+ throw new Error(`SaasPlatformModule.forRoot: tenantBilling is enabled, but adapters are missing: ${missing.join(", ")}`);
628
+ }
629
+ }
630
+ if (subscriptionBundlesConfig && !tenantBillingConfig) {
631
+ throw new Error("SaasPlatformModule.forRoot: subscriptionBundles requires tenantBilling so auth and subscription usage can be shared.");
632
+ }
633
+ if (subscriptionBundlesConfig) {
634
+ const missing = [];
635
+ if (!persistence?.entitlement?.subscriptionBundleRepository) {
636
+ missing.push("subscriptionBundleRepository");
637
+ }
638
+ if (!persistence?.catalog?.bundleRepository) {
639
+ missing.push("bundleRepository");
640
+ }
641
+ if (missing.length) {
642
+ throw new Error(`SaasPlatformModule.forRoot: subscriptionBundles is enabled, but persistence adapters are missing: ${missing.join(", ")}`);
643
+ }
644
+ }
645
+ if (requiresFullEntitlement) {
480
646
  const missing = [];
481
647
  if (!adapters.subscriptionRepository) missing.push("subscriptionRepository");
482
648
  if (!adapters.planVersionRepository) missing.push("planVersionRepository");
483
649
  if (!adapters.transactionRunner) missing.push("transactionRunner");
484
650
  if (missing.length) {
485
- throw new Error(`SaasPlatformModule.forRoot: entitlement active, but adapters are missing: ${missing.join(", ")}`);
651
+ throw new Error(`SaasPlatformModule.forRoot: entitlement active or required by the standard stack, but adapters are missing: ${missing.join(", ")}`);
486
652
  }
487
653
  if (persistence) {
488
654
  assertPersistenceCapabilities(persistence.capabilities, {
@@ -508,6 +674,9 @@ var SaasPlatformModule = class _SaasPlatformModule {
508
674
  version: options.planCatalog?.app?.version ?? options.dbCatalog?.app?.version ?? "0.0.0"
509
675
  };
510
676
  const imports = [
677
+ // Make injected client/auth providers visible to the high-level
678
+ // module's own quota providers and automatic plan resolver too.
679
+ ...options.imports ?? [],
511
680
  planCatalogModule,
512
681
  DiscoveryModule.forRoot({
513
682
  app: appInfo,
@@ -521,12 +690,15 @@ var SaasPlatformModule = class _SaasPlatformModule {
521
690
  mfaPort,
522
691
  auditPort,
523
692
  rlsBypassPort,
693
+ imports: options.imports,
524
694
  global: true
525
695
  }),
526
696
  AdminManifestModule.forRoot({
527
697
  config: options.adminManifestConfig ?? buildMinimalManifestConfig(),
698
+ includeManifestController: options.includeManifestController,
528
699
  guards: options.controller.guards,
529
700
  reloadGuards: options.reloadGuards,
701
+ imports: options.imports,
530
702
  // Global like AdminModule above: apps register their manifest
531
703
  // contribution by injecting AdminManifestService into one of
532
704
  // their own modules (handbook §6.6). Re-exporting the module
@@ -534,33 +706,172 @@ var SaasPlatformModule = class _SaasPlatformModule {
534
706
  global: true
535
707
  })
536
708
  ];
537
- if (options.entitlement) {
709
+ if (requiresFullEntitlement) {
538
710
  imports.push(EntitlementModule.forRoot({
539
711
  subscriptionRepository: adapters.subscriptionRepository,
540
712
  planVersionRepository: adapters.planVersionRepository,
541
713
  transactionRunner: adapters.transactionRunner,
542
- resolutionConfig: options.entitlement.resolutionConfig,
714
+ resolutionConfig: options.entitlement ? options.entitlement.resolutionConfig : void 0,
543
715
  subscriptionContractRepository: persistence?.entitlement?.subscriptionContractRepository,
544
716
  subscriptionBundleRepository: persistence?.entitlement?.subscriptionBundleRepository,
545
- bundleRepository: persistence?.entitlement?.bundleRepository
717
+ bundleRepository: persistence?.entitlement?.bundleRepository,
718
+ imports: options.imports,
719
+ global: Boolean(tenantBillingConfig || subscriptionBundlesConfig)
720
+ }));
721
+ }
722
+ if (catalogConfig) {
723
+ const catalog = persistence?.catalog;
724
+ imports.push(CatalogModule.forRoot({
725
+ planRepository: catalog.planRepository,
726
+ bundleRepository: catalog.bundleRepository,
727
+ catalogEntryRepository: catalog.catalogEntryRepository,
728
+ marketingProjectionRepository: catalog.marketingProjectionRepository,
729
+ promotionRepository: catalog.promotionRepository,
730
+ marketingSettingsRepository: catalog.marketingSettingsRepository,
731
+ controller: catalogConfig.adminControllers === false ? void 0 : {
732
+ guards: options.controller.guards
733
+ },
734
+ imports: catalogConfig.imports ?? options.imports,
735
+ extraProviders: catalogConfig.extraProviders,
736
+ strictModeCheckMode: catalogConfig.strictModeCheckMode,
737
+ autoSyncDiscoveryAtBoot: catalogConfig.autoSyncDiscoveryAtBoot,
738
+ marketedOnlyFeatures: catalogConfig.marketedOnlyFeatures,
739
+ featureUiRegistry: catalogConfig.featureUiRegistry,
740
+ publicMarketingCatalog: catalogConfig.publicMarketingCatalog
741
+ }));
742
+ if (catalogConfig.publicCatalog !== false) {
743
+ imports.push(PublicCatalogModule.forRoot({
744
+ featureUiRegistry: catalogConfig.featureUiRegistry,
745
+ projectKey: appInfo.key,
746
+ bundleRepository: catalog.bundleRepository,
747
+ marketingRepository: catalog.marketingProjectionRepository,
748
+ catalogEntryRepository: catalog.catalogEntryRepository,
749
+ imports: catalogConfig.imports ?? options.imports
750
+ }));
751
+ }
752
+ }
753
+ if (adminResourcesConfig) {
754
+ const config = adminResourcesConfig === true ? {} : adminResourcesConfig;
755
+ imports.push(AdminResourcesModule.forRoot({
756
+ resources: config.resources ?? persistence?.adminResources?.resources,
757
+ guards: config.guards ?? [
758
+ ...options.controller.guards,
759
+ SuperAdminGuard
760
+ ],
761
+ imports: config.imports ?? options.imports
762
+ }));
763
+ }
764
+ if (promoCodesConfig) {
765
+ const config = promoCodesConfig === true ? {} : promoCodesConfig;
766
+ const promo = persistence?.promo;
767
+ const { firstTimeCustomerCheck, adminGuards, imports: promoImports, includePublicController, adminController, ...promoOptions } = config;
768
+ imports.push(PromoCodesModule.forRoot({
769
+ ...promoOptions,
770
+ promoCodeRepository: promo.promoCodeRepository,
771
+ redemptionRepository: promo.redemptionRepository,
772
+ validationLogRepository: promo.validationLogRepository,
773
+ subscriptionLookup: promo.subscriptionLookup,
774
+ revenueAggregator: promo.revenueAggregator,
775
+ transactionRunner: adapters.transactionRunner,
776
+ firstTimeCustomerCheck: firstTimeCustomerCheck ?? {
777
+ hasExistingCustomerForEmail: /* @__PURE__ */ __name(async () => false, "hasExistingCustomerForEmail")
778
+ },
779
+ includePublicController: includePublicController ?? false,
780
+ adminController: adminController === false ? false : {
781
+ guards: adminGuards ?? [
782
+ ...options.controller.guards,
783
+ SuperAdminGuard
784
+ ]
785
+ },
786
+ imports: promoImports ?? options.imports
787
+ }));
788
+ }
789
+ let sharedTenantAuthGuards;
790
+ let sharedSubscriptionUsagePort;
791
+ let quotaProvidersHostedByTenantBilling = false;
792
+ if (tenantBillingConfig) {
793
+ const tenantSlice = persistence?.tenantBilling;
794
+ const { authGuards, subscriptionUsagePort, usageSnapshotPort, subscriptionWritePort, imports: tenantImports, extraProviders, extraExports, ...tenantOptions } = tenantBillingConfig;
795
+ const quotaProviders = options.quotaProviders ?? [];
796
+ quotaProvidersHostedByTenantBilling = quotaProviders.length > 0;
797
+ sharedTenantAuthGuards = normalizeTenantAuthGuards(authGuards);
798
+ sharedSubscriptionUsagePort = subscriptionUsagePort ?? tenantSlice?.subscriptionUsagePort;
799
+ const resolvedUsageSnapshotPort = usageSnapshotPort ?? tenantSlice?.usageSnapshotPort ?? quotaUsageSnapshotProvider(options.quotaProviders ?? []);
800
+ imports.push(TenantBillingModule.forRoot({
801
+ ...tenantOptions,
802
+ authGuards: sharedTenantAuthGuards,
803
+ subscriptionUsagePort: sharedSubscriptionUsagePort,
804
+ usageSnapshotPort: resolvedUsageSnapshotPort,
805
+ subscriptionWritePort: subscriptionWritePort ?? tenantSlice?.subscriptionWritePort,
806
+ imports: tenantImports ?? options.imports,
807
+ extraProviders: [
808
+ ...quotaProviders,
809
+ ...extraProviders ?? []
810
+ ],
811
+ extraExports: [
812
+ ...quotaProviders,
813
+ ...extraExports ?? []
814
+ ]
815
+ }));
816
+ }
817
+ if (subscriptionBundlesConfig) {
818
+ const config = subscriptionBundlesConfig === true ? {} : subscriptionBundlesConfig;
819
+ const { controller, imports: bundleImports, extraProviders, ...bundleOptions } = config;
820
+ imports.push(SubscriptionBundleModule.forRoot({
821
+ ...bundleOptions,
822
+ subscriptionBundleRepository: persistence?.entitlement?.subscriptionBundleRepository,
823
+ bundleRepository: persistence?.catalog?.bundleRepository,
824
+ controller: controller === false ? void 0 : {
825
+ ...controller ?? {},
826
+ authGuards: sharedTenantAuthGuards,
827
+ subscriptionUsagePort: sharedSubscriptionUsagePort
828
+ },
829
+ imports: bundleImports ?? tenantBillingConfig?.imports ?? options.imports,
830
+ extraProviders
546
831
  }));
547
832
  }
548
833
  const lightweightProviders = [];
549
834
  const lightweightExports = [];
550
- const hasResolver = !!adapters.planResolver;
835
+ if (options.autoManifest !== false) {
836
+ const contribution = buildStandardManifestContribution(catalogConfig, adminResourcesConfig, promoCodesConfig);
837
+ lightweightProviders.push({
838
+ provide: STANDARD_MANIFEST_REGISTRATION_TOKEN,
839
+ useFactory: /* @__PURE__ */ __name((manifest) => {
840
+ manifest.register(contribution);
841
+ return contribution;
842
+ }, "useFactory"),
843
+ inject: [
844
+ AdminManifestService
845
+ ]
846
+ });
847
+ }
848
+ const hasExplicitResolver = !!adapters.planResolver;
849
+ const hasSubscriptionResolver = Boolean(tenantBillingConfig && adapters.subscriptionRepository);
850
+ const hasResolver = hasExplicitResolver || hasSubscriptionResolver;
551
851
  const hasFallback = !!options.defaultPlanId;
552
852
  if (hasResolver || hasFallback) {
553
- lightweightProviders.push(hasResolver ? asProvider(PLAN_RESOLVER_PORT_TOKEN, adapters.planResolver) : {
853
+ if (hasSubscriptionResolver && !hasExplicitResolver) {
854
+ lightweightProviders.push(asProvider(PLATFORM_SUBSCRIPTION_REPOSITORY_TOKEN, adapters.subscriptionRepository));
855
+ }
856
+ lightweightProviders.push(hasExplicitResolver ? asProvider(PLAN_RESOLVER_PORT_TOKEN, adapters.planResolver) : hasSubscriptionResolver ? {
857
+ provide: PLAN_RESOLVER_PORT_TOKEN,
858
+ useFactory: /* @__PURE__ */ __name((subscriptions) => new SubscriptionPlanResolver(subscriptions), "useFactory"),
859
+ inject: [
860
+ PLATFORM_SUBSCRIPTION_REPOSITORY_TOKEN
861
+ ]
862
+ } : {
554
863
  provide: PLAN_RESOLVER_PORT_TOKEN,
555
864
  useValue: new StaticPlanResolver(options.defaultPlanId)
556
- }, StaticEntitlementService, StaticFeatureGuard, EnforceQuotaInterceptor, ...options.quotaProviders ?? [], {
865
+ }, StaticEntitlementService, StaticFeatureGuard, EnforceQuotaInterceptor, ...quotaProvidersHostedByTenantBilling ? [] : options.quotaProviders ?? [], {
557
866
  provide: QUOTA_PROVIDERS_TOKEN,
558
867
  useFactory: /* @__PURE__ */ __name((...providers) => providers, "useFactory"),
559
868
  inject: options.quotaProviders ?? []
560
- }, {
561
- provide: APP_GUARD,
562
- useExisting: StaticFeatureGuard
563
- }, {
869
+ }, ...options.globalFeatureGuard === false ? [] : [
870
+ {
871
+ provide: APP_GUARD,
872
+ useExisting: StaticFeatureGuard
873
+ }
874
+ ], {
564
875
  provide: APP_INTERCEPTOR,
565
876
  useExisting: EnforceQuotaInterceptor
566
877
  });
@@ -569,11 +880,13 @@ var SaasPlatformModule = class _SaasPlatformModule {
569
880
  const tenantControllers = [];
570
881
  if (options.tenantManifest) {
571
882
  if (!hasResolver && !hasFallback) {
572
- throw new Error("SaasPlatformModule.forRoot: tenantManifest erfordert defaultPlanId oder adapters.planResolver, sonst kann das Manifest keine Features aufl\xF6sen.");
883
+ throw new Error("SaasPlatformModule.forRoot: tenantManifest requires defaultPlanId, adapters.planResolver or tenantBilling so it can resolve features.");
573
884
  }
574
885
  lightweightProviders.push(TenantManifestService);
575
886
  lightweightExports.push(TenantManifestService);
576
- tenantControllers.push(buildTenantManifestController(options.tenantManifest));
887
+ tenantControllers.push(buildTenantManifestController(options.tenantManifest === true ? {
888
+ guards: Array.isArray(tenantBillingConfig?.authGuards) ? tenantBillingConfig.authGuards : options.controller.guards
889
+ } : options.tenantManifest));
577
890
  }
578
891
  return {
579
892
  module: _SaasPlatformModule,
@@ -588,9 +901,24 @@ var SaasPlatformModule = class _SaasPlatformModule {
588
901
  // imported module's token directly is an UnknownExportException
589
902
  // at boot — the module export below already carries it.
590
903
  AdminManifestModule,
591
- ...options.entitlement ? [
904
+ ...requiresFullEntitlement ? [
592
905
  EntitlementModule
593
906
  ] : [],
907
+ ...catalogConfig ? [
908
+ CatalogModule
909
+ ] : [],
910
+ ...tenantBillingConfig ? [
911
+ TenantBillingModule
912
+ ] : [],
913
+ ...subscriptionBundlesConfig ? [
914
+ SubscriptionBundleModule
915
+ ] : [],
916
+ ...adminResourcesConfig ? [
917
+ AdminResourcesModule
918
+ ] : [],
919
+ ...promoCodesConfig ? [
920
+ PromoCodesModule
921
+ ] : [],
594
922
  ...lightweightExports
595
923
  ],
596
924
  global: true
@@ -609,6 +937,8 @@ export {
609
937
  StaticFeatureGuard,
610
938
  QUOTA_PROVIDERS_TOKEN,
611
939
  EnforceQuotaInterceptor,
940
+ QuotaProvidersUsageSnapshot,
941
+ SubscriptionPlanResolver,
612
942
  TenantManifestService,
613
943
  buildTenantManifestController,
614
944
  SaasPlatformModule