@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.
- package/README.md +44 -8
- package/dist/.build-stamp +1 -1
- package/dist/_entries.cjs +10214 -4796
- package/dist/admin/index.d.cts +5 -4
- package/dist/admin/index.d.ts +5 -4
- package/dist/admin/index.js +13 -9
- package/dist/{admin-resources.module-CpvzcfCc.d.cts → admin-resources.module-B4XNvdH-.d.cts} +1 -2
- package/dist/{admin-resources.module-DF5_Q7RD.d.ts → admin-resources.module-CWqdI9BV.d.ts} +1 -2
- package/dist/{admin-stats.service-TYiSUwIR.d.ts → admin-stats.service-CsLNt19Z.d.ts} +11 -7
- package/dist/{admin-stats.service-DfKupywR.d.cts → admin-stats.service-DrZrU8IJ.d.cts} +11 -7
- package/dist/{aggregation-B3CD0v_d.d.cts → aggregation-ChpZDh0g.d.ts} +39 -6
- package/dist/{aggregation-BFzW07DE.d.ts → aggregation-OpnhwzF5.d.cts} +39 -6
- package/dist/billing/index.d.cts +448 -108
- package/dist/billing/index.d.ts +448 -108
- package/dist/billing/index.js +108 -41
- package/dist/catalog/index.d.cts +74 -45
- package/dist/catalog/index.d.ts +74 -45
- package/dist/catalog/index.js +21 -13
- package/dist/catalog.module-C9O7BxRq.d.ts +180 -0
- package/dist/catalog.module-T-I5Ricz.d.cts +180 -0
- package/dist/checkout-offer/index.d.cts +17 -22
- package/dist/checkout-offer/index.d.ts +17 -22
- package/dist/checkout-offer/index.js +21 -7
- package/dist/checkout-offer.module-DFhGy6JO.d.cts +58 -0
- package/dist/checkout-offer.module-x7SP_dX4.d.ts +58 -0
- package/dist/checkout-offer.service-BeHgYikI.d.ts +242 -0
- package/dist/checkout-offer.service-CCVxG9b2.d.cts +242 -0
- package/dist/chunk-34XVPR6P.js +222 -0
- package/dist/{chunk-AOQJEYOL.js → chunk-3QVGA6JX.js} +3 -1
- package/dist/chunk-3RLWML2S.js +896 -0
- package/dist/{chunk-TXC3LHHB.js → chunk-4SDSYV4A.js} +21 -13
- package/dist/chunk-55QEDAMX.js +946 -0
- package/dist/chunk-5CKC7O52.js +140 -0
- package/dist/chunk-6PJE7EWO.js +185 -0
- package/dist/chunk-7NJKVC5X.js +353 -0
- package/dist/{chunk-RUNZ3X4C.js → chunk-AOS2JPDD.js} +1263 -185
- package/dist/{chunk-WOVQPXV4.js → chunk-CQ2ZZMTD.js} +702 -725
- package/dist/{chunk-NA6O63B7.js → chunk-F5X66HF5.js} +5 -5
- package/dist/chunk-FEQZCWUL.js +174 -0
- package/dist/{chunk-VIJ5NJWC.js → chunk-G6EZWECL.js} +107 -31
- package/dist/chunk-G7RQO2XO.js +0 -0
- package/dist/{chunk-N3L3AICU.js → chunk-GPQWGA6B.js} +5 -6
- package/dist/chunk-GZTL64QK.js +7 -0
- package/dist/{chunk-SABTXESR.js → chunk-J7NJRK3K.js} +17 -1
- package/dist/{chunk-SEPN52AD.js → chunk-JBCC6C3M.js} +566 -307
- package/dist/{chunk-I7GTA3RX.js → chunk-JQRA724W.js} +1 -1
- package/dist/{chunk-WXYJHZCN.js → chunk-JVAEKTJ4.js} +2 -14
- package/dist/chunk-KPEMTBOP.js +26 -0
- package/dist/chunk-LF6J4YYN.js +301 -0
- package/dist/{chunk-LTT736P3.js → chunk-LLYVYRGJ.js} +228 -387
- package/dist/{chunk-6Z7JR4EW.js → chunk-M47BNEY2.js} +1897 -633
- package/dist/{chunk-KFT5AIIH.js → chunk-NDLC5GYK.js} +206 -24
- package/dist/{chunk-O2J2HDXA.js → chunk-OOTEXP47.js} +3 -8
- package/dist/chunk-QPVDCKYS.js +702 -0
- package/dist/{chunk-NHVDCYK5.js → chunk-R5YCJBHY.js} +71 -106
- package/dist/{chunk-XBYAFEOR.js → chunk-S33SO5XX.js} +1 -1
- package/dist/chunk-SZ7RFPXA.js +10 -0
- package/dist/chunk-TBBZWZQT.js +98 -0
- package/dist/chunk-TOEFN7DN.js +295 -0
- package/dist/{chunk-7ZEGFL42.js → chunk-WHJSYKNC.js} +6 -6
- package/dist/chunk-WOEJ6K7M.js +55 -0
- package/dist/{chunk-AU3OOREM.js → chunk-XSSWYPP5.js} +16 -3
- package/dist/contract-line-item-money-B0Z3Ogur.d.cts +43 -0
- package/dist/contract-line-item-money-B0Z3Ogur.d.ts +43 -0
- package/dist/discovery/index.d.cts +2 -2
- package/dist/discovery/index.d.ts +2 -2
- package/dist/discovery/index.js +6 -6
- package/dist/{discovery.scanner-9cMqF95o.d.cts → discovery.scanner-DXKc6JkV.d.cts} +1 -1
- package/dist/{discovery.scanner-9cMqF95o.d.ts → discovery.scanner-DXKc6JkV.d.ts} +1 -1
- package/dist/{enforce-quota.interceptor-Df0e3OWe.d.cts → enforce-quota.interceptor-BFyVdWe8.d.cts} +6 -6
- package/dist/{enforce-quota.interceptor-DW3etVyI.d.ts → enforce-quota.interceptor-GaBXboGx.d.ts} +6 -6
- package/dist/entitlement/index.d.cts +27 -4
- package/dist/entitlement/index.d.ts +27 -4
- package/dist/entitlement/index.js +13 -8
- package/dist/index.d.cts +209 -28
- package/dist/index.d.ts +209 -28
- package/dist/index.js +285 -163
- package/dist/issuer-identity.check-CBO0vcsq.d.cts +197 -0
- package/dist/issuer-identity.check-Cz9CEMkZ.d.ts +197 -0
- package/dist/{module-options-COSEb0VW.d.ts → module-options-DmQ3G4sZ.d.ts} +135 -41
- package/dist/{module-options-BG3MzQva.d.cts → module-options-qAOwinHR.d.cts} +135 -41
- package/dist/payment-callback.service-CR84Xw1Z.d.cts +125 -0
- package/dist/payment-callback.service-CR84Xw1Z.d.ts +125 -0
- package/dist/payments/index.cjs +4 -0
- package/dist/payments/index.d.cts +187 -0
- package/dist/payments/index.d.ts +187 -0
- package/dist/payments/index.js +141 -0
- package/dist/payments.module-BdUq9ot_.d.cts +57 -0
- package/dist/payments.module-CQGv_VG7.d.ts +57 -0
- package/dist/plan-catalog-source-DWe-BGY1.d.cts +21 -0
- package/dist/plan-catalog-source-DWe-BGY1.d.ts +21 -0
- package/dist/{plan-resolution-Cgo_TR1H.d.cts → plan-resolution-CwUy_OqC.d.cts} +14 -0
- package/dist/{plan-resolution-Cgo_TR1H.d.ts → plan-resolution-CwUy_OqC.d.ts} +14 -0
- package/dist/{plan-versions.service-DChhbK6h.d.ts → plan-versions.service-C0N-Em2W.d.ts} +38 -64
- package/dist/{plan-versions.service-TPEtbN0r.d.cts → plan-versions.service-DARLh-qI.d.cts} +38 -64
- package/dist/platform/index.d.cts +52 -23
- package/dist/platform/index.d.ts +52 -23
- package/dist/platform/index.js +57 -36
- package/dist/promo/index.d.cts +23 -8
- package/dist/promo/index.d.ts +23 -8
- package/dist/promo/index.js +15 -7
- package/dist/{promo.module-DtvIycpt.d.cts → promo.module-CKkECmU8.d.cts} +9 -3
- package/dist/{promo.module-Z8h1PNX-.d.ts → promo.module-DuIJdK3U.d.ts} +9 -3
- package/dist/promo.service-BYBu6dy3.d.cts +215 -0
- package/dist/promo.service-CFjfF0Xm.d.ts +215 -0
- package/dist/registration/index.d.cts +104 -47
- package/dist/registration/index.d.ts +104 -47
- package/dist/registration/index.js +15 -9
- package/dist/subscriber/index.cjs +4 -0
- package/dist/subscriber/index.d.cts +28 -0
- package/dist/subscriber/index.d.ts +28 -0
- package/dist/subscriber/index.js +16 -0
- package/dist/subscriber.service-5W6Gi-1B.d.cts +68 -0
- package/dist/subscriber.service-5W6Gi-1B.d.ts +68 -0
- package/dist/subscription-contract/index.d.cts +4 -2
- package/dist/subscription-contract/index.d.ts +4 -2
- package/dist/subscription-contract/index.js +13 -5
- package/dist/{subscription-contract.module-fDFdQ0jm.d.ts → subscription-contract.module-BfIRpc1o.d.ts} +8 -2
- package/dist/{subscription-contract.module-CEK8HS5-.d.cts → subscription-contract.module-DTMuUJYd.d.cts} +8 -2
- package/dist/{subscription-contract.service-hD87MKyg.d.cts → subscription-contract.service-CPZrTM9C.d.ts} +32 -3
- package/dist/{subscription-contract.service-hD87MKyg.d.ts → subscription-contract.service-CfNApLuP.d.cts} +32 -3
- package/dist/tenant-billing.controller-BLqbsONe.d.ts +812 -0
- package/dist/tenant-billing.controller-DKbAqTbM.d.cts +812 -0
- package/dist/tenant-billing.module-ClbjzocC.d.cts +364 -0
- package/dist/tenant-billing.module-DohgJJ2m.d.ts +364 -0
- package/dist/tenant-billing.tokens-G-1xOlMr.d.cts +129 -0
- package/dist/tenant-billing.tokens-G-1xOlMr.d.ts +129 -0
- package/dist/testing/index.d.cts +44 -17
- package/dist/testing/index.d.ts +44 -17
- package/dist/testing/index.js +189 -40
- package/package.json +24 -4
- package/dist/catalog.module-BDDO6iwq.d.cts +0 -103
- package/dist/catalog.module-BTnMsE6u.d.ts +0 -103
- package/dist/checkout-offer.module-CMtiQJTT.d.ts +0 -39
- package/dist/checkout-offer.module-CVpWrzbe.d.cts +0 -39
- package/dist/checkout-offer.service-BLOv2HOo.d.cts +0 -49
- package/dist/checkout-offer.service-BLOv2HOo.d.ts +0 -49
- package/dist/chunk-57V6ZTI6.js +0 -22
- package/dist/chunk-BSK6YBLI.js +0 -87
- package/dist/chunk-DQKCK7DX.js +0 -217
- package/dist/chunk-VXEYLNIB.js +0 -632
- package/dist/chunk-WUDYIPYH.js +0 -715
- package/dist/define-saasicat-DROBe-b1.d.ts +0 -69
- package/dist/define-saasicat-vGBCkFys.d.cts +0 -69
- package/dist/promo.service-BJbKAmw3.d.cts +0 -112
- package/dist/promo.service-BJbKAmw3.d.ts +0 -112
- package/dist/tenant-billing.controller-BQ67mlmI.d.ts +0 -438
- package/dist/tenant-billing.controller-DS8kB8a7.d.cts +0 -438
- package/dist/tenant-billing.module-BznC3XtW.d.cts +0 -299
- package/dist/tenant-billing.module-wUbsQSFG.d.ts +0 -299
- /package/dist/{chunk-2FR6ZL7R.js → chunk-BHZOH2DY.js} +0 -0
- /package/dist/{chunk-2SLTRKXC.js → chunk-DFOW3JVO.js} +0 -0
- /package/dist/{chunk-DBZGV3HC.js → chunk-DU56UKEH.js} +0 -0
- /package/dist/{chunk-OYNHOY45.js → chunk-GAUEVIAE.js} +0 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import { PlanCatalog, BillingCycle, PlanVersionRow, SubscriptionUsagePort, SubscriptionBundleRepository, BundleRepository, UsageSnapshotPort, TenantSubscriptionWritePort, SubscriptionContractRepository, SubscriberRepository } from '@saasicat/core';
|
|
2
|
+
import { Type, CanActivate, DynamicModule, ForwardReference, Provider } from '@nestjs/common';
|
|
3
|
+
import { P as ProviderSpec } from './di-CcNeq9v-.js';
|
|
4
|
+
import { T as TenantIdResolver, a as TrialProjectionPort, P as PendingPlanQueryPort, b as UserIdResolver, U as UserEmailResolver, A as AuditContextResolver } from './tenant-billing.tokens-G-1xOlMr.js';
|
|
5
|
+
import { P as PricedContractLineItem } from './contract-line-item-money-B0Z3Ogur.js';
|
|
6
|
+
|
|
7
|
+
declare const FEATURE_GUARD_MARKER: unique symbol;
|
|
8
|
+
/** A guard class or instance carrying the platform's marker. */
|
|
9
|
+
interface MarkedFeatureGuard {
|
|
10
|
+
[FEATURE_GUARD_MARKER]: true;
|
|
11
|
+
}
|
|
12
|
+
/** True when `target` is one of the platform's entitlement guards. */
|
|
13
|
+
declare function isPlatformFeatureGuard(target: unknown): boolean;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The variables a document may refer to; `process.env` in production.
|
|
17
|
+
*
|
|
18
|
+
* `null` means there is no environment to resolve against, and every reference
|
|
19
|
+
* is then refused. That is the case for a document that did not come from the
|
|
20
|
+
* installation's own file — an upload through the catalogue import — because
|
|
21
|
+
* resolving `${DATABASE_URL}` for whoever posts a YAML body would hand them the
|
|
22
|
+
* server's environment one variable at a time.
|
|
23
|
+
*/
|
|
24
|
+
type EnvironmentVariables = Readonly<Record<string, string | undefined>> | null;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Schema validation error bundling all Ajv errors — one call returns
|
|
28
|
+
* the full list, no round-trip editing needed.
|
|
29
|
+
*/
|
|
30
|
+
interface AjvErrorLike {
|
|
31
|
+
instancePath?: string;
|
|
32
|
+
message?: string;
|
|
33
|
+
schemaPath?: string;
|
|
34
|
+
/** `envVar` names the variable behind a reference that could not be resolved. */
|
|
35
|
+
params?: {
|
|
36
|
+
missingProperty?: string;
|
|
37
|
+
additionalProperty?: string;
|
|
38
|
+
envVar?: string;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* `Error.name` for a document that could not be read as a catalog at all.
|
|
43
|
+
*
|
|
44
|
+
* A name rather than a class because the other failure of this kind comes from
|
|
45
|
+
* the YAML parser and is not ours to subclass — one predicate has to cover
|
|
46
|
+
* both, and a name is what both can carry.
|
|
47
|
+
*/
|
|
48
|
+
declare const PLAN_CATALOG_UNREADABLE_ERROR = "PlanCatalogUnreadableError";
|
|
49
|
+
declare class PlanCatalogValidationError extends Error {
|
|
50
|
+
readonly source: string;
|
|
51
|
+
readonly errors: AjvErrorLike[];
|
|
52
|
+
constructor(source: string, errors: AjvErrorLike[]);
|
|
53
|
+
}
|
|
54
|
+
interface LoadPlanCatalogOptions {
|
|
55
|
+
/**
|
|
56
|
+
* Absolute path or relative path (resolved against CWD).
|
|
57
|
+
*/
|
|
58
|
+
path: string;
|
|
59
|
+
/**
|
|
60
|
+
* Optional: additional cross-field validations that the JSON schema
|
|
61
|
+
* cannot cover. Default: enable all (see validateConsistency).
|
|
62
|
+
*/
|
|
63
|
+
crossFieldChecks?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* The variables a `${NAME}` in the file may resolve against. Left out,
|
|
66
|
+
* `process.env`; a record of their own for tests; `null` to refuse every
|
|
67
|
+
* reference, the way the string variant does without one.
|
|
68
|
+
*/
|
|
69
|
+
env?: EnvironmentVariables;
|
|
70
|
+
}
|
|
71
|
+
interface LoadPlanCatalogFromStringOptions {
|
|
72
|
+
/** Names the document in error messages — a path, or where the text came from. */
|
|
73
|
+
source: string;
|
|
74
|
+
crossFieldChecks?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* The variables a `${NAME}` in the document may resolve against.
|
|
77
|
+
*
|
|
78
|
+
* Left out, every reference is refused: a document handed in as text did
|
|
79
|
+
* not come from the installation's own file, and the catalogue import is
|
|
80
|
+
* one such caller. Resolving references for an uploaded body would read
|
|
81
|
+
* the server's environment for whoever can post one.
|
|
82
|
+
*/
|
|
83
|
+
env?: EnvironmentVariables;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Loads + validates a saas.yaml file.
|
|
87
|
+
*
|
|
88
|
+
* Throws `PlanCatalogValidationError` on schema violations, cross-field
|
|
89
|
+
* violations, and references the environment cannot satisfy. Throws `Error`
|
|
90
|
+
* on IO/YAML parse errors.
|
|
91
|
+
*/
|
|
92
|
+
declare function loadPlanCatalogFromFile(opts: LoadPlanCatalogOptions): PlanCatalog;
|
|
93
|
+
/**
|
|
94
|
+
* The absolute path `loadPlanCatalogFromFile` read `catalog` from, or null for
|
|
95
|
+
* a catalogue assembled in code — or copied: a spread of the loaded object is
|
|
96
|
+
* a new object, and this cannot follow it.
|
|
97
|
+
*/
|
|
98
|
+
declare function catalogSource(catalog: PlanCatalog): string | null;
|
|
99
|
+
/**
|
|
100
|
+
* Variant for tests / in-memory loading: takes YAML content as a string,
|
|
101
|
+
* `source` is only for error logging.
|
|
102
|
+
*/
|
|
103
|
+
declare function loadPlanCatalogFromString(yamlContent: string, opts: LoadPlanCatalogFromStringOptions): PlanCatalog;
|
|
104
|
+
|
|
105
|
+
declare const SELF_SERVICE_BLOCKED_PLANS_TOKEN: unique symbol;
|
|
106
|
+
/**
|
|
107
|
+
* Bundle counterpart (#37): `bundleKeys` lists bundles that are not
|
|
108
|
+
* bookable via self-service (only via sales/special contract).
|
|
109
|
+
* Applies in `SubscriptionBundlesService.addBundleToSubscription`
|
|
110
|
+
* (enforcement, 422) and in the bundle preview (blocker indication).
|
|
111
|
+
*/
|
|
112
|
+
interface SelfServiceBlockedBundles {
|
|
113
|
+
bundleKeys?: string[];
|
|
114
|
+
}
|
|
115
|
+
declare const SELF_SERVICE_BLOCKED_BUNDLES_TOKEN: unique symbol;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Optional hook token: the platform `changePlan` path + the
|
|
119
|
+
* `PendingPlanMaterializationService` call the freeze after the plan mutation
|
|
120
|
+
* (analogous to `TrialProjectionPort`). Without a port nothing is frozen — the
|
|
121
|
+
* entitlements then stay catalog-/version-pinned as before.
|
|
122
|
+
*/
|
|
123
|
+
declare const CONTRACT_FREEZE_PORT_TOKEN: unique symbol;
|
|
124
|
+
/** Adapter token: consumer-specific bundle/version data access. */
|
|
125
|
+
declare const CONTRACT_FREEZE_SOURCE_PORT_TOKEN: unique symbol;
|
|
126
|
+
interface ContractFreezePort {
|
|
127
|
+
/**
|
|
128
|
+
* Refuses, with `SUBSCRIBER_REQUIRED`, a tenant that has no subscriber.
|
|
129
|
+
*
|
|
130
|
+
* A frozen contract names the party it is concluded with, and a freeze
|
|
131
|
+
* runs after the change that asks for it — a plan change already written,
|
|
132
|
+
* an add-on already booked. Asked first, the change is refused while
|
|
133
|
+
* nothing has moved; asked only by the freeze, the tenant would be on the
|
|
134
|
+
* new plan with the old contract still in force.
|
|
135
|
+
*/
|
|
136
|
+
assertPartyFor(tenantId: string): Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* Freezes the agreed service at `effectiveFrom` as the new active
|
|
139
|
+
* `SubscriptionContract` (supersedes the previous one). Non-fatal for the
|
|
140
|
+
* caller — the plan change is already persisted.
|
|
141
|
+
*/
|
|
142
|
+
freezeOnPlanChange(tenantId: string, newPlan: string, billingCycle: BillingCycle, effectiveFrom: Date,
|
|
143
|
+
/**
|
|
144
|
+
* When the subscription ends, or null while it runs on.
|
|
145
|
+
*
|
|
146
|
+
* A contract cannot outlive the subscription it froze, and a freeze
|
|
147
|
+
* happens AFTER a cancellation as well: a plan change on a cancelled
|
|
148
|
+
* subscription is allowed, and each one supersedes the capped contract
|
|
149
|
+
* with a fresh one. Without this the replacement is uncapped and the
|
|
150
|
+
* ending is lost — repaired once at the cancellation, undone by the
|
|
151
|
+
* next change.
|
|
152
|
+
*/
|
|
153
|
+
endsAt: Date | null): Promise<void>;
|
|
154
|
+
/**
|
|
155
|
+
* Ends the active contract at `effectiveAt`, with no successor.
|
|
156
|
+
*
|
|
157
|
+
* A frozen contract is the agreed service, and it cannot outlive the
|
|
158
|
+
* subscription that agreed to it. Without this the tenant's entitlements
|
|
159
|
+
* end on the date while the invoice side goes on reading an active
|
|
160
|
+
* agreement — two answers to "is this customer under contract", and the
|
|
161
|
+
* one that bills says yes.
|
|
162
|
+
*
|
|
163
|
+
* Same mechanic as the supersession above and deliberately not the same
|
|
164
|
+
* call: there is nothing to succeed it with.
|
|
165
|
+
*/
|
|
166
|
+
endOnCancellation(tenantId: string, effectiveAt: Date): Promise<void>;
|
|
167
|
+
}
|
|
168
|
+
/** Frozen bundle line items + their version ids (trace). */
|
|
169
|
+
interface ContractFreezeBundleSnapshot {
|
|
170
|
+
/**
|
|
171
|
+
* Priced in net, and nothing more — the platform records the gross, the
|
|
172
|
+
* currency and the tax rate, so a source never restates a setting it does
|
|
173
|
+
* not own, and every line of the contract carries its share of one tax
|
|
174
|
+
* computation rather than a rounding of its own.
|
|
175
|
+
*/
|
|
176
|
+
lineItems: PricedContractLineItem[];
|
|
177
|
+
bundleVersionIds: string[];
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Consumer-specific data access for the freeze: the plan version the tenant's
|
|
181
|
+
* subscription is bound to + booked bundles as contract line items. The generic
|
|
182
|
+
* freeze logic (plan line item, snapshot, contract assembly) lives in the
|
|
183
|
+
* platform `SubscriptionContractFreezeService`.
|
|
184
|
+
*/
|
|
185
|
+
interface ContractFreezeSourcePort {
|
|
186
|
+
/**
|
|
187
|
+
* The plan version the tenant's subscription is bound to — the row its
|
|
188
|
+
* `planVersionId` points at, whether or not a newer one is on sale — or
|
|
189
|
+
* `null` when the tenant has no subscription.
|
|
190
|
+
*
|
|
191
|
+
* The contract records this version's price, features and quotas. Not the
|
|
192
|
+
* version on sale now: a tenant who books an add-on after the operator
|
|
193
|
+
* published a successor keeps the version they bought (`SC-SUB-012`), and
|
|
194
|
+
* after a plan change the write has already bound the version it sold.
|
|
195
|
+
*/
|
|
196
|
+
findBoundPlanVersion(tenantId: string): Promise<PlanVersionRow | null>;
|
|
197
|
+
/**
|
|
198
|
+
* The tenant's active (non-terminated) bundle bookings as line items,
|
|
199
|
+
* priced in net. Apps without a bundle schema return empty lists.
|
|
200
|
+
*
|
|
201
|
+
* `cycle` is the **plan's** rhythm, not the bookings'. A tenant on a yearly
|
|
202
|
+
* plan may hold monthly add-ons, so a source prices each booking in the
|
|
203
|
+
* rhythm that booking was made in and says which one that is on the line's
|
|
204
|
+
* own `billingCycle`. Pricing every line in the plan's rhythm puts a figure
|
|
205
|
+
* on the contract that nobody is charged, and the contract is the evidence
|
|
206
|
+
* of what was agreed.
|
|
207
|
+
*/
|
|
208
|
+
loadBookedBundles(tenantId: string, cycle: 'monthly' | 'yearly'): Promise<ContractFreezeBundleSnapshot>;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
interface SubscriptionBundleControllerOptions {
|
|
212
|
+
extraGuards?: Array<Type<CanActivate>>;
|
|
213
|
+
/**
|
|
214
|
+
* Auth guard list analogous to `TenantBillingModule.forRoot.authGuards`.
|
|
215
|
+
* Without this list, `ComposedTenantAuthGuard` blocks fail-closed.
|
|
216
|
+
*/
|
|
217
|
+
authGuards?: ProviderSpec<ReadonlyArray<CanActivate>>;
|
|
218
|
+
/**
|
|
219
|
+
* Usage port for subscription lookup and current plan compatibility.
|
|
220
|
+
* If not set, an imported module must export the token.
|
|
221
|
+
*/
|
|
222
|
+
subscriptionUsagePort?: ProviderSpec<SubscriptionUsagePort>;
|
|
223
|
+
/** Optional tenant-ID resolver. Default: `req.user.tenantId`. */
|
|
224
|
+
tenantIdResolver?: TenantIdResolver;
|
|
225
|
+
}
|
|
226
|
+
interface SubscriptionBundleModuleOptions {
|
|
227
|
+
subscriptionBundleRepository: ProviderSpec<SubscriptionBundleRepository>;
|
|
228
|
+
/**
|
|
229
|
+
* Optional — if omitted, `BUNDLE_REPOSITORY_TOKEN` is expected via
|
|
230
|
+
* default inject from the DI scope (typically via an imported
|
|
231
|
+
* `CatalogModule`).
|
|
232
|
+
*/
|
|
233
|
+
bundleRepository?: ProviderSpec<BundleRepository>;
|
|
234
|
+
/**
|
|
235
|
+
* Default minimum term (months). Default = **0** — no commitment, so an
|
|
236
|
+
* add-on can be cancelled to its own period end. Set one to bind.
|
|
237
|
+
*/
|
|
238
|
+
defaultMinimumTermMonths?: number;
|
|
239
|
+
/**
|
|
240
|
+
* Self-service policy (#37): bundles that are only bookable via sales.
|
|
241
|
+
* Applies in `addBundleToSubscription` (422 BUNDLE_NOT_SELF_SERVICE)
|
|
242
|
+
* and in the preview (blocker).
|
|
243
|
+
*/
|
|
244
|
+
selfServiceBlockedBundles?: SelfServiceBlockedBundles;
|
|
245
|
+
/**
|
|
246
|
+
* If set: the tenant self-service controller is mounted at
|
|
247
|
+
* `/billing/subscription-bundles` (GET/POST/DELETE). The
|
|
248
|
+
* `extraGuards` are applied in addition to the platform default
|
|
249
|
+
* `ComposedTenantAuthGuard` (role/MFA guards).
|
|
250
|
+
*
|
|
251
|
+
* Prerequisite: the consumer has already registered `TenantBillingModule`
|
|
252
|
+
* — the controller needs `SUBSCRIPTION_USAGE_PORT_TOKEN`
|
|
253
|
+
* + `TENANT_ID_RESOLVER_TOKEN` in the same DI scope.
|
|
254
|
+
*/
|
|
255
|
+
controller?: SubscriptionBundleControllerOptions;
|
|
256
|
+
imports?: Array<Type<unknown> | DynamicModule | Promise<DynamicModule> | ForwardReference>;
|
|
257
|
+
extraProviders?: Provider[];
|
|
258
|
+
global?: boolean;
|
|
259
|
+
}
|
|
260
|
+
declare class SubscriptionBundleModule {
|
|
261
|
+
static forRoot(options: SubscriptionBundleModuleOptions): DynamicModule;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
interface TenantBillingModuleOptions {
|
|
265
|
+
/**
|
|
266
|
+
* App guards in the order in which they should be executed
|
|
267
|
+
* (e.g. `[JwtAuthGuard, TenantGuard]`). The platform combines them via
|
|
268
|
+
* `ComposedTenantAuthGuard`. At least one guard is mandatory —
|
|
269
|
+
* missing configuration leads to 403 (safe default).
|
|
270
|
+
*
|
|
271
|
+
* Variant 1: array of guard instances (e.g. via factory provider).
|
|
272
|
+
* Variant 2: Pick<FactoryProvider, 'useFactory' | 'inject'> — apps
|
|
273
|
+
* pass their guard classes through via `inject` and the factory builds
|
|
274
|
+
* the array.
|
|
275
|
+
*/
|
|
276
|
+
authGuards: ProviderSpec<ReadonlyArray<CanActivate>>;
|
|
277
|
+
/** Adapter to the subscription display form (`GET /billing/usage`). */
|
|
278
|
+
subscriptionUsagePort: ProviderSpec<SubscriptionUsagePort>;
|
|
279
|
+
/** Adapter to usage counters of all `quotaKeys`. */
|
|
280
|
+
usageSnapshotPort: ProviderSpec<UsageSnapshotPort>;
|
|
281
|
+
/** Adapter for plan/add-on mutations (phase C). */
|
|
282
|
+
subscriptionWritePort: ProviderSpec<TenantSubscriptionWritePort>;
|
|
283
|
+
/**
|
|
284
|
+
* Optional adapter that provides the projected new trial end of a change
|
|
285
|
+
* (app trial logic, e.g. carry-over). Without a port,
|
|
286
|
+
* `PlanChangePreviewDto.projectedTrialEndsAt` stays `null`.
|
|
287
|
+
*/
|
|
288
|
+
trialProjectionPort?: ProviderSpec<TrialProjectionPort>;
|
|
289
|
+
/**
|
|
290
|
+
* Optional adapter for the tenant's bundle bookings.
|
|
291
|
+
*
|
|
292
|
+
* The plan-change preview reads it for one rule: a bundle may run in a
|
|
293
|
+
* shorter rhythm than its plan, never a longer one, so a move to a shorter
|
|
294
|
+
* cycle is refused while an add-on with a longer one is still active.
|
|
295
|
+
* `SubscriptionBundleModule` exports the same token, but it is a sibling
|
|
296
|
+
* import rather than an ancestor — its exports do not reach this module's
|
|
297
|
+
* providers, so without this option the rule resolves to "no bookings" and
|
|
298
|
+
* silently allows the move it exists to prevent.
|
|
299
|
+
*/
|
|
300
|
+
subscriptionBundleRepository?: ProviderSpec<SubscriptionBundleRepository>;
|
|
301
|
+
/**
|
|
302
|
+
* Optional adapter that provides due scheduled plan changes (#19). If it is
|
|
303
|
+
* passed, the module registers the `PendingPlanMaterializationService`
|
|
304
|
+
* (exported) — the consumer triggers it via its own cron. Without a
|
|
305
|
+
* port, the materialization stays disabled (lazy resolution as before).
|
|
306
|
+
*/
|
|
307
|
+
pendingPlanQueryPort?: ProviderSpec<PendingPlanQueryPort>;
|
|
308
|
+
/**
|
|
309
|
+
* Optional contract freeze hook (#18). If it is configured, the
|
|
310
|
+
* platform `changePlan` path (non-TRIAL) AND the materialization freeze
|
|
311
|
+
* the agreed service after the plan mutation as a `SubscriptionContract`
|
|
312
|
+
* (analogous to `trialProjectionPort`). Consumer-specific is only the
|
|
313
|
+
* bundle/version data access (`sourcePort`); the contract logic is
|
|
314
|
+
* generic. `subscriptionContractRepository` is the same repo that also goes
|
|
315
|
+
* to `EntitlementModule.forRoot` — the freeze needs it in its own scope.
|
|
316
|
+
*/
|
|
317
|
+
contractFreeze?: {
|
|
318
|
+
sourcePort: ProviderSpec<ContractFreezeSourcePort>;
|
|
319
|
+
subscriptionContractRepository: ProviderSpec<SubscriptionContractRepository>;
|
|
320
|
+
/**
|
|
321
|
+
* The parties a frozen contract is between. A plan change or a booking
|
|
322
|
+
* for a tenant without a subscriber is refused before anything changes.
|
|
323
|
+
*/
|
|
324
|
+
subscriberRepository: ProviderSpec<SubscriberRepository>;
|
|
325
|
+
};
|
|
326
|
+
/** Optional tenant ID resolver. Default: `req.user.tenantId`. */
|
|
327
|
+
tenantIdResolver?: TenantIdResolver;
|
|
328
|
+
/** Optional user ID resolver. Default: `req.user.sub ?? req.user.id`. */
|
|
329
|
+
userIdResolver?: UserIdResolver;
|
|
330
|
+
/**
|
|
331
|
+
* Optional email resolver for the audit log path. Default: `req.user.email`.
|
|
332
|
+
* If the consumer JWT does not include the email, the resolver can
|
|
333
|
+
* return `null` — the audit log then uses `'unknown'`.
|
|
334
|
+
*/
|
|
335
|
+
userEmailResolver?: UserEmailResolver;
|
|
336
|
+
/**
|
|
337
|
+
* Optional audit context resolver (session ID / trace ID). Default:
|
|
338
|
+
* `req.headers['x-session-id']` or `'tenant-self-service'`.
|
|
339
|
+
*/
|
|
340
|
+
auditContextResolver?: AuditContextResolver;
|
|
341
|
+
/**
|
|
342
|
+
* Modules whose providers must be visible within this module.
|
|
343
|
+
* Typical use case: the app's own `AuthModule`, so that the `JwtAuthGuard`
|
|
344
|
+
* is injectable in the `authGuards` factory. Without this entry NestJS throws
|
|
345
|
+
* `UnknownDependenciesException` for JwtAuthGuard.
|
|
346
|
+
*/
|
|
347
|
+
imports?: Array<Type<unknown> | DynamicModule | Promise<DynamicModule> | ForwardReference>;
|
|
348
|
+
/**
|
|
349
|
+
* Additional providers that are registered in the DynamicModule itself —
|
|
350
|
+
* typically: the adapter classes referenced in `inject:[Adapter]` lists
|
|
351
|
+
* (e.g. `PrismaSubscriptionUsagePort`). NestJS 11.1.19
|
|
352
|
+
* resolves factory inject tokens only in the DynamicModule's own scope.
|
|
353
|
+
*/
|
|
354
|
+
extraProviders?: Provider[];
|
|
355
|
+
/** Additional providers this module makes visible to importing modules. */
|
|
356
|
+
extraExports?: NonNullable<DynamicModule['exports']>;
|
|
357
|
+
/** Register the module globally — default `false`. */
|
|
358
|
+
global?: boolean;
|
|
359
|
+
}
|
|
360
|
+
declare class TenantBillingModule {
|
|
361
|
+
static forRoot(options: TenantBillingModuleOptions): DynamicModule;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
export { type AjvErrorLike as A, CONTRACT_FREEZE_PORT_TOKEN as C, type EnvironmentVariables as E, FEATURE_GUARD_MARKER as F, type LoadPlanCatalogOptions as L, type MarkedFeatureGuard as M, PLAN_CATALOG_UNREADABLE_ERROR as P, type SubscriptionBundleModuleOptions as S, type TenantBillingModuleOptions as T, type SubscriptionBundleControllerOptions as a, CONTRACT_FREEZE_SOURCE_PORT_TOKEN as b, type ContractFreezeBundleSnapshot as c, type ContractFreezePort as d, type ContractFreezeSourcePort as e, type LoadPlanCatalogFromStringOptions as f, PlanCatalogValidationError as g, SELF_SERVICE_BLOCKED_BUNDLES_TOKEN as h, SELF_SERVICE_BLOCKED_PLANS_TOKEN as i, type SelfServiceBlockedBundles as j, SubscriptionBundleModule as k, TenantBillingModule as l, catalogSource as m, isPlatformFeatureGuard as n, loadPlanCatalogFromFile as o, loadPlanCatalogFromString as p };
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { CanActivate } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* List of all auth guards the platform controller should iterate in order
|
|
5
|
+
* (analogous to `@UseGuards(JwtAuthGuard, TenantGuard, RolesGuard)`).
|
|
6
|
+
* Provided via `forRoot.authGuards` — can be an array of existing instances
|
|
7
|
+
* or a factory provider.
|
|
8
|
+
*/
|
|
9
|
+
declare const TENANT_AUTH_GUARDS_TOKEN: unique symbol;
|
|
10
|
+
/**
|
|
11
|
+
* Optional guards deciding who holds the billing permission (`BillingPermissionGuard`).
|
|
12
|
+
* Without them the tenant's administrator holds it.
|
|
13
|
+
*/
|
|
14
|
+
declare const BILLING_PERMISSION_GUARDS_TOKEN: unique symbol;
|
|
15
|
+
/**
|
|
16
|
+
* Resolver function `(req) => string` that extracts the `tenantId` from the
|
|
17
|
+
* request. Default: `req.user.tenantId`.
|
|
18
|
+
*/
|
|
19
|
+
declare const TENANT_ID_RESOLVER_TOKEN: unique symbol;
|
|
20
|
+
/**
|
|
21
|
+
* Resolver function `(req) => string` that extracts the `userId` from the
|
|
22
|
+
* request. Default: `req.user.sub ?? req.user.id`.
|
|
23
|
+
*/
|
|
24
|
+
declare const USER_ID_RESOLVER_TOKEN: unique symbol;
|
|
25
|
+
/**
|
|
26
|
+
* Resolver function `(req) => string` that extracts the user email from the
|
|
27
|
+
* request. Optional — used by the audit-log path to build the AdminActor
|
|
28
|
+
* (`{userId, email, source: 'web', context}`). Default: `req.user.email`.
|
|
29
|
+
* If the consumer's JWT does not carry an email, the resolver can return
|
|
30
|
+
* `null` — the audit-log path then falls back to `'unknown'`.
|
|
31
|
+
*/
|
|
32
|
+
declare const USER_EMAIL_RESOLVER_TOKEN: unique symbol;
|
|
33
|
+
/**
|
|
34
|
+
* Resolver function `(req) => string` that extracts an audit context from the
|
|
35
|
+
* request (e.g. session ID, trace ID). Default: `req.headers['x-session-id']`
|
|
36
|
+
* or `'tenant-self-service'`.
|
|
37
|
+
*/
|
|
38
|
+
declare const AUDIT_CONTEXT_RESOLVER_TOKEN: unique symbol;
|
|
39
|
+
/** Adapter token: consumer's `SubscriptionUsagePort` implementation. */
|
|
40
|
+
declare const SUBSCRIPTION_USAGE_PORT_TOKEN: unique symbol;
|
|
41
|
+
/** Adapter token: consumer's `UsageSnapshotPort` implementation. */
|
|
42
|
+
declare const USAGE_SNAPSHOT_PORT_TOKEN: unique symbol;
|
|
43
|
+
/** Adapter token: consumer's `TenantSubscriptionWritePort` implementation. */
|
|
44
|
+
declare const SUBSCRIPTION_WRITE_PORT_TOKEN: unique symbol;
|
|
45
|
+
type TenantIdResolver = (req: unknown) => string | null | undefined;
|
|
46
|
+
type UserIdResolver = (req: unknown) => string | null | undefined;
|
|
47
|
+
type UserEmailResolver = (req: unknown) => string | null | undefined;
|
|
48
|
+
type AuditContextResolver = (req: unknown) => string | null | undefined;
|
|
49
|
+
type AuthGuardList = ReadonlyArray<CanActivate>;
|
|
50
|
+
/**
|
|
51
|
+
* Optional adapter token: projects the new trial end of a change
|
|
52
|
+
* (app-specific trial logic, e.g. carry-over of the remaining time). Without a
|
|
53
|
+
* port, `PlanChangePreviewDto.projectedTrialEndsAt` stays `null` and the wizard
|
|
54
|
+
* falls back to the current trial end.
|
|
55
|
+
*/
|
|
56
|
+
declare const TRIAL_PROJECTION_PORT_TOKEN: unique symbol;
|
|
57
|
+
interface TrialProjectionInput {
|
|
58
|
+
/** Current plan key of the subscription. */
|
|
59
|
+
currentPlan: string;
|
|
60
|
+
/** Target plan key of the change. */
|
|
61
|
+
targetPlan: string;
|
|
62
|
+
/** Current trial end (null = no trial). */
|
|
63
|
+
currentTrialEndsAt: Date | null;
|
|
64
|
+
/** Subscription status (e.g. 'TRIAL'/'ACTIVE'). */
|
|
65
|
+
status: string;
|
|
66
|
+
now: Date;
|
|
67
|
+
}
|
|
68
|
+
interface TrialProjectionPort {
|
|
69
|
+
/**
|
|
70
|
+
* Projected new trial end after the change. `null` if nothing changes or
|
|
71
|
+
* the target package does not support a trial.
|
|
72
|
+
*/
|
|
73
|
+
projectTrialEndsAt(input: TrialProjectionInput): Promise<Date | null>;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Optional adapter token: provides due scheduled plan changes for the
|
|
77
|
+
* `PendingPlanMaterializationService`. Without a port, the service is not
|
|
78
|
+
* registered (materialization is opt-in).
|
|
79
|
+
*/
|
|
80
|
+
declare const PENDING_PLAN_QUERY_PORT_TOKEN: unique symbol;
|
|
81
|
+
/** A due scheduled plan change — minimal for materialization. */
|
|
82
|
+
interface DuePendingPlanChange {
|
|
83
|
+
tenantId: string;
|
|
84
|
+
/** Target plan key of the scheduled change (`pendingPlan`). */
|
|
85
|
+
pendingPlan: string;
|
|
86
|
+
/** Target cycle (`pendingBillingCycle`); `null` → default MONTHLY. */
|
|
87
|
+
pendingBillingCycle: string | null;
|
|
88
|
+
/**
|
|
89
|
+
* The subscription's cancellation, so materialization can decline.
|
|
90
|
+
*
|
|
91
|
+
* A change scheduled before the customer cancelled comes due anyway, and
|
|
92
|
+
* applying it to a subscription that has already ended restarts the billing
|
|
93
|
+
* period and runs the plan-change follow-up hooks on a contract that is
|
|
94
|
+
* over. Required, and required together, for the reason the same pair is
|
|
95
|
+
* required on `SubscriptionRecord`: a record that omits them cannot answer
|
|
96
|
+
* the question, and the silent answer is to go ahead.
|
|
97
|
+
*
|
|
98
|
+
* A cancellation that has NOT landed does not decline anything — a customer
|
|
99
|
+
* who bought a further period by cancelling late may still choose the plan
|
|
100
|
+
* they spend it on.
|
|
101
|
+
*/
|
|
102
|
+
canceledAt: Date | null;
|
|
103
|
+
canceledEffectiveAt: Date | null;
|
|
104
|
+
}
|
|
105
|
+
interface PendingPlanQueryPort {
|
|
106
|
+
/**
|
|
107
|
+
* Returns all subscriptions with a due scheduled plan change:
|
|
108
|
+
* `pendingPlan != null AND pendingEffectiveAt <= now AND status != 'TRIAL'`.
|
|
109
|
+
* TRIAL is excluded — there the trial lifecycle drives the transition.
|
|
110
|
+
*
|
|
111
|
+
* Cancelled subscriptions are NOT excluded by the query: whether a landed
|
|
112
|
+
* cancellation declines the change is a decision, and it is taken above
|
|
113
|
+
* this port, from the two dates on the record.
|
|
114
|
+
*/
|
|
115
|
+
findDuePendingPlanChanges(now: Date): Promise<DuePendingPlanChange[]>;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Days before a term ends after which a cancellation lands one period later.
|
|
119
|
+
*
|
|
120
|
+
* Zero by default, and that is the value to leave it at unless someone asked
|
|
121
|
+
* for otherwise: with no window there is no door to be shut out of, and a
|
|
122
|
+
* customer who cancels on the last day of their year is out at the end of it.
|
|
123
|
+
* Where a window is configured the cut is hard — four days late costs a year on
|
|
124
|
+
* a yearly term — which is why `/plan` states the date before the confirmation
|
|
125
|
+
* rather than after it.
|
|
126
|
+
*/
|
|
127
|
+
declare const CANCELLATION_NOTICE_DAYS_TOKEN: unique symbol;
|
|
128
|
+
|
|
129
|
+
export { type AuditContextResolver as A, BILLING_PERMISSION_GUARDS_TOKEN as B, CANCELLATION_NOTICE_DAYS_TOKEN as C, type DuePendingPlanChange as D, type PendingPlanQueryPort as P, SUBSCRIPTION_USAGE_PORT_TOKEN as S, type TenantIdResolver as T, type UserEmailResolver as U, type TrialProjectionPort as a, type UserIdResolver as b, AUDIT_CONTEXT_RESOLVER_TOKEN as c, type AuthGuardList as d, PENDING_PLAN_QUERY_PORT_TOKEN as e, SUBSCRIPTION_WRITE_PORT_TOKEN as f, TENANT_AUTH_GUARDS_TOKEN as g, TENANT_ID_RESOLVER_TOKEN as h, TRIAL_PROJECTION_PORT_TOKEN as i, type TrialProjectionInput as j, USAGE_SNAPSHOT_PORT_TOKEN as k, USER_EMAIL_RESOLVER_TOKEN as l, USER_ID_RESOLVER_TOKEN as m };
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { CanActivate } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* List of all auth guards the platform controller should iterate in order
|
|
5
|
+
* (analogous to `@UseGuards(JwtAuthGuard, TenantGuard, RolesGuard)`).
|
|
6
|
+
* Provided via `forRoot.authGuards` — can be an array of existing instances
|
|
7
|
+
* or a factory provider.
|
|
8
|
+
*/
|
|
9
|
+
declare const TENANT_AUTH_GUARDS_TOKEN: unique symbol;
|
|
10
|
+
/**
|
|
11
|
+
* Optional guards deciding who holds the billing permission (`BillingPermissionGuard`).
|
|
12
|
+
* Without them the tenant's administrator holds it.
|
|
13
|
+
*/
|
|
14
|
+
declare const BILLING_PERMISSION_GUARDS_TOKEN: unique symbol;
|
|
15
|
+
/**
|
|
16
|
+
* Resolver function `(req) => string` that extracts the `tenantId` from the
|
|
17
|
+
* request. Default: `req.user.tenantId`.
|
|
18
|
+
*/
|
|
19
|
+
declare const TENANT_ID_RESOLVER_TOKEN: unique symbol;
|
|
20
|
+
/**
|
|
21
|
+
* Resolver function `(req) => string` that extracts the `userId` from the
|
|
22
|
+
* request. Default: `req.user.sub ?? req.user.id`.
|
|
23
|
+
*/
|
|
24
|
+
declare const USER_ID_RESOLVER_TOKEN: unique symbol;
|
|
25
|
+
/**
|
|
26
|
+
* Resolver function `(req) => string` that extracts the user email from the
|
|
27
|
+
* request. Optional — used by the audit-log path to build the AdminActor
|
|
28
|
+
* (`{userId, email, source: 'web', context}`). Default: `req.user.email`.
|
|
29
|
+
* If the consumer's JWT does not carry an email, the resolver can return
|
|
30
|
+
* `null` — the audit-log path then falls back to `'unknown'`.
|
|
31
|
+
*/
|
|
32
|
+
declare const USER_EMAIL_RESOLVER_TOKEN: unique symbol;
|
|
33
|
+
/**
|
|
34
|
+
* Resolver function `(req) => string` that extracts an audit context from the
|
|
35
|
+
* request (e.g. session ID, trace ID). Default: `req.headers['x-session-id']`
|
|
36
|
+
* or `'tenant-self-service'`.
|
|
37
|
+
*/
|
|
38
|
+
declare const AUDIT_CONTEXT_RESOLVER_TOKEN: unique symbol;
|
|
39
|
+
/** Adapter token: consumer's `SubscriptionUsagePort` implementation. */
|
|
40
|
+
declare const SUBSCRIPTION_USAGE_PORT_TOKEN: unique symbol;
|
|
41
|
+
/** Adapter token: consumer's `UsageSnapshotPort` implementation. */
|
|
42
|
+
declare const USAGE_SNAPSHOT_PORT_TOKEN: unique symbol;
|
|
43
|
+
/** Adapter token: consumer's `TenantSubscriptionWritePort` implementation. */
|
|
44
|
+
declare const SUBSCRIPTION_WRITE_PORT_TOKEN: unique symbol;
|
|
45
|
+
type TenantIdResolver = (req: unknown) => string | null | undefined;
|
|
46
|
+
type UserIdResolver = (req: unknown) => string | null | undefined;
|
|
47
|
+
type UserEmailResolver = (req: unknown) => string | null | undefined;
|
|
48
|
+
type AuditContextResolver = (req: unknown) => string | null | undefined;
|
|
49
|
+
type AuthGuardList = ReadonlyArray<CanActivate>;
|
|
50
|
+
/**
|
|
51
|
+
* Optional adapter token: projects the new trial end of a change
|
|
52
|
+
* (app-specific trial logic, e.g. carry-over of the remaining time). Without a
|
|
53
|
+
* port, `PlanChangePreviewDto.projectedTrialEndsAt` stays `null` and the wizard
|
|
54
|
+
* falls back to the current trial end.
|
|
55
|
+
*/
|
|
56
|
+
declare const TRIAL_PROJECTION_PORT_TOKEN: unique symbol;
|
|
57
|
+
interface TrialProjectionInput {
|
|
58
|
+
/** Current plan key of the subscription. */
|
|
59
|
+
currentPlan: string;
|
|
60
|
+
/** Target plan key of the change. */
|
|
61
|
+
targetPlan: string;
|
|
62
|
+
/** Current trial end (null = no trial). */
|
|
63
|
+
currentTrialEndsAt: Date | null;
|
|
64
|
+
/** Subscription status (e.g. 'TRIAL'/'ACTIVE'). */
|
|
65
|
+
status: string;
|
|
66
|
+
now: Date;
|
|
67
|
+
}
|
|
68
|
+
interface TrialProjectionPort {
|
|
69
|
+
/**
|
|
70
|
+
* Projected new trial end after the change. `null` if nothing changes or
|
|
71
|
+
* the target package does not support a trial.
|
|
72
|
+
*/
|
|
73
|
+
projectTrialEndsAt(input: TrialProjectionInput): Promise<Date | null>;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Optional adapter token: provides due scheduled plan changes for the
|
|
77
|
+
* `PendingPlanMaterializationService`. Without a port, the service is not
|
|
78
|
+
* registered (materialization is opt-in).
|
|
79
|
+
*/
|
|
80
|
+
declare const PENDING_PLAN_QUERY_PORT_TOKEN: unique symbol;
|
|
81
|
+
/** A due scheduled plan change — minimal for materialization. */
|
|
82
|
+
interface DuePendingPlanChange {
|
|
83
|
+
tenantId: string;
|
|
84
|
+
/** Target plan key of the scheduled change (`pendingPlan`). */
|
|
85
|
+
pendingPlan: string;
|
|
86
|
+
/** Target cycle (`pendingBillingCycle`); `null` → default MONTHLY. */
|
|
87
|
+
pendingBillingCycle: string | null;
|
|
88
|
+
/**
|
|
89
|
+
* The subscription's cancellation, so materialization can decline.
|
|
90
|
+
*
|
|
91
|
+
* A change scheduled before the customer cancelled comes due anyway, and
|
|
92
|
+
* applying it to a subscription that has already ended restarts the billing
|
|
93
|
+
* period and runs the plan-change follow-up hooks on a contract that is
|
|
94
|
+
* over. Required, and required together, for the reason the same pair is
|
|
95
|
+
* required on `SubscriptionRecord`: a record that omits them cannot answer
|
|
96
|
+
* the question, and the silent answer is to go ahead.
|
|
97
|
+
*
|
|
98
|
+
* A cancellation that has NOT landed does not decline anything — a customer
|
|
99
|
+
* who bought a further period by cancelling late may still choose the plan
|
|
100
|
+
* they spend it on.
|
|
101
|
+
*/
|
|
102
|
+
canceledAt: Date | null;
|
|
103
|
+
canceledEffectiveAt: Date | null;
|
|
104
|
+
}
|
|
105
|
+
interface PendingPlanQueryPort {
|
|
106
|
+
/**
|
|
107
|
+
* Returns all subscriptions with a due scheduled plan change:
|
|
108
|
+
* `pendingPlan != null AND pendingEffectiveAt <= now AND status != 'TRIAL'`.
|
|
109
|
+
* TRIAL is excluded — there the trial lifecycle drives the transition.
|
|
110
|
+
*
|
|
111
|
+
* Cancelled subscriptions are NOT excluded by the query: whether a landed
|
|
112
|
+
* cancellation declines the change is a decision, and it is taken above
|
|
113
|
+
* this port, from the two dates on the record.
|
|
114
|
+
*/
|
|
115
|
+
findDuePendingPlanChanges(now: Date): Promise<DuePendingPlanChange[]>;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Days before a term ends after which a cancellation lands one period later.
|
|
119
|
+
*
|
|
120
|
+
* Zero by default, and that is the value to leave it at unless someone asked
|
|
121
|
+
* for otherwise: with no window there is no door to be shut out of, and a
|
|
122
|
+
* customer who cancels on the last day of their year is out at the end of it.
|
|
123
|
+
* Where a window is configured the cut is hard — four days late costs a year on
|
|
124
|
+
* a yearly term — which is why `/plan` states the date before the confirmation
|
|
125
|
+
* rather than after it.
|
|
126
|
+
*/
|
|
127
|
+
declare const CANCELLATION_NOTICE_DAYS_TOKEN: unique symbol;
|
|
128
|
+
|
|
129
|
+
export { type AuditContextResolver as A, BILLING_PERMISSION_GUARDS_TOKEN as B, CANCELLATION_NOTICE_DAYS_TOKEN as C, type DuePendingPlanChange as D, type PendingPlanQueryPort as P, SUBSCRIPTION_USAGE_PORT_TOKEN as S, type TenantIdResolver as T, type UserEmailResolver as U, type TrialProjectionPort as a, type UserIdResolver as b, AUDIT_CONTEXT_RESOLVER_TOKEN as c, type AuthGuardList as d, PENDING_PLAN_QUERY_PORT_TOKEN as e, SUBSCRIPTION_WRITE_PORT_TOKEN as f, TENANT_AUTH_GUARDS_TOKEN as g, TENANT_ID_RESOLVER_TOKEN as h, TRIAL_PROJECTION_PORT_TOKEN as i, type TrialProjectionInput as j, USAGE_SNAPSHOT_PORT_TOKEN as k, USER_EMAIL_RESOLVER_TOKEN as l, USER_ID_RESOLVER_TOKEN as m };
|