@saasicat/nest 0.26.1 → 0.27.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 (37) hide show
  1. package/LICENSE +90 -201
  2. package/dist/_entries.cjs +3265 -2846
  3. package/dist/billing/index.d.cts +4 -4
  4. package/dist/billing/index.d.ts +4 -4
  5. package/dist/billing/index.js +29 -27
  6. package/dist/chunk-2FR6ZL7R.js +0 -0
  7. package/dist/chunk-2JTISFUZ.js +792 -0
  8. package/dist/chunk-2SLTRKXC.js +0 -0
  9. package/dist/chunk-3E6ONOMG.js +50 -0
  10. package/dist/{chunk-B7NRY2LV.js → chunk-CJFAOD7K.js} +1 -39
  11. package/dist/{chunk-TCBYOFAC.js → chunk-EHKMPP6J.js} +1164 -745
  12. package/dist/{chunk-NNNXXUPT.js → chunk-F4B2NJUZ.js} +23 -88
  13. package/dist/chunk-H4DOTO7S.js +85 -0
  14. package/dist/{chunk-YWDLYOFN.js → chunk-UHD6FJBQ.js} +412 -1188
  15. package/dist/{define-saasicat-BK6RRWWG.d.ts → define-saasicat-3ois8t8s.d.ts} +26 -3
  16. package/dist/{define-saasicat-D30I-WET.d.cts → define-saasicat-CFxut1VR.d.cts} +26 -3
  17. package/dist/{enforce-quota.interceptor-B9QueAFf.d.ts → enforce-quota.interceptor-CYDNvAQz.d.ts} +3 -3
  18. package/dist/{enforce-quota.interceptor-DY6CLjoP.d.cts → enforce-quota.interceptor-N80KC34N.d.cts} +3 -3
  19. package/dist/entitlement/index.js +5 -2
  20. package/dist/index.d.cts +5 -5
  21. package/dist/index.d.ts +5 -5
  22. package/dist/index.js +39 -31
  23. package/dist/{saas-platform.module-D1mUZPrz.d.ts → module-options-ByJL7FZm.d.ts} +40 -27
  24. package/dist/{saas-platform.module-CgalxzcT.d.cts → module-options-klrPnWGF.d.cts} +40 -27
  25. package/dist/platform/index.d.cts +201 -19
  26. package/dist/platform/index.d.ts +201 -19
  27. package/dist/platform/index.js +34 -9
  28. package/dist/subscription-contract/index.js +5 -2
  29. package/dist/{tenant-billing.controller-DHSUrT_N.d.cts → tenant-billing.controller-CAMMdfn1.d.cts} +1 -1
  30. package/dist/{tenant-billing.controller-BSlpFqVA.d.ts → tenant-billing.controller-CYYK0CpT.d.ts} +1 -1
  31. package/dist/{subscription-bundles.module-BDbODWvA.d.ts → tenant-billing.module-B0stLoJn.d.ts} +51 -51
  32. package/dist/{subscription-bundles.module-Cfi-lFWF.d.cts → tenant-billing.module-BxJS6fX4.d.cts} +51 -51
  33. package/dist/testing/index.d.cts +8 -8
  34. package/dist/testing/index.d.ts +8 -8
  35. package/dist/testing/index.js +6 -4
  36. package/package.json +4 -4
  37. /package/dist/{chunk-FY4EJTV4.js → chunk-OYNHOY45.js} +0 -0
@@ -1,31 +1,179 @@
1
- import { p as PlanResolverPort } from '../saas-platform.module-CgalxzcT.cjs';
2
- export { P as PLAN_RESOLVER_PORT_TOKEN, S as SaaSiCatModule, a as SaasPlatformAdapters, b as SaasPlatformAdminResourcesOptions, c as SaasPlatformAdminStatsOptions, d as SaasPlatformCatalogOptions, e as SaasPlatformCheckoutOfferOptions, S as SaasPlatformModule, f as SaasPlatformModuleOptions, g as SaasPlatformPromoCodesOptions, h as SaasPlatformSetupOptions, i as SaasPlatformSubscriptionBundlesOptions, j as SaasPlatformSubscriptionContractOptions, k as SaasPlatformTenantAuthGuards, l as SaasPlatformTenantBillingOptions, m as SetupModule, n as SetupModuleOptions, o as StaticPlanResolver, T as TenantManifestControllerOptions, q as buildTenantManifestController } from '../saas-platform.module-CgalxzcT.cjs';
3
- export { a as SAASICAT_PUBLIC_ROUTE_KEY, d as SaaSiCatPublicRoute, S as SetupService, f as defineSaaSiCat, i as isSaaSiCatPublicRoute } from '../define-saasicat-D30I-WET.cjs';
4
- import { SubscriptionRepository, UsageSnapshotPort, QuotaProvider } from '@saasicat/types';
5
- import { S as StaticEntitlementService } from '../enforce-quota.interceptor-DY6CLjoP.cjs';
6
- export { E as EnforceQuotaInterceptor, Q as QUOTA_PROVIDERS_TOKEN, b as STATIC_FEATURE_GUARD_CONFIG_TOKEN, c as StaticEntitlementSnapshot, a as StaticFeatureGuard, d as StaticFeatureGuardConfig } from '../enforce-quota.interceptor-DY6CLjoP.cjs';
7
- import { OnApplicationBootstrap } from '@nestjs/common';
1
+ export { a as SAASICAT_PUBLIC_ROUTE_KEY, d as SaaSiCatModule, e as SaaSiCatPublicRoute, d as SaasPlatformModule, S as SetupService, g as defineSaaSiCat, i as isSaaSiCatPublicRoute } from '../define-saasicat-CFxut1VR.cjs';
2
+ import { CanActivate, DynamicModule, OnApplicationBootstrap } from '@nestjs/common';
3
+ import { SaasicatPersistenceAdapter, SubscriptionUsagePort, SubscriptionRepository, UsageSnapshotPort, QuotaProvider } from '@saasicat/types';
4
+ import { P as ProviderSpec } from '../di-CcNeq9v-.cjs';
5
+ import { b as DiscoveryAppInfo } from '../discovery.scanner-DLgEvuoy.cjs';
6
+ export { c as DiscoveryScanner } from '../discovery.scanner-DLgEvuoy.cjs';
7
+ import { e as SaasPlatformModuleOptions, S as SaasPlatformAdapters, o as PlanResolverPort } from '../module-options-klrPnWGF.cjs';
8
+ export { P as PLAN_RESOLVER_PORT_TOKEN, a as SaasPlatformAdminResourcesOptions, b as SaasPlatformAdminStatsOptions, c as SaasPlatformCatalogOptions, d as SaasPlatformCheckoutOfferOptions, f as SaasPlatformPromoCodesOptions, g as SaasPlatformSetupOptions, h as SaasPlatformSubscriptionBundlesOptions, i as SaasPlatformSubscriptionContractOptions, j as SaasPlatformTenantAuthGuards, k as SaasPlatformTenantBillingOptions, l as SetupModule, m as SetupModuleOptions, n as StaticPlanResolver, T as TenantManifestControllerOptions, p as buildTenantManifestController } from '../module-options-klrPnWGF.cjs';
9
+ import { S as StaticEntitlementService } from '../enforce-quota.interceptor-N80KC34N.cjs';
10
+ export { E as EnforceQuotaInterceptor, Q as QUOTA_PROVIDERS_TOKEN, b as STATIC_FEATURE_GUARD_CONFIG_TOKEN, c as StaticEntitlementSnapshot, a as StaticFeatureGuard, d as StaticFeatureGuardConfig } from '../enforce-quota.interceptor-N80KC34N.cjs';
8
11
  import { DiscoveryService, MetadataScanner } from '@nestjs/core';
9
12
  export { A as AdminBypassRlsInterceptor, a as AdminManifestModule, b as AdminManifestModuleOptions, c as AdminManifestService, d as AdminModule, e as AdminModuleOptions, f as AdminStatsService, M as MfaGuard, h as MfaService, S as SuperAdminGuard } from '../admin-stats.service-HUZIU448.cjs';
10
13
  export { A as AdminAuditService } from '../admin-audit.service-9IqXMlZm.cjs';
11
14
  export { e as AdminResourcesService, f as AdminStatsModule, g as AdminStatsModuleOptions } from '../admin-resources.module-DGgGSdzN.cjs';
12
15
  export { c as EntitlementService } from '../aggregation-mZ7WZROK.cjs';
13
- export { d as ComposedTenantAuthGuard, P as PendingPlanMaterializationService, h as PlanChangePreviewService, r as SubscriptionBundlePreviewService, s as SubscriptionBundlesService, T as TenantAdminGuard, t as TenantBillingController } from '../tenant-billing.controller-DHSUrT_N.cjs';
16
+ export { d as ComposedTenantAuthGuard, P as PendingPlanMaterializationService, h as PlanChangePreviewService, r as SubscriptionBundlePreviewService, s as SubscriptionBundlesService, T as TenantAdminGuard, t as TenantBillingController } from '../tenant-billing.controller-CAMMdfn1.cjs';
14
17
  export { B as BundlesService, C as CatalogEntriesService, M as MarketingProjectionsService, b as MarketingSettingsService, P as PlanVersionsService, c as PlansService, d as PromotionsService, e as PublicMarketingCatalogService } from '../plan-versions.service-_saKzb50.cjs';
15
- export { c as DiscoveryScanner } from '../discovery.scanner-DLgEvuoy.cjs';
16
18
  export { P as PromoCodeRateLimitGuard } from '../rate-limit.guard-dntqAlfZ.cjs';
17
19
  export { g as PromoCodesService } from '../service-D48clObR.cjs';
18
20
  export { a as CheckoutOfferModule, b as CheckoutOfferModuleOptions } from '../checkout-offer.module-DmlpPevf.cjs';
19
21
  export { C as CheckoutOfferService } from '../checkout-offer.service-BX51dljL.cjs';
20
22
  export { S as SubscriptionContractModule, a as SubscriptionContractModuleOptions } from '../subscription-contract.module-B2uNvUJL.cjs';
21
23
  export { S as SubscriptionContractService } from '../subscription-contract.service--cm47ZJJ.cjs';
22
- import '../di-CcNeq9v-.cjs';
24
+ import '../tenant-billing.module-BxJS6fX4.cjs';
25
+ import '../catalog.module-D-_pQDNA.cjs';
23
26
  import '../plan-resolution-CFCoUkrE.cjs';
24
27
  import '../module-CHMIyWlf.cjs';
25
- import '../catalog.module-D-_pQDNA.cjs';
26
- import '../subscription-bundles.module-Cfi-lFWF.cjs';
27
28
  import 'rxjs';
28
29
 
30
+ /**
31
+ * What `tenantBilling` resolved and its two dependants need.
32
+ *
33
+ * Mutable, and the only mutable thing here: `composeTenantBilling` writes it,
34
+ * `composeSubscriptionBundles` and the entitlement runtime read it. A composer
35
+ * that runs before tenant billing sees the initial values, which is correct —
36
+ * there was no tenant billing to resolve anything.
37
+ */
38
+ interface SharedTenantBinding {
39
+ /** Normalised `authGuards`, shared with the bundle controller. */
40
+ authGuards?: ProviderSpec<ReadonlyArray<CanActivate>>;
41
+ /** The usage port both controllers report against. */
42
+ subscriptionUsagePort?: ProviderSpec<SubscriptionUsagePort>;
43
+ /**
44
+ * Whether the tenant-billing module already registered the app's quota
45
+ * providers.
46
+ *
47
+ * It registers them as its own providers and exports, so the entitlement
48
+ * runtime must not register them a second time — two instances of a
49
+ * `QuotaProvider` are two counters over the same rows.
50
+ */
51
+ quotaProvidersHostedByTenantBilling: boolean;
52
+ }
53
+ interface CompositionContext {
54
+ readonly options: SaasPlatformModuleOptions;
55
+ /** After the bundle slices and the explicit entries have been merged. */
56
+ readonly adapters: SaasPlatformAdapters;
57
+ readonly persistence: SaasicatPersistenceAdapter | undefined;
58
+ /** App key and version, for discovery and the public catalogue. */
59
+ readonly appInfo: DiscoveryAppInfo;
60
+ /** Entitlement is needed directly, or by tenant billing / bundles. */
61
+ readonly requiresFullEntitlement: boolean;
62
+ readonly shared: SharedTenantBinding;
63
+ }
64
+
65
+ /** One feature's contribution: the modules it adds, or none. */
66
+ type Composer = (ctx: CompositionContext) => DynamicModule[];
67
+ declare const FEATURE_COMPOSERS: readonly Composer[];
68
+ /** Every module the enabled features contribute, in composer order. */
69
+ declare function composeFeatures(ctx: CompositionContext): DynamicModule[];
70
+
71
+ /**
72
+ * Mounted on purpose and deliberately not re-exported.
73
+ *
74
+ * Declared here rather than left as an absence, because the guard in
75
+ * `tests/platform-composition.test.js` otherwise reports it every time and the
76
+ * next person has to re-derive whether it is a bug.
77
+ *
78
+ * `PublicCatalogModule` is a controller and one token, `FEATURE_UI_REGISTRY` —
79
+ * which the APP supplies through `catalog: { featureUiRegistry }` rather than
80
+ * injects. Re-exporting it would also collide with the identically named token
81
+ * from `billing`, which means something else (see CONTRIBUTING, "One bundle,
82
+ * many entries").
83
+ */
84
+ declare const MOUNTED_BUT_NOT_EXPORTED: readonly string[];
85
+ /** The `exports` of the assembled module, plus whatever it provides itself. */
86
+ declare function composeModuleExports(ctx: CompositionContext, ownExports: NonNullable<DynamicModule['exports']>): NonNullable<DynamicModule['exports']>;
87
+
88
+ /** Everything a rule may look at: the options as given, the adapters as resolved. */
89
+ interface PlatformConfiguration {
90
+ readonly options: SaasPlatformModuleOptions;
91
+ /** After the bundle slices and the explicit entries have been merged. */
92
+ readonly adapters: SaasPlatformAdapters;
93
+ }
94
+ interface PlatformRule {
95
+ /** Stable id — `<area>.<what-it-requires>`. Appears in the boot error. */
96
+ readonly id: string;
97
+ /** Whether this rule has anything to say about this configuration. */
98
+ readonly when: (c: PlatformConfiguration) => boolean;
99
+ /** True when the configuration satisfies it. */
100
+ readonly assert: (c: PlatformConfiguration) => boolean;
101
+ /**
102
+ * What is wrong and what to do about it.
103
+ *
104
+ * A function only where it has to be: several of these name which of a set
105
+ * is missing, and "adapters are missing: subscriptionRepository,
106
+ * transactionRunner" is worth more than "adapters are missing". The rest
107
+ * are the same sentence every time, and a closure that ignores its argument
108
+ * is one more thing a reader has to check for.
109
+ */
110
+ readonly message: string | ((c: PlatformConfiguration) => string);
111
+ /**
112
+ * Where the option is documented.
113
+ *
114
+ * Derived from the id rather than written beside it — see `docsUrlFor`.
115
+ */
116
+ readonly docs: string;
117
+ /**
118
+ * The typed error this rule threw before the table existed, when it has
119
+ * one.
120
+ *
121
+ * Used only when this rule is the ONLY violation — and that is not a
122
+ * compromise, it is the exact set of cases where the typed error was ever
123
+ * observable. A configuration with two problems used to throw whichever
124
+ * check ran first and stop, so nothing could catch the second one by type
125
+ * either. Where a consumer could see `PersistenceCapabilityError`, it still
126
+ * sees it; where it could not, it now gets the full list instead of one
127
+ * arbitrary problem.
128
+ */
129
+ readonly error?: (c: PlatformConfiguration) => Error;
130
+ }
131
+ /**
132
+ * GitHub's heading slug for `text`: lowercased, punctuation dropped, spaces
133
+ * hyphenated.
134
+ *
135
+ * Written out because the anchor has to match a heading nobody types — the
136
+ * page is generated. `tests/options-reference-is-generated.test.js` asserts
137
+ * every rule's anchor is a heading in the file, so a wrong slug fails the build
138
+ * rather than producing a link that lands at the top of the page.
139
+ */
140
+ declare const headingSlug: (text: string) => string;
141
+ /**
142
+ * The documentation link for a rule.
143
+ *
144
+ * Derived from the id, so the two cannot disagree. They did, on the first
145
+ * attempt: eleven hand-written anchors against headings the generator names
146
+ * after the ids, and every one of them landed at the top of the page.
147
+ */
148
+ declare const docsUrlFor: (id: string) => string;
149
+ declare const PLATFORM_RULES: readonly PlatformRule[];
150
+
151
+ interface PlatformViolation {
152
+ readonly id: string;
153
+ readonly message: string;
154
+ readonly docs: string;
155
+ }
156
+ /** Every rule that applies to this configuration and is not satisfied by it. */
157
+ declare function findViolations(configuration: PlatformConfiguration, rules?: readonly PlatformRule[]): PlatformViolation[];
158
+ /**
159
+ * The error a misconfigured application boots into.
160
+ *
161
+ * Carries the violations as data as well as prose: a consumer's own boot
162
+ * diagnostics can group them, and the rule ids are stable enough to assert on.
163
+ */
164
+ declare class SaaSiCatConfigurationError extends Error {
165
+ readonly violations: readonly PlatformViolation[];
166
+ constructor(violations: readonly PlatformViolation[]);
167
+ }
168
+ /**
169
+ * Throws once, naming every problem, or returns.
170
+ *
171
+ * A single violation from a rule that owns a typed error raises that error
172
+ * instead of the aggregate — see `PlatformRule.error` for why that is the
173
+ * whole of the previous behaviour rather than part of it.
174
+ */
175
+ declare function assertConfiguration(configuration: PlatformConfiguration, rules?: readonly PlatformRule[]): void;
176
+
29
177
  /**
30
178
  * Standard `tenantId → planId` resolver backed by the canonical subscription
31
179
  * repository. This removes the identical resolver class most applications
@@ -48,22 +196,56 @@ declare class QuotaProvidersUsageSnapshot implements UsageSnapshotPort {
48
196
  snapshot(tenantId: string): Promise<Record<string, number>>;
49
197
  }
50
198
 
51
- declare class FeatureGuardCoverageCheck implements OnApplicationBootstrap {
199
+ /** What the module knows at composition time and this check cannot see. */
200
+ interface EnforcementChainState {
201
+ /**
202
+ * Whether anything can enforce `@RequireFeature`.
203
+ *
204
+ * True on the static path, and also on the V3 path, where the application
205
+ * binds `FeatureGuard` from `@saasicat/nest/billing` itself.
206
+ */
207
+ readonly featureEnforcementActive: boolean;
208
+ /**
209
+ * Whether anything can enforce `@EnforceQuota`.
210
+ *
211
+ * Narrower than the field above, and the two were one field until an app
212
+ * on the V3 path with no plan resolver booted with every quota unenforced:
213
+ * `EnforceQuotaInterceptor` comes from `composeEnforcementRuntime`, which
214
+ * registers nothing unless a plan resolver exists. A flag named for
215
+ * "entitlement" answered a question about features and was read as an
216
+ * answer about quotas.
217
+ */
218
+ readonly quotaEnforcementActive: boolean;
219
+ /** Whether the platform bound its own global feature guard. */
220
+ readonly globalGuardBound: boolean;
221
+ /**
222
+ * Whether `globalFeatureGuard: false` is what unbound it.
223
+ *
224
+ * Distinct from `globalGuardBound` because the two have different causes
225
+ * and the message has to say the true one. On the V3 entitlement path the
226
+ * platform binds no global guard either — it never registers the static
227
+ * stack — and naming an option the application did not set sends the reader
228
+ * to look for a line that is not in their file.
229
+ */
230
+ readonly globalGuardOptedOut: boolean;
231
+ }
232
+ declare const ENFORCEMENT_CHAIN_STATE_TOKEN: unique symbol;
233
+ declare class EnforcementChainCheck implements OnApplicationBootstrap {
52
234
  private readonly discoveryService;
53
235
  private readonly metadataScanner;
54
- private readonly logger;
55
- constructor(discoveryService: DiscoveryService, metadataScanner: MetadataScanner);
236
+ private readonly state;
237
+ constructor(discoveryService: DiscoveryService, metadataScanner: MetadataScanner, state?: EnforcementChainState);
56
238
  onApplicationBootstrap(): void;
57
239
  /**
58
- * Annotated handlers whose own guards and whose controller's guards contain
59
- * no recognisable feature guard.
240
+ * Every route carrying an enforcement annotation, with whether a
241
+ * recognisable feature guard covers it.
60
242
  *
61
243
  * Deliberately conservative in one direction only: an unrecognised wrapper
62
244
  * is reported (and the message says so) rather than assumed safe, because
63
245
  * the named route is cheap to check. The reverse — staying silent about a
64
246
  * route with no guard at all — is the failure this exists to prevent.
65
247
  */
66
- private findUncoveredRoutes;
248
+ private findAnnotatedRoutes;
67
249
  }
68
250
 
69
251
  interface TenantNavItem {
@@ -119,4 +301,4 @@ declare class TenantManifestService {
119
301
  getManifest(tenantId: string): Promise<TenantManifest>;
120
302
  }
121
303
 
122
- export { FeatureGuardCoverageCheck, PlanResolverPort, QuotaProvidersUsageSnapshot, StaticEntitlementService, SubscriptionPlanResolver, type TenantManifest, TenantManifestService, type TenantNavItem };
304
+ export { type Composer, type CompositionContext, ENFORCEMENT_CHAIN_STATE_TOKEN, EnforcementChainCheck, type EnforcementChainState, FEATURE_COMPOSERS, MOUNTED_BUT_NOT_EXPORTED, PLATFORM_RULES, PlanResolverPort, type PlatformConfiguration, type PlatformRule, type PlatformViolation, QuotaProvidersUsageSnapshot, SaaSiCatConfigurationError, SaasPlatformAdapters, SaasPlatformModuleOptions, StaticEntitlementService, SubscriptionPlanResolver, type TenantManifest, TenantManifestService, type TenantNavItem, assertConfiguration, composeFeatures, composeModuleExports, docsUrlFor, findViolations, headingSlug };
@@ -1,31 +1,179 @@
1
- import { p as PlanResolverPort } from '../saas-platform.module-D1mUZPrz.js';
2
- export { P as PLAN_RESOLVER_PORT_TOKEN, S as SaaSiCatModule, a as SaasPlatformAdapters, b as SaasPlatformAdminResourcesOptions, c as SaasPlatformAdminStatsOptions, d as SaasPlatformCatalogOptions, e as SaasPlatformCheckoutOfferOptions, S as SaasPlatformModule, f as SaasPlatformModuleOptions, g as SaasPlatformPromoCodesOptions, h as SaasPlatformSetupOptions, i as SaasPlatformSubscriptionBundlesOptions, j as SaasPlatformSubscriptionContractOptions, k as SaasPlatformTenantAuthGuards, l as SaasPlatformTenantBillingOptions, m as SetupModule, n as SetupModuleOptions, o as StaticPlanResolver, T as TenantManifestControllerOptions, q as buildTenantManifestController } from '../saas-platform.module-D1mUZPrz.js';
3
- export { a as SAASICAT_PUBLIC_ROUTE_KEY, d as SaaSiCatPublicRoute, S as SetupService, f as defineSaaSiCat, i as isSaaSiCatPublicRoute } from '../define-saasicat-BK6RRWWG.js';
4
- import { SubscriptionRepository, UsageSnapshotPort, QuotaProvider } from '@saasicat/types';
5
- import { S as StaticEntitlementService } from '../enforce-quota.interceptor-B9QueAFf.js';
6
- export { E as EnforceQuotaInterceptor, Q as QUOTA_PROVIDERS_TOKEN, b as STATIC_FEATURE_GUARD_CONFIG_TOKEN, c as StaticEntitlementSnapshot, a as StaticFeatureGuard, d as StaticFeatureGuardConfig } from '../enforce-quota.interceptor-B9QueAFf.js';
7
- import { OnApplicationBootstrap } from '@nestjs/common';
1
+ export { a as SAASICAT_PUBLIC_ROUTE_KEY, d as SaaSiCatModule, e as SaaSiCatPublicRoute, d as SaasPlatformModule, S as SetupService, g as defineSaaSiCat, i as isSaaSiCatPublicRoute } from '../define-saasicat-3ois8t8s.js';
2
+ import { CanActivate, DynamicModule, OnApplicationBootstrap } from '@nestjs/common';
3
+ import { SaasicatPersistenceAdapter, SubscriptionUsagePort, SubscriptionRepository, UsageSnapshotPort, QuotaProvider } from '@saasicat/types';
4
+ import { P as ProviderSpec } from '../di-CcNeq9v-.js';
5
+ import { b as DiscoveryAppInfo } from '../discovery.scanner-DLgEvuoy.js';
6
+ export { c as DiscoveryScanner } from '../discovery.scanner-DLgEvuoy.js';
7
+ import { e as SaasPlatformModuleOptions, S as SaasPlatformAdapters, o as PlanResolverPort } from '../module-options-ByJL7FZm.js';
8
+ export { P as PLAN_RESOLVER_PORT_TOKEN, a as SaasPlatformAdminResourcesOptions, b as SaasPlatformAdminStatsOptions, c as SaasPlatformCatalogOptions, d as SaasPlatformCheckoutOfferOptions, f as SaasPlatformPromoCodesOptions, g as SaasPlatformSetupOptions, h as SaasPlatformSubscriptionBundlesOptions, i as SaasPlatformSubscriptionContractOptions, j as SaasPlatformTenantAuthGuards, k as SaasPlatformTenantBillingOptions, l as SetupModule, m as SetupModuleOptions, n as StaticPlanResolver, T as TenantManifestControllerOptions, p as buildTenantManifestController } from '../module-options-ByJL7FZm.js';
9
+ import { S as StaticEntitlementService } from '../enforce-quota.interceptor-CYDNvAQz.js';
10
+ export { E as EnforceQuotaInterceptor, Q as QUOTA_PROVIDERS_TOKEN, b as STATIC_FEATURE_GUARD_CONFIG_TOKEN, c as StaticEntitlementSnapshot, a as StaticFeatureGuard, d as StaticFeatureGuardConfig } from '../enforce-quota.interceptor-CYDNvAQz.js';
8
11
  import { DiscoveryService, MetadataScanner } from '@nestjs/core';
9
12
  export { A as AdminBypassRlsInterceptor, a as AdminManifestModule, b as AdminManifestModuleOptions, c as AdminManifestService, d as AdminModule, e as AdminModuleOptions, f as AdminStatsService, M as MfaGuard, h as MfaService, S as SuperAdminGuard } from '../admin-stats.service-Db2exT_d.js';
10
13
  export { A as AdminAuditService } from '../admin-audit.service-9IqXMlZm.js';
11
14
  export { e as AdminResourcesService, f as AdminStatsModule, g as AdminStatsModuleOptions } from '../admin-resources.module-BY5r_1MG.js';
12
15
  export { c as EntitlementService } from '../aggregation-DP5zfKbU.js';
13
- export { d as ComposedTenantAuthGuard, P as PendingPlanMaterializationService, h as PlanChangePreviewService, r as SubscriptionBundlePreviewService, s as SubscriptionBundlesService, T as TenantAdminGuard, t as TenantBillingController } from '../tenant-billing.controller-BSlpFqVA.js';
16
+ export { d as ComposedTenantAuthGuard, P as PendingPlanMaterializationService, h as PlanChangePreviewService, r as SubscriptionBundlePreviewService, s as SubscriptionBundlesService, T as TenantAdminGuard, t as TenantBillingController } from '../tenant-billing.controller-CYYK0CpT.js';
14
17
  export { B as BundlesService, C as CatalogEntriesService, M as MarketingProjectionsService, b as MarketingSettingsService, P as PlanVersionsService, c as PlansService, d as PromotionsService, e as PublicMarketingCatalogService } from '../plan-versions.service-B-YoE4SZ.js';
15
- export { c as DiscoveryScanner } from '../discovery.scanner-DLgEvuoy.js';
16
18
  export { P as PromoCodeRateLimitGuard } from '../rate-limit.guard-dntqAlfZ.js';
17
19
  export { g as PromoCodesService } from '../service-D48clObR.js';
18
20
  export { a as CheckoutOfferModule, b as CheckoutOfferModuleOptions } from '../checkout-offer.module-Ca3_TieR.js';
19
21
  export { C as CheckoutOfferService } from '../checkout-offer.service-BX51dljL.js';
20
22
  export { S as SubscriptionContractModule, a as SubscriptionContractModuleOptions } from '../subscription-contract.module-edyhBr4n.js';
21
23
  export { S as SubscriptionContractService } from '../subscription-contract.service--cm47ZJJ.js';
22
- import '../di-CcNeq9v-.js';
24
+ import '../tenant-billing.module-B0stLoJn.js';
25
+ import '../catalog.module-CQr1FpkI.js';
23
26
  import '../plan-resolution-CFCoUkrE.js';
24
27
  import '../module-BE17XFLg.js';
25
- import '../catalog.module-CQr1FpkI.js';
26
- import '../subscription-bundles.module-BDbODWvA.js';
27
28
  import 'rxjs';
28
29
 
30
+ /**
31
+ * What `tenantBilling` resolved and its two dependants need.
32
+ *
33
+ * Mutable, and the only mutable thing here: `composeTenantBilling` writes it,
34
+ * `composeSubscriptionBundles` and the entitlement runtime read it. A composer
35
+ * that runs before tenant billing sees the initial values, which is correct —
36
+ * there was no tenant billing to resolve anything.
37
+ */
38
+ interface SharedTenantBinding {
39
+ /** Normalised `authGuards`, shared with the bundle controller. */
40
+ authGuards?: ProviderSpec<ReadonlyArray<CanActivate>>;
41
+ /** The usage port both controllers report against. */
42
+ subscriptionUsagePort?: ProviderSpec<SubscriptionUsagePort>;
43
+ /**
44
+ * Whether the tenant-billing module already registered the app's quota
45
+ * providers.
46
+ *
47
+ * It registers them as its own providers and exports, so the entitlement
48
+ * runtime must not register them a second time — two instances of a
49
+ * `QuotaProvider` are two counters over the same rows.
50
+ */
51
+ quotaProvidersHostedByTenantBilling: boolean;
52
+ }
53
+ interface CompositionContext {
54
+ readonly options: SaasPlatformModuleOptions;
55
+ /** After the bundle slices and the explicit entries have been merged. */
56
+ readonly adapters: SaasPlatformAdapters;
57
+ readonly persistence: SaasicatPersistenceAdapter | undefined;
58
+ /** App key and version, for discovery and the public catalogue. */
59
+ readonly appInfo: DiscoveryAppInfo;
60
+ /** Entitlement is needed directly, or by tenant billing / bundles. */
61
+ readonly requiresFullEntitlement: boolean;
62
+ readonly shared: SharedTenantBinding;
63
+ }
64
+
65
+ /** One feature's contribution: the modules it adds, or none. */
66
+ type Composer = (ctx: CompositionContext) => DynamicModule[];
67
+ declare const FEATURE_COMPOSERS: readonly Composer[];
68
+ /** Every module the enabled features contribute, in composer order. */
69
+ declare function composeFeatures(ctx: CompositionContext): DynamicModule[];
70
+
71
+ /**
72
+ * Mounted on purpose and deliberately not re-exported.
73
+ *
74
+ * Declared here rather than left as an absence, because the guard in
75
+ * `tests/platform-composition.test.js` otherwise reports it every time and the
76
+ * next person has to re-derive whether it is a bug.
77
+ *
78
+ * `PublicCatalogModule` is a controller and one token, `FEATURE_UI_REGISTRY` —
79
+ * which the APP supplies through `catalog: { featureUiRegistry }` rather than
80
+ * injects. Re-exporting it would also collide with the identically named token
81
+ * from `billing`, which means something else (see CONTRIBUTING, "One bundle,
82
+ * many entries").
83
+ */
84
+ declare const MOUNTED_BUT_NOT_EXPORTED: readonly string[];
85
+ /** The `exports` of the assembled module, plus whatever it provides itself. */
86
+ declare function composeModuleExports(ctx: CompositionContext, ownExports: NonNullable<DynamicModule['exports']>): NonNullable<DynamicModule['exports']>;
87
+
88
+ /** Everything a rule may look at: the options as given, the adapters as resolved. */
89
+ interface PlatformConfiguration {
90
+ readonly options: SaasPlatformModuleOptions;
91
+ /** After the bundle slices and the explicit entries have been merged. */
92
+ readonly adapters: SaasPlatformAdapters;
93
+ }
94
+ interface PlatformRule {
95
+ /** Stable id — `<area>.<what-it-requires>`. Appears in the boot error. */
96
+ readonly id: string;
97
+ /** Whether this rule has anything to say about this configuration. */
98
+ readonly when: (c: PlatformConfiguration) => boolean;
99
+ /** True when the configuration satisfies it. */
100
+ readonly assert: (c: PlatformConfiguration) => boolean;
101
+ /**
102
+ * What is wrong and what to do about it.
103
+ *
104
+ * A function only where it has to be: several of these name which of a set
105
+ * is missing, and "adapters are missing: subscriptionRepository,
106
+ * transactionRunner" is worth more than "adapters are missing". The rest
107
+ * are the same sentence every time, and a closure that ignores its argument
108
+ * is one more thing a reader has to check for.
109
+ */
110
+ readonly message: string | ((c: PlatformConfiguration) => string);
111
+ /**
112
+ * Where the option is documented.
113
+ *
114
+ * Derived from the id rather than written beside it — see `docsUrlFor`.
115
+ */
116
+ readonly docs: string;
117
+ /**
118
+ * The typed error this rule threw before the table existed, when it has
119
+ * one.
120
+ *
121
+ * Used only when this rule is the ONLY violation — and that is not a
122
+ * compromise, it is the exact set of cases where the typed error was ever
123
+ * observable. A configuration with two problems used to throw whichever
124
+ * check ran first and stop, so nothing could catch the second one by type
125
+ * either. Where a consumer could see `PersistenceCapabilityError`, it still
126
+ * sees it; where it could not, it now gets the full list instead of one
127
+ * arbitrary problem.
128
+ */
129
+ readonly error?: (c: PlatformConfiguration) => Error;
130
+ }
131
+ /**
132
+ * GitHub's heading slug for `text`: lowercased, punctuation dropped, spaces
133
+ * hyphenated.
134
+ *
135
+ * Written out because the anchor has to match a heading nobody types — the
136
+ * page is generated. `tests/options-reference-is-generated.test.js` asserts
137
+ * every rule's anchor is a heading in the file, so a wrong slug fails the build
138
+ * rather than producing a link that lands at the top of the page.
139
+ */
140
+ declare const headingSlug: (text: string) => string;
141
+ /**
142
+ * The documentation link for a rule.
143
+ *
144
+ * Derived from the id, so the two cannot disagree. They did, on the first
145
+ * attempt: eleven hand-written anchors against headings the generator names
146
+ * after the ids, and every one of them landed at the top of the page.
147
+ */
148
+ declare const docsUrlFor: (id: string) => string;
149
+ declare const PLATFORM_RULES: readonly PlatformRule[];
150
+
151
+ interface PlatformViolation {
152
+ readonly id: string;
153
+ readonly message: string;
154
+ readonly docs: string;
155
+ }
156
+ /** Every rule that applies to this configuration and is not satisfied by it. */
157
+ declare function findViolations(configuration: PlatformConfiguration, rules?: readonly PlatformRule[]): PlatformViolation[];
158
+ /**
159
+ * The error a misconfigured application boots into.
160
+ *
161
+ * Carries the violations as data as well as prose: a consumer's own boot
162
+ * diagnostics can group them, and the rule ids are stable enough to assert on.
163
+ */
164
+ declare class SaaSiCatConfigurationError extends Error {
165
+ readonly violations: readonly PlatformViolation[];
166
+ constructor(violations: readonly PlatformViolation[]);
167
+ }
168
+ /**
169
+ * Throws once, naming every problem, or returns.
170
+ *
171
+ * A single violation from a rule that owns a typed error raises that error
172
+ * instead of the aggregate — see `PlatformRule.error` for why that is the
173
+ * whole of the previous behaviour rather than part of it.
174
+ */
175
+ declare function assertConfiguration(configuration: PlatformConfiguration, rules?: readonly PlatformRule[]): void;
176
+
29
177
  /**
30
178
  * Standard `tenantId → planId` resolver backed by the canonical subscription
31
179
  * repository. This removes the identical resolver class most applications
@@ -48,22 +196,56 @@ declare class QuotaProvidersUsageSnapshot implements UsageSnapshotPort {
48
196
  snapshot(tenantId: string): Promise<Record<string, number>>;
49
197
  }
50
198
 
51
- declare class FeatureGuardCoverageCheck implements OnApplicationBootstrap {
199
+ /** What the module knows at composition time and this check cannot see. */
200
+ interface EnforcementChainState {
201
+ /**
202
+ * Whether anything can enforce `@RequireFeature`.
203
+ *
204
+ * True on the static path, and also on the V3 path, where the application
205
+ * binds `FeatureGuard` from `@saasicat/nest/billing` itself.
206
+ */
207
+ readonly featureEnforcementActive: boolean;
208
+ /**
209
+ * Whether anything can enforce `@EnforceQuota`.
210
+ *
211
+ * Narrower than the field above, and the two were one field until an app
212
+ * on the V3 path with no plan resolver booted with every quota unenforced:
213
+ * `EnforceQuotaInterceptor` comes from `composeEnforcementRuntime`, which
214
+ * registers nothing unless a plan resolver exists. A flag named for
215
+ * "entitlement" answered a question about features and was read as an
216
+ * answer about quotas.
217
+ */
218
+ readonly quotaEnforcementActive: boolean;
219
+ /** Whether the platform bound its own global feature guard. */
220
+ readonly globalGuardBound: boolean;
221
+ /**
222
+ * Whether `globalFeatureGuard: false` is what unbound it.
223
+ *
224
+ * Distinct from `globalGuardBound` because the two have different causes
225
+ * and the message has to say the true one. On the V3 entitlement path the
226
+ * platform binds no global guard either — it never registers the static
227
+ * stack — and naming an option the application did not set sends the reader
228
+ * to look for a line that is not in their file.
229
+ */
230
+ readonly globalGuardOptedOut: boolean;
231
+ }
232
+ declare const ENFORCEMENT_CHAIN_STATE_TOKEN: unique symbol;
233
+ declare class EnforcementChainCheck implements OnApplicationBootstrap {
52
234
  private readonly discoveryService;
53
235
  private readonly metadataScanner;
54
- private readonly logger;
55
- constructor(discoveryService: DiscoveryService, metadataScanner: MetadataScanner);
236
+ private readonly state;
237
+ constructor(discoveryService: DiscoveryService, metadataScanner: MetadataScanner, state?: EnforcementChainState);
56
238
  onApplicationBootstrap(): void;
57
239
  /**
58
- * Annotated handlers whose own guards and whose controller's guards contain
59
- * no recognisable feature guard.
240
+ * Every route carrying an enforcement annotation, with whether a
241
+ * recognisable feature guard covers it.
60
242
  *
61
243
  * Deliberately conservative in one direction only: an unrecognised wrapper
62
244
  * is reported (and the message says so) rather than assumed safe, because
63
245
  * the named route is cheap to check. The reverse — staying silent about a
64
246
  * route with no guard at all — is the failure this exists to prevent.
65
247
  */
66
- private findUncoveredRoutes;
248
+ private findAnnotatedRoutes;
67
249
  }
68
250
 
69
251
  interface TenantNavItem {
@@ -119,4 +301,4 @@ declare class TenantManifestService {
119
301
  getManifest(tenantId: string): Promise<TenantManifest>;
120
302
  }
121
303
 
122
- export { FeatureGuardCoverageCheck, PlanResolverPort, QuotaProvidersUsageSnapshot, StaticEntitlementService, SubscriptionPlanResolver, type TenantManifest, TenantManifestService, type TenantNavItem };
304
+ export { type Composer, type CompositionContext, ENFORCEMENT_CHAIN_STATE_TOKEN, EnforcementChainCheck, type EnforcementChainState, FEATURE_COMPOSERS, MOUNTED_BUT_NOT_EXPORTED, PLATFORM_RULES, PlanResolverPort, type PlatformConfiguration, type PlatformRule, type PlatformViolation, QuotaProvidersUsageSnapshot, SaaSiCatConfigurationError, SaasPlatformAdapters, SaasPlatformModuleOptions, StaticEntitlementService, SubscriptionPlanResolver, type TenantManifest, TenantManifestService, type TenantNavItem, assertConfiguration, composeFeatures, composeModuleExports, docsUrlFor, findViolations, headingSlug };
@@ -1,13 +1,18 @@
1
1
  import {
2
2
  defineSaaSiCat
3
- } from "../chunk-FY4EJTV4.js";
3
+ } from "../chunk-OYNHOY45.js";
4
4
  import {
5
+ ENFORCEMENT_CHAIN_STATE_TOKEN,
5
6
  EnforceQuotaInterceptor,
6
- FeatureGuardCoverageCheck,
7
+ EnforcementChainCheck,
8
+ FEATURE_COMPOSERS,
9
+ MOUNTED_BUT_NOT_EXPORTED,
7
10
  PLAN_RESOLVER_PORT_TOKEN,
11
+ PLATFORM_RULES,
8
12
  QUOTA_PROVIDERS_TOKEN,
9
13
  QuotaProvidersUsageSnapshot,
10
14
  STATIC_FEATURE_GUARD_CONFIG_TOKEN,
15
+ SaaSiCatConfigurationError,
11
16
  SaasPlatformModule,
12
17
  SetupModule,
13
18
  SetupService,
@@ -16,11 +21,20 @@ import {
16
21
  StaticPlanResolver,
17
22
  SubscriptionPlanResolver,
18
23
  TenantManifestService,
19
- buildTenantManifestController
20
- } from "../chunk-TCBYOFAC.js";
24
+ assertConfiguration,
25
+ buildTenantManifestController,
26
+ composeFeatures,
27
+ composeModuleExports,
28
+ docsUrlFor,
29
+ findViolations,
30
+ headingSlug
31
+ } from "../chunk-EHKMPP6J.js";
21
32
  import {
22
33
  PromoCodeRateLimitGuard
23
34
  } from "../chunk-B6IWLV7R.js";
35
+ import {
36
+ SubscriptionContractModule
37
+ } from "../chunk-3E6ONOMG.js";
24
38
  import {
25
39
  AdminBypassRlsInterceptor,
26
40
  AdminManifestModule,
@@ -41,14 +55,13 @@ import {
41
55
  SubscriptionBundlesService,
42
56
  TenantAdminGuard,
43
57
  TenantBillingController
44
- } from "../chunk-YWDLYOFN.js";
58
+ } from "../chunk-UHD6FJBQ.js";
45
59
  import {
46
60
  PromoCodesService
47
61
  } from "../chunk-37HEKVDH.js";
48
62
  import {
49
- SubscriptionContractModule,
50
63
  SubscriptionContractService
51
- } from "../chunk-B7NRY2LV.js";
64
+ } from "../chunk-CJFAOD7K.js";
52
65
  import {
53
66
  BundlesService,
54
67
  CatalogEntriesService,
@@ -77,9 +90,10 @@ import "../chunk-6WL6VQ7Z.js";
77
90
  import {
78
91
  DiscoveryScanner
79
92
  } from "../chunk-5ZXBXYXU.js";
93
+ import "../chunk-H4DOTO7S.js";
80
94
  import {
81
95
  EntitlementService
82
- } from "../chunk-NNNXXUPT.js";
96
+ } from "../chunk-F4B2NJUZ.js";
83
97
  import "../chunk-DQKCK7DX.js";
84
98
  import "../chunk-NYLON2VC.js";
85
99
  import "../chunk-EBJJNTW6.js";
@@ -103,14 +117,18 @@ export {
103
117
  CheckoutOfferService,
104
118
  ComposedTenantAuthGuard,
105
119
  DiscoveryScanner,
120
+ ENFORCEMENT_CHAIN_STATE_TOKEN,
106
121
  EnforceQuotaInterceptor,
122
+ EnforcementChainCheck,
107
123
  EntitlementService,
108
- FeatureGuardCoverageCheck,
124
+ FEATURE_COMPOSERS,
125
+ MOUNTED_BUT_NOT_EXPORTED,
109
126
  MarketingProjectionsService,
110
127
  MarketingSettingsService,
111
128
  MfaGuard,
112
129
  MfaService,
113
130
  PLAN_RESOLVER_PORT_TOKEN,
131
+ PLATFORM_RULES,
114
132
  PendingPlanMaterializationService,
115
133
  PlanChangePreviewService,
116
134
  PlanVersionsService,
@@ -123,6 +141,7 @@ export {
123
141
  QuotaProvidersUsageSnapshot,
124
142
  SAASICAT_PUBLIC_ROUTE_KEY,
125
143
  STATIC_FEATURE_GUARD_CONFIG_TOKEN,
144
+ SaaSiCatConfigurationError,
126
145
  SaasPlatformModule as SaaSiCatModule,
127
146
  SaaSiCatPublicRoute,
128
147
  SaasPlatformModule,
@@ -140,7 +159,13 @@ export {
140
159
  TenantAdminGuard,
141
160
  TenantBillingController,
142
161
  TenantManifestService,
162
+ assertConfiguration,
143
163
  buildTenantManifestController,
164
+ composeFeatures,
165
+ composeModuleExports,
144
166
  defineSaaSiCat,
167
+ docsUrlFor,
168
+ findViolations,
169
+ headingSlug,
145
170
  isSaaSiCatPublicRoute
146
171
  };
@@ -1,10 +1,13 @@
1
+ import "../chunk-2SLTRKXC.js";
2
+ import {
3
+ SubscriptionContractModule
4
+ } from "../chunk-3E6ONOMG.js";
1
5
  import {
2
- SubscriptionContractModule,
3
6
  SubscriptionContractService,
4
7
  contractLineItemToInvoiceLineItem,
5
8
  sortContractLineItemsForInvoice,
6
9
  subscriptionContractToInvoiceSnapshot
7
- } from "../chunk-B7NRY2LV.js";
10
+ } from "../chunk-CJFAOD7K.js";
8
11
  import "../chunk-BSK6YBLI.js";
9
12
  import {
10
13
  SUBSCRIPTION_CONTRACT_REPOSITORY_TOKEN