@saasicat/nest 0.2.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.
- package/LICENSE +202 -0
- package/README.md +53 -0
- package/dist/admin/index.cjs +1283 -0
- package/dist/admin/index.d.cts +225 -0
- package/dist/admin/index.d.ts +225 -0
- package/dist/admin/index.js +58 -0
- package/dist/admin-audit.service-9IqXMlZm.d.cts +30 -0
- package/dist/admin-audit.service-9IqXMlZm.d.ts +30 -0
- package/dist/admin-manifest.config-DyrQNT7M.d.cts +10 -0
- package/dist/admin-manifest.config-DyrQNT7M.d.ts +10 -0
- package/dist/aggregation-CJ3qQf92.d.cts +288 -0
- package/dist/aggregation-Dvz9e8X2.d.ts +288 -0
- package/dist/billing/index.cjs +5205 -0
- package/dist/billing/index.d.cts +1326 -0
- package/dist/billing/index.d.ts +1326 -0
- package/dist/billing/index.js +197 -0
- package/dist/catalog/index.cjs +6449 -0
- package/dist/catalog/index.d.cts +1073 -0
- package/dist/catalog/index.d.ts +1073 -0
- package/dist/catalog/index.js +158 -0
- package/dist/checkout-offer/index.cjs +807 -0
- package/dist/checkout-offer/index.d.cts +120 -0
- package/dist/checkout-offer/index.d.ts +120 -0
- package/dist/checkout-offer/index.js +20 -0
- package/dist/chunk-2FR6ZL7R.js +0 -0
- package/dist/chunk-2PNX2QL2.js +347 -0
- package/dist/chunk-2VNZYRDV.js +326 -0
- package/dist/chunk-7MLGHWKO.js +12 -0
- package/dist/chunk-ABB2CRAS.js +31 -0
- package/dist/chunk-AJ3EPELS.js +157 -0
- package/dist/chunk-E56W4U2P.js +90 -0
- package/dist/chunk-HRU3CP57.js +71 -0
- package/dist/chunk-L4SUWH5B.js +14 -0
- package/dist/chunk-M6CZQYPY.js +314 -0
- package/dist/chunk-MDIZUVIK.js +87 -0
- package/dist/chunk-MV6LW5UB.js +217 -0
- package/dist/chunk-NYLON2VC.js +6 -0
- package/dist/chunk-P6MYZMXQ.js +951 -0
- package/dist/chunk-Q53N43LQ.js +13 -0
- package/dist/chunk-QJVPRD3R.js +573 -0
- package/dist/chunk-RIGLQ4BV.js +3367 -0
- package/dist/chunk-SABTXESR.js +24 -0
- package/dist/chunk-SHUYVCID.js +6 -0
- package/dist/chunk-SYEVGPFC.js +686 -0
- package/dist/chunk-V7HUZ6UQ.js +5923 -0
- package/dist/chunk-WPUVJRIN.js +29 -0
- package/dist/chunk-XCZ3TTFK.js +22 -0
- package/dist/chunk-XSGU2Y2O.js +1611 -0
- package/dist/chunk-YNEW3L63.js +190 -0
- package/dist/chunk-YXGHPILG.js +614 -0
- package/dist/chunk-ZL4LLE2Z.js +465 -0
- package/dist/di-CcNeq9v-.d.cts +16 -0
- package/dist/di-CcNeq9v-.d.ts +16 -0
- package/dist/discovery/index.cjs +588 -0
- package/dist/discovery/index.d.cts +299 -0
- package/dist/discovery/index.d.ts +299 -0
- package/dist/discovery/index.js +49 -0
- package/dist/discovery.scanner-CUYLKlYT.d.cts +59 -0
- package/dist/discovery.scanner-CUYLKlYT.d.ts +59 -0
- package/dist/entitlement/index.cjs +805 -0
- package/dist/entitlement/index.d.cts +60 -0
- package/dist/entitlement/index.d.ts +60 -0
- package/dist/entitlement/index.js +57 -0
- package/dist/index.cjs +15918 -0
- package/dist/index.d.cts +85 -0
- package/dist/index.d.ts +85 -0
- package/dist/index.js +956 -0
- package/dist/plan-resolution-CFCoUkrE.d.cts +41 -0
- package/dist/plan-resolution-CFCoUkrE.d.ts +41 -0
- package/dist/platform/index.cjs +2772 -0
- package/dist/platform/index.d.cts +118 -0
- package/dist/platform/index.d.ts +118 -0
- package/dist/platform/index.js +37 -0
- package/dist/promo/index.cjs +995 -0
- package/dist/promo/index.d.cts +121 -0
- package/dist/promo/index.d.ts +121 -0
- package/dist/promo/index.js +58 -0
- package/dist/registration/index.cjs +1687 -0
- package/dist/registration/index.d.cts +444 -0
- package/dist/registration/index.d.ts +444 -0
- package/dist/registration/index.js +82 -0
- package/dist/saas-platform.module-CgZ2omgg.d.cts +180 -0
- package/dist/saas-platform.module-DT9TnCzk.d.ts +180 -0
- package/dist/service-DX8KbGXl.d.cts +109 -0
- package/dist/service-DX8KbGXl.d.ts +109 -0
- package/dist/subscription-contract/index.cjs +471 -0
- package/dist/subscription-contract/index.d.cts +17 -0
- package/dist/subscription-contract/index.d.ts +17 -0
- package/dist/subscription-contract/index.js +21 -0
- package/dist/subscription-contract.service--cm47ZJJ.d.cts +39 -0
- package/dist/subscription-contract.service--cm47ZJJ.d.ts +39 -0
- package/dist/testing/index.cjs +3942 -0
- package/dist/testing/index.d.cts +279 -0
- package/dist/testing/index.d.ts +279 -0
- package/dist/testing/index.js +1195 -0
- package/package.json +185 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { BundleRepository, BundleRow, BundleVersionRow, BundleListFilter, CreateBundleData, UpdateBundleData, CreateBundleVersionDraftData, UpdateBundleVersionDraftData, VersionChange, BusinessTypeRepository, BusinessTypeRow, BusinessTypeVersionRow, BusinessTypeListFilter, CreateBusinessTypeData, UpdateBusinessTypeData, CreateBusinessTypeVersionDraftData, UpdateBusinessTypeVersionDraftData, MarketingProjectionRepository, MarketingProjectionRow, MarketingProjectionFilter, CreateMarketingProjectionData, UpdateMarketingProjectionData, PlanRepository, PlanRow, PlanVersionRow, PlanListFilter, CreatePlanData, UpdatePlanData, CreatePlanVersionDraftData, UpdatePlanVersionDraftData, PlanVersionRepository, PlanVersionRecord, TransactionContext, SubscriptionBundleRepository, SubscriptionBundleRecord, CreateSubscriptionBundleData, CancelSubscriptionBundleData, SubscriptionContractRepository, SubscriptionContractFilter, SubscriptionContractRecord, CreateSubscriptionContractData, TerminateSubscriptionContractData, SubscriptionRepository, SubscriptionRecord, TransactionRunner, PlanCatalog, QuotaProvider } from '@saasicat/types';
|
|
2
|
+
import { Type, DynamicModule } from '@nestjs/common';
|
|
3
|
+
import { S as SaasPlatformModuleOptions } from '../saas-platform.module-CgZ2omgg.cjs';
|
|
4
|
+
import '../di-CcNeq9v-.cjs';
|
|
5
|
+
import '../admin-manifest.config-DyrQNT7M.cjs';
|
|
6
|
+
import '../discovery.scanner-CUYLKlYT.cjs';
|
|
7
|
+
import '@nestjs/core';
|
|
8
|
+
import '../plan-resolution-CFCoUkrE.cjs';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* In-memory FakeSubscriptionRepository — stores subscriptions by
|
|
12
|
+
* `tenantId` and returns them as a read snapshot. Lock semantics are not
|
|
13
|
+
* emulated (`findByTenantIdLocked` delegates to `findByTenantId`).
|
|
14
|
+
*
|
|
15
|
+
* Test setup:
|
|
16
|
+
*
|
|
17
|
+
* const repo = new FakeSubscriptionRepository();
|
|
18
|
+
* repo.set({ id: 'sub-1', tenantId: 't1', plan: 'STANDARD', ... });
|
|
19
|
+
*/
|
|
20
|
+
declare class FakeSubscriptionRepository implements SubscriptionRepository {
|
|
21
|
+
private readonly byTenantId;
|
|
22
|
+
/**
|
|
23
|
+
* Test stubs for the `countBy*` editability methods. Tests set them
|
|
24
|
+
* directly; default 0 (= "no subscription binds the version" →
|
|
25
|
+
* published-but-future versions are editable when they are latest-in-chain
|
|
26
|
+
* and validFrom lies in the future).
|
|
27
|
+
*/
|
|
28
|
+
private readonly planVersionCounts;
|
|
29
|
+
private readonly bundleVersionCounts;
|
|
30
|
+
set(record: SubscriptionRecord): void;
|
|
31
|
+
delete(tenantId: string): void;
|
|
32
|
+
clear(): void;
|
|
33
|
+
/** Test helper: force the number of subscriptions for a PlanVersion. */
|
|
34
|
+
setPlanVersionCount(planVersionId: string, count: number): void;
|
|
35
|
+
/** Test helper: force the number of subscriptions for a BundleVersion. */
|
|
36
|
+
setBundleVersionCount(bundleVersionId: string, count: number): void;
|
|
37
|
+
findByTenantId(tenantId: string): Promise<SubscriptionRecord | null>;
|
|
38
|
+
findByTenantIdLocked(tenantId: string, _tx: TransactionContext): Promise<SubscriptionRecord | null>;
|
|
39
|
+
countByPlanVersionId(planVersionId: string): Promise<number>;
|
|
40
|
+
countByBundleVersionId(bundleVersionId: string): Promise<number>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* In-memory FakeSubscriptionBundleRepository — stores
|
|
44
|
+
* `subscription_bundles` entries by id. `add` / `cancel` are
|
|
45
|
+
* fail-fast (no re-cancel); the minimum-term default comes from the service
|
|
46
|
+
* (not implemented yet today — tests set the field explicitly).
|
|
47
|
+
*
|
|
48
|
+
* Test setup:
|
|
49
|
+
*
|
|
50
|
+
* const repo = new FakeSubscriptionBundleRepository();
|
|
51
|
+
* const row = await repo.add({ subscriptionId: 's1',
|
|
52
|
+
* bundleVersionId: 'bv1', startedAt: new Date() });
|
|
53
|
+
*/
|
|
54
|
+
declare class FakeSubscriptionBundleRepository implements SubscriptionBundleRepository {
|
|
55
|
+
private readonly byId;
|
|
56
|
+
private nextId;
|
|
57
|
+
private now;
|
|
58
|
+
clear(): void;
|
|
59
|
+
listBySubscription(subscriptionId: string): Promise<SubscriptionBundleRecord[]>;
|
|
60
|
+
findById(subscriptionBundleId: string): Promise<SubscriptionBundleRecord | null>;
|
|
61
|
+
listActiveBySubscription(subscriptionId: string, asOf?: Date): Promise<SubscriptionBundleRecord[]>;
|
|
62
|
+
add(data: CreateSubscriptionBundleData): Promise<SubscriptionBundleRecord>;
|
|
63
|
+
cancel(subscriptionBundleId: string, data: CancelSubscriptionBundleData): Promise<SubscriptionBundleRecord>;
|
|
64
|
+
reactivate(subscriptionBundleId: string): Promise<SubscriptionBundleRecord>;
|
|
65
|
+
countActiveByBundleVersionId(bundleVersionId: string, asOf?: Date): Promise<number>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* In-memory FakeSubscriptionContractRepository — models the V3
|
|
69
|
+
* append-only contract schema. `create` clones snapshots deeply enough for
|
|
70
|
+
* immutability tests; `terminate` only changes status/effectiveUntil.
|
|
71
|
+
*/
|
|
72
|
+
declare class FakeSubscriptionContractRepository implements SubscriptionContractRepository {
|
|
73
|
+
private readonly byId;
|
|
74
|
+
private nextContractId;
|
|
75
|
+
private nextLineItemId;
|
|
76
|
+
clear(): void;
|
|
77
|
+
list(filter: SubscriptionContractFilter): Promise<SubscriptionContractRecord[]>;
|
|
78
|
+
findById(contractId: string): Promise<SubscriptionContractRecord | null>;
|
|
79
|
+
findActiveByTenantId(tenantId: string, asOf?: Date): Promise<SubscriptionContractRecord | null>;
|
|
80
|
+
create(data: CreateSubscriptionContractData): Promise<SubscriptionContractRecord>;
|
|
81
|
+
terminate(contractId: string, data: TerminateSubscriptionContractData): Promise<SubscriptionContractRecord>;
|
|
82
|
+
private isActiveAt;
|
|
83
|
+
private cloneRecord;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* In-memory FakePlanVersionRepository — stores PlanVersions per `planId`
|
|
87
|
+
* and returns the most recently set one as "latest live".
|
|
88
|
+
*/
|
|
89
|
+
declare class FakePlanVersionRepository implements PlanVersionRepository {
|
|
90
|
+
private readonly byPlanId;
|
|
91
|
+
set(record: PlanVersionRecord): void;
|
|
92
|
+
clear(): void;
|
|
93
|
+
findLatestLive(planId: string, _tx?: TransactionContext): Promise<PlanVersionRecord | null>;
|
|
94
|
+
findActive(planId: string, _asOf?: Date, _tx?: TransactionContext): Promise<PlanVersionRecord | null>;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Fake TransactionRunner for tests — runs the callback function directly
|
|
98
|
+
* and passes a sentinel `tx` value. No rollback behavior, no atomic
|
|
99
|
+
* guarantees — tests that need to check the tx semantics should run
|
|
100
|
+
* against a real DB.
|
|
101
|
+
*/
|
|
102
|
+
declare class FakeTransactionRunner implements TransactionRunner {
|
|
103
|
+
/** Sentinel value passed through as the TransactionContext. */
|
|
104
|
+
static readonly TX_SENTINEL: unique symbol;
|
|
105
|
+
runCount: number;
|
|
106
|
+
run<T>(fn: (tx: TransactionContext) => Promise<T>): Promise<T>;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* In-memory implementation of `BundleRepository` for tests of the
|
|
110
|
+
* `BundlesService`. Implements the partial unique constraints (max. 1
|
|
111
|
+
* draft per bundle) and the publish semantics (previous live gets
|
|
112
|
+
* superseded) as pure-JS logic.
|
|
113
|
+
*/
|
|
114
|
+
declare class FakeBundleRepository implements BundleRepository {
|
|
115
|
+
private readonly bundles;
|
|
116
|
+
private readonly versions;
|
|
117
|
+
private nextId;
|
|
118
|
+
private genId;
|
|
119
|
+
private nowIso;
|
|
120
|
+
seedBundle(row: BundleRow): void;
|
|
121
|
+
seedVersion(row: BundleVersionRow): void;
|
|
122
|
+
clear(): void;
|
|
123
|
+
list(filter: BundleListFilter): Promise<BundleRow[]>;
|
|
124
|
+
findById(bundleId: string): Promise<BundleRow | null>;
|
|
125
|
+
findByKey(projectKey: string, bundleKey: string): Promise<BundleRow | null>;
|
|
126
|
+
create(data: CreateBundleData): Promise<BundleRow>;
|
|
127
|
+
update(bundleId: string, data: UpdateBundleData): Promise<BundleRow>;
|
|
128
|
+
softDelete(bundleId: string): Promise<void>;
|
|
129
|
+
listVersions(bundleId: string): Promise<BundleVersionRow[]>;
|
|
130
|
+
findVersionById(versionId: string): Promise<BundleVersionRow | null>;
|
|
131
|
+
findCurrentDraft(bundleId: string): Promise<BundleVersionRow | null>;
|
|
132
|
+
findLatestLive(bundleId: string): Promise<BundleVersionRow | null>;
|
|
133
|
+
createDraft(data: CreateBundleVersionDraftData): Promise<BundleVersionRow>;
|
|
134
|
+
updateDraft(versionId: string, data: UpdateBundleVersionDraftData): Promise<BundleVersionRow>;
|
|
135
|
+
deleteDraft(versionId: string): Promise<void>;
|
|
136
|
+
publishDraft(versionId: string, publishMeta: {
|
|
137
|
+
publishedByUserId: string | null;
|
|
138
|
+
publishedChanges: VersionChange[];
|
|
139
|
+
nonRegressive: boolean;
|
|
140
|
+
validFrom: Date;
|
|
141
|
+
validUntil: Date | null;
|
|
142
|
+
}): Promise<BundleVersionRow>;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* In-memory implementation of `BusinessTypeRepository`. Keeps the
|
|
146
|
+
* BusinessTypeBundle junction directly inside the BusinessTypeVersionRow objects
|
|
147
|
+
* (analogous to the wire-format convention).
|
|
148
|
+
*/
|
|
149
|
+
declare class FakeBusinessTypeRepository implements BusinessTypeRepository {
|
|
150
|
+
private readonly types;
|
|
151
|
+
private readonly versions;
|
|
152
|
+
private nextId;
|
|
153
|
+
private genId;
|
|
154
|
+
private nowIso;
|
|
155
|
+
seedBusinessType(row: BusinessTypeRow): void;
|
|
156
|
+
seedVersion(row: BusinessTypeVersionRow): void;
|
|
157
|
+
clear(): void;
|
|
158
|
+
list(filter: BusinessTypeListFilter): Promise<BusinessTypeRow[]>;
|
|
159
|
+
findById(businessTypeId: string): Promise<BusinessTypeRow | null>;
|
|
160
|
+
findByKey(projectKey: string, businessTypeKey: string): Promise<BusinessTypeRow | null>;
|
|
161
|
+
create(data: CreateBusinessTypeData): Promise<BusinessTypeRow>;
|
|
162
|
+
update(businessTypeId: string, data: UpdateBusinessTypeData): Promise<BusinessTypeRow>;
|
|
163
|
+
softDelete(businessTypeId: string): Promise<void>;
|
|
164
|
+
listVersions(businessTypeId: string): Promise<BusinessTypeVersionRow[]>;
|
|
165
|
+
findVersionById(versionId: string): Promise<BusinessTypeVersionRow | null>;
|
|
166
|
+
findCurrentDraft(businessTypeId: string): Promise<BusinessTypeVersionRow | null>;
|
|
167
|
+
findLatestLive(businessTypeId: string): Promise<BusinessTypeVersionRow | null>;
|
|
168
|
+
createDraft(data: CreateBusinessTypeVersionDraftData): Promise<BusinessTypeVersionRow>;
|
|
169
|
+
updateDraft(versionId: string, data: UpdateBusinessTypeVersionDraftData): Promise<BusinessTypeVersionRow>;
|
|
170
|
+
publishDraft(versionId: string, publishMeta: {
|
|
171
|
+
publishedByUserId: string | null;
|
|
172
|
+
publishedChanges: VersionChange[];
|
|
173
|
+
nonRegressive: boolean;
|
|
174
|
+
}): Promise<BusinessTypeVersionRow>;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* In-memory implementation of `MarketingProjectionRepository`. Enforces
|
|
178
|
+
* uniqueness over (`targetType`, `targetVersionId`, `locale`) like the
|
|
179
|
+
* DB schema.
|
|
180
|
+
*/
|
|
181
|
+
declare class FakeMarketingProjectionRepository implements MarketingProjectionRepository {
|
|
182
|
+
private readonly rows;
|
|
183
|
+
private nextId;
|
|
184
|
+
private genId;
|
|
185
|
+
private nowIso;
|
|
186
|
+
seed(row: MarketingProjectionRow): void;
|
|
187
|
+
clear(): void;
|
|
188
|
+
list(filter: MarketingProjectionFilter): Promise<MarketingProjectionRow[]>;
|
|
189
|
+
findById(id: string): Promise<MarketingProjectionRow | null>;
|
|
190
|
+
findByTarget(targetType: string, targetVersionId: string, locale: string): Promise<MarketingProjectionRow | null>;
|
|
191
|
+
create(data: CreateMarketingProjectionData): Promise<MarketingProjectionRow>;
|
|
192
|
+
update(id: string, data: UpdateMarketingProjectionData): Promise<MarketingProjectionRow>;
|
|
193
|
+
delete(id: string): Promise<void>;
|
|
194
|
+
}
|
|
195
|
+
declare class FakePlanRepository implements PlanRepository {
|
|
196
|
+
private readonly plans;
|
|
197
|
+
private readonly versions;
|
|
198
|
+
private nextId;
|
|
199
|
+
private nextVersionId;
|
|
200
|
+
private genId;
|
|
201
|
+
private genVersionId;
|
|
202
|
+
private nowIso;
|
|
203
|
+
seed(row: PlanRow): void;
|
|
204
|
+
seedVersion(row: PlanVersionRow): void;
|
|
205
|
+
clear(): void;
|
|
206
|
+
list(filter: PlanListFilter): Promise<PlanRow[]>;
|
|
207
|
+
findById(planId: string): Promise<PlanRow | null>;
|
|
208
|
+
findByKey(projectKey: string, planKey: string): Promise<PlanRow | null>;
|
|
209
|
+
create(data: CreatePlanData): Promise<PlanRow>;
|
|
210
|
+
update(planId: string, data: UpdatePlanData): Promise<PlanRow>;
|
|
211
|
+
softDelete(planId: string): Promise<void>;
|
|
212
|
+
hardDelete(planId: string): Promise<void>;
|
|
213
|
+
listVersions(planKey: string): Promise<PlanVersionRow[]>;
|
|
214
|
+
findVersionById(versionId: string): Promise<PlanVersionRow | null>;
|
|
215
|
+
findCurrentDraft(planKey: string): Promise<PlanVersionRow | null>;
|
|
216
|
+
findLatestLivePlanVersion(planKey: string): Promise<PlanVersionRow | null>;
|
|
217
|
+
findActivePlanVersion(planKey: string, asOf?: Date): Promise<PlanVersionRow | null>;
|
|
218
|
+
createPlanVersionDraft(data: CreatePlanVersionDraftData): Promise<PlanVersionRow>;
|
|
219
|
+
updatePlanVersionDraft(versionId: string, data: UpdatePlanVersionDraftData): Promise<PlanVersionRow>;
|
|
220
|
+
publishPlanVersionDraft(versionId: string, publishMeta: {
|
|
221
|
+
publishedByUserId: string | null;
|
|
222
|
+
publishedChanges: VersionChange[];
|
|
223
|
+
nonRegressive: boolean;
|
|
224
|
+
validFrom: Date;
|
|
225
|
+
validUntil: Date | null;
|
|
226
|
+
}): Promise<PlanVersionRow>;
|
|
227
|
+
deletePlanVersionDraft(versionId: string): Promise<void>;
|
|
228
|
+
terminate(versionId: string, endsAt: Date): Promise<PlanVersionRow>;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** No-op stub for `MfaPort`. */
|
|
232
|
+
declare class StubMfaPort {
|
|
233
|
+
private secrets;
|
|
234
|
+
getSecret(userId: string): Promise<string | null>;
|
|
235
|
+
setSecret(userId: string, secret: string | null): Promise<void>;
|
|
236
|
+
isEnabled(userId: string): Promise<boolean>;
|
|
237
|
+
}
|
|
238
|
+
/** No-op stub for `AuditPort`. Keeps calls as a list for asserts. */
|
|
239
|
+
declare class StubAuditPort {
|
|
240
|
+
readonly calls: Array<{
|
|
241
|
+
actor: unknown;
|
|
242
|
+
entity: string;
|
|
243
|
+
entityId: string;
|
|
244
|
+
action: string;
|
|
245
|
+
changes?: Record<string, unknown>;
|
|
246
|
+
}>;
|
|
247
|
+
write(input: {
|
|
248
|
+
actor: unknown;
|
|
249
|
+
entity: string;
|
|
250
|
+
entityId: string;
|
|
251
|
+
action: string;
|
|
252
|
+
changes?: Record<string, unknown>;
|
|
253
|
+
}): Promise<void>;
|
|
254
|
+
}
|
|
255
|
+
/** No-op stub for `RlsBypassPort` — calls the callback without wrapping. */
|
|
256
|
+
declare class StubRlsBypassPort {
|
|
257
|
+
runWithBypass<T>(fn: () => Promise<T>): Promise<T>;
|
|
258
|
+
}
|
|
259
|
+
interface CreateSaasPlatformTestModuleOptions {
|
|
260
|
+
planCatalog: PlanCatalog;
|
|
261
|
+
/** Default `'starter'` if the catalog has at least one plan. */
|
|
262
|
+
defaultPlanId?: string;
|
|
263
|
+
/** QuotaProvider classes for the `EnforceQuotaInterceptor`. */
|
|
264
|
+
quotaProviders?: Array<Type<QuotaProvider>>;
|
|
265
|
+
/** Overrides — if the test needs a different adapter. */
|
|
266
|
+
overrides?: Partial<{
|
|
267
|
+
mfa: SaasPlatformModuleOptions['adapters']['mfa'];
|
|
268
|
+
audit: SaasPlatformModuleOptions['adapters']['audit'];
|
|
269
|
+
rlsBypass: SaasPlatformModuleOptions['adapters']['rlsBypass'];
|
|
270
|
+
}>;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Returns a `DynamicModule` that sets up the SaasPlatformModule with stub
|
|
274
|
+
* adapters + the given PlanCatalog in static entitlement mode.
|
|
275
|
+
* Tests can drop this into `Test.createTestingModule({ imports: [...] })`.
|
|
276
|
+
*/
|
|
277
|
+
declare function createSaasPlatformTestModule(options: CreateSaasPlatformTestModuleOptions): DynamicModule;
|
|
278
|
+
|
|
279
|
+
export { type CreateSaasPlatformTestModuleOptions, FakeBundleRepository, FakeBusinessTypeRepository, FakeMarketingProjectionRepository, FakePlanRepository, FakePlanVersionRepository, FakeSubscriptionBundleRepository, FakeSubscriptionContractRepository, FakeSubscriptionRepository, FakeTransactionRunner, StubAuditPort, StubMfaPort, StubRlsBypassPort, createSaasPlatformTestModule };
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { BundleRepository, BundleRow, BundleVersionRow, BundleListFilter, CreateBundleData, UpdateBundleData, CreateBundleVersionDraftData, UpdateBundleVersionDraftData, VersionChange, BusinessTypeRepository, BusinessTypeRow, BusinessTypeVersionRow, BusinessTypeListFilter, CreateBusinessTypeData, UpdateBusinessTypeData, CreateBusinessTypeVersionDraftData, UpdateBusinessTypeVersionDraftData, MarketingProjectionRepository, MarketingProjectionRow, MarketingProjectionFilter, CreateMarketingProjectionData, UpdateMarketingProjectionData, PlanRepository, PlanRow, PlanVersionRow, PlanListFilter, CreatePlanData, UpdatePlanData, CreatePlanVersionDraftData, UpdatePlanVersionDraftData, PlanVersionRepository, PlanVersionRecord, TransactionContext, SubscriptionBundleRepository, SubscriptionBundleRecord, CreateSubscriptionBundleData, CancelSubscriptionBundleData, SubscriptionContractRepository, SubscriptionContractFilter, SubscriptionContractRecord, CreateSubscriptionContractData, TerminateSubscriptionContractData, SubscriptionRepository, SubscriptionRecord, TransactionRunner, PlanCatalog, QuotaProvider } from '@saasicat/types';
|
|
2
|
+
import { Type, DynamicModule } from '@nestjs/common';
|
|
3
|
+
import { S as SaasPlatformModuleOptions } from '../saas-platform.module-DT9TnCzk.js';
|
|
4
|
+
import '../di-CcNeq9v-.js';
|
|
5
|
+
import '../admin-manifest.config-DyrQNT7M.js';
|
|
6
|
+
import '../discovery.scanner-CUYLKlYT.js';
|
|
7
|
+
import '@nestjs/core';
|
|
8
|
+
import '../plan-resolution-CFCoUkrE.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* In-memory FakeSubscriptionRepository — stores subscriptions by
|
|
12
|
+
* `tenantId` and returns them as a read snapshot. Lock semantics are not
|
|
13
|
+
* emulated (`findByTenantIdLocked` delegates to `findByTenantId`).
|
|
14
|
+
*
|
|
15
|
+
* Test setup:
|
|
16
|
+
*
|
|
17
|
+
* const repo = new FakeSubscriptionRepository();
|
|
18
|
+
* repo.set({ id: 'sub-1', tenantId: 't1', plan: 'STANDARD', ... });
|
|
19
|
+
*/
|
|
20
|
+
declare class FakeSubscriptionRepository implements SubscriptionRepository {
|
|
21
|
+
private readonly byTenantId;
|
|
22
|
+
/**
|
|
23
|
+
* Test stubs for the `countBy*` editability methods. Tests set them
|
|
24
|
+
* directly; default 0 (= "no subscription binds the version" →
|
|
25
|
+
* published-but-future versions are editable when they are latest-in-chain
|
|
26
|
+
* and validFrom lies in the future).
|
|
27
|
+
*/
|
|
28
|
+
private readonly planVersionCounts;
|
|
29
|
+
private readonly bundleVersionCounts;
|
|
30
|
+
set(record: SubscriptionRecord): void;
|
|
31
|
+
delete(tenantId: string): void;
|
|
32
|
+
clear(): void;
|
|
33
|
+
/** Test helper: force the number of subscriptions for a PlanVersion. */
|
|
34
|
+
setPlanVersionCount(planVersionId: string, count: number): void;
|
|
35
|
+
/** Test helper: force the number of subscriptions for a BundleVersion. */
|
|
36
|
+
setBundleVersionCount(bundleVersionId: string, count: number): void;
|
|
37
|
+
findByTenantId(tenantId: string): Promise<SubscriptionRecord | null>;
|
|
38
|
+
findByTenantIdLocked(tenantId: string, _tx: TransactionContext): Promise<SubscriptionRecord | null>;
|
|
39
|
+
countByPlanVersionId(planVersionId: string): Promise<number>;
|
|
40
|
+
countByBundleVersionId(bundleVersionId: string): Promise<number>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* In-memory FakeSubscriptionBundleRepository — stores
|
|
44
|
+
* `subscription_bundles` entries by id. `add` / `cancel` are
|
|
45
|
+
* fail-fast (no re-cancel); the minimum-term default comes from the service
|
|
46
|
+
* (not implemented yet today — tests set the field explicitly).
|
|
47
|
+
*
|
|
48
|
+
* Test setup:
|
|
49
|
+
*
|
|
50
|
+
* const repo = new FakeSubscriptionBundleRepository();
|
|
51
|
+
* const row = await repo.add({ subscriptionId: 's1',
|
|
52
|
+
* bundleVersionId: 'bv1', startedAt: new Date() });
|
|
53
|
+
*/
|
|
54
|
+
declare class FakeSubscriptionBundleRepository implements SubscriptionBundleRepository {
|
|
55
|
+
private readonly byId;
|
|
56
|
+
private nextId;
|
|
57
|
+
private now;
|
|
58
|
+
clear(): void;
|
|
59
|
+
listBySubscription(subscriptionId: string): Promise<SubscriptionBundleRecord[]>;
|
|
60
|
+
findById(subscriptionBundleId: string): Promise<SubscriptionBundleRecord | null>;
|
|
61
|
+
listActiveBySubscription(subscriptionId: string, asOf?: Date): Promise<SubscriptionBundleRecord[]>;
|
|
62
|
+
add(data: CreateSubscriptionBundleData): Promise<SubscriptionBundleRecord>;
|
|
63
|
+
cancel(subscriptionBundleId: string, data: CancelSubscriptionBundleData): Promise<SubscriptionBundleRecord>;
|
|
64
|
+
reactivate(subscriptionBundleId: string): Promise<SubscriptionBundleRecord>;
|
|
65
|
+
countActiveByBundleVersionId(bundleVersionId: string, asOf?: Date): Promise<number>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* In-memory FakeSubscriptionContractRepository — models the V3
|
|
69
|
+
* append-only contract schema. `create` clones snapshots deeply enough for
|
|
70
|
+
* immutability tests; `terminate` only changes status/effectiveUntil.
|
|
71
|
+
*/
|
|
72
|
+
declare class FakeSubscriptionContractRepository implements SubscriptionContractRepository {
|
|
73
|
+
private readonly byId;
|
|
74
|
+
private nextContractId;
|
|
75
|
+
private nextLineItemId;
|
|
76
|
+
clear(): void;
|
|
77
|
+
list(filter: SubscriptionContractFilter): Promise<SubscriptionContractRecord[]>;
|
|
78
|
+
findById(contractId: string): Promise<SubscriptionContractRecord | null>;
|
|
79
|
+
findActiveByTenantId(tenantId: string, asOf?: Date): Promise<SubscriptionContractRecord | null>;
|
|
80
|
+
create(data: CreateSubscriptionContractData): Promise<SubscriptionContractRecord>;
|
|
81
|
+
terminate(contractId: string, data: TerminateSubscriptionContractData): Promise<SubscriptionContractRecord>;
|
|
82
|
+
private isActiveAt;
|
|
83
|
+
private cloneRecord;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* In-memory FakePlanVersionRepository — stores PlanVersions per `planId`
|
|
87
|
+
* and returns the most recently set one as "latest live".
|
|
88
|
+
*/
|
|
89
|
+
declare class FakePlanVersionRepository implements PlanVersionRepository {
|
|
90
|
+
private readonly byPlanId;
|
|
91
|
+
set(record: PlanVersionRecord): void;
|
|
92
|
+
clear(): void;
|
|
93
|
+
findLatestLive(planId: string, _tx?: TransactionContext): Promise<PlanVersionRecord | null>;
|
|
94
|
+
findActive(planId: string, _asOf?: Date, _tx?: TransactionContext): Promise<PlanVersionRecord | null>;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Fake TransactionRunner for tests — runs the callback function directly
|
|
98
|
+
* and passes a sentinel `tx` value. No rollback behavior, no atomic
|
|
99
|
+
* guarantees — tests that need to check the tx semantics should run
|
|
100
|
+
* against a real DB.
|
|
101
|
+
*/
|
|
102
|
+
declare class FakeTransactionRunner implements TransactionRunner {
|
|
103
|
+
/** Sentinel value passed through as the TransactionContext. */
|
|
104
|
+
static readonly TX_SENTINEL: unique symbol;
|
|
105
|
+
runCount: number;
|
|
106
|
+
run<T>(fn: (tx: TransactionContext) => Promise<T>): Promise<T>;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* In-memory implementation of `BundleRepository` for tests of the
|
|
110
|
+
* `BundlesService`. Implements the partial unique constraints (max. 1
|
|
111
|
+
* draft per bundle) and the publish semantics (previous live gets
|
|
112
|
+
* superseded) as pure-JS logic.
|
|
113
|
+
*/
|
|
114
|
+
declare class FakeBundleRepository implements BundleRepository {
|
|
115
|
+
private readonly bundles;
|
|
116
|
+
private readonly versions;
|
|
117
|
+
private nextId;
|
|
118
|
+
private genId;
|
|
119
|
+
private nowIso;
|
|
120
|
+
seedBundle(row: BundleRow): void;
|
|
121
|
+
seedVersion(row: BundleVersionRow): void;
|
|
122
|
+
clear(): void;
|
|
123
|
+
list(filter: BundleListFilter): Promise<BundleRow[]>;
|
|
124
|
+
findById(bundleId: string): Promise<BundleRow | null>;
|
|
125
|
+
findByKey(projectKey: string, bundleKey: string): Promise<BundleRow | null>;
|
|
126
|
+
create(data: CreateBundleData): Promise<BundleRow>;
|
|
127
|
+
update(bundleId: string, data: UpdateBundleData): Promise<BundleRow>;
|
|
128
|
+
softDelete(bundleId: string): Promise<void>;
|
|
129
|
+
listVersions(bundleId: string): Promise<BundleVersionRow[]>;
|
|
130
|
+
findVersionById(versionId: string): Promise<BundleVersionRow | null>;
|
|
131
|
+
findCurrentDraft(bundleId: string): Promise<BundleVersionRow | null>;
|
|
132
|
+
findLatestLive(bundleId: string): Promise<BundleVersionRow | null>;
|
|
133
|
+
createDraft(data: CreateBundleVersionDraftData): Promise<BundleVersionRow>;
|
|
134
|
+
updateDraft(versionId: string, data: UpdateBundleVersionDraftData): Promise<BundleVersionRow>;
|
|
135
|
+
deleteDraft(versionId: string): Promise<void>;
|
|
136
|
+
publishDraft(versionId: string, publishMeta: {
|
|
137
|
+
publishedByUserId: string | null;
|
|
138
|
+
publishedChanges: VersionChange[];
|
|
139
|
+
nonRegressive: boolean;
|
|
140
|
+
validFrom: Date;
|
|
141
|
+
validUntil: Date | null;
|
|
142
|
+
}): Promise<BundleVersionRow>;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* In-memory implementation of `BusinessTypeRepository`. Keeps the
|
|
146
|
+
* BusinessTypeBundle junction directly inside the BusinessTypeVersionRow objects
|
|
147
|
+
* (analogous to the wire-format convention).
|
|
148
|
+
*/
|
|
149
|
+
declare class FakeBusinessTypeRepository implements BusinessTypeRepository {
|
|
150
|
+
private readonly types;
|
|
151
|
+
private readonly versions;
|
|
152
|
+
private nextId;
|
|
153
|
+
private genId;
|
|
154
|
+
private nowIso;
|
|
155
|
+
seedBusinessType(row: BusinessTypeRow): void;
|
|
156
|
+
seedVersion(row: BusinessTypeVersionRow): void;
|
|
157
|
+
clear(): void;
|
|
158
|
+
list(filter: BusinessTypeListFilter): Promise<BusinessTypeRow[]>;
|
|
159
|
+
findById(businessTypeId: string): Promise<BusinessTypeRow | null>;
|
|
160
|
+
findByKey(projectKey: string, businessTypeKey: string): Promise<BusinessTypeRow | null>;
|
|
161
|
+
create(data: CreateBusinessTypeData): Promise<BusinessTypeRow>;
|
|
162
|
+
update(businessTypeId: string, data: UpdateBusinessTypeData): Promise<BusinessTypeRow>;
|
|
163
|
+
softDelete(businessTypeId: string): Promise<void>;
|
|
164
|
+
listVersions(businessTypeId: string): Promise<BusinessTypeVersionRow[]>;
|
|
165
|
+
findVersionById(versionId: string): Promise<BusinessTypeVersionRow | null>;
|
|
166
|
+
findCurrentDraft(businessTypeId: string): Promise<BusinessTypeVersionRow | null>;
|
|
167
|
+
findLatestLive(businessTypeId: string): Promise<BusinessTypeVersionRow | null>;
|
|
168
|
+
createDraft(data: CreateBusinessTypeVersionDraftData): Promise<BusinessTypeVersionRow>;
|
|
169
|
+
updateDraft(versionId: string, data: UpdateBusinessTypeVersionDraftData): Promise<BusinessTypeVersionRow>;
|
|
170
|
+
publishDraft(versionId: string, publishMeta: {
|
|
171
|
+
publishedByUserId: string | null;
|
|
172
|
+
publishedChanges: VersionChange[];
|
|
173
|
+
nonRegressive: boolean;
|
|
174
|
+
}): Promise<BusinessTypeVersionRow>;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* In-memory implementation of `MarketingProjectionRepository`. Enforces
|
|
178
|
+
* uniqueness over (`targetType`, `targetVersionId`, `locale`) like the
|
|
179
|
+
* DB schema.
|
|
180
|
+
*/
|
|
181
|
+
declare class FakeMarketingProjectionRepository implements MarketingProjectionRepository {
|
|
182
|
+
private readonly rows;
|
|
183
|
+
private nextId;
|
|
184
|
+
private genId;
|
|
185
|
+
private nowIso;
|
|
186
|
+
seed(row: MarketingProjectionRow): void;
|
|
187
|
+
clear(): void;
|
|
188
|
+
list(filter: MarketingProjectionFilter): Promise<MarketingProjectionRow[]>;
|
|
189
|
+
findById(id: string): Promise<MarketingProjectionRow | null>;
|
|
190
|
+
findByTarget(targetType: string, targetVersionId: string, locale: string): Promise<MarketingProjectionRow | null>;
|
|
191
|
+
create(data: CreateMarketingProjectionData): Promise<MarketingProjectionRow>;
|
|
192
|
+
update(id: string, data: UpdateMarketingProjectionData): Promise<MarketingProjectionRow>;
|
|
193
|
+
delete(id: string): Promise<void>;
|
|
194
|
+
}
|
|
195
|
+
declare class FakePlanRepository implements PlanRepository {
|
|
196
|
+
private readonly plans;
|
|
197
|
+
private readonly versions;
|
|
198
|
+
private nextId;
|
|
199
|
+
private nextVersionId;
|
|
200
|
+
private genId;
|
|
201
|
+
private genVersionId;
|
|
202
|
+
private nowIso;
|
|
203
|
+
seed(row: PlanRow): void;
|
|
204
|
+
seedVersion(row: PlanVersionRow): void;
|
|
205
|
+
clear(): void;
|
|
206
|
+
list(filter: PlanListFilter): Promise<PlanRow[]>;
|
|
207
|
+
findById(planId: string): Promise<PlanRow | null>;
|
|
208
|
+
findByKey(projectKey: string, planKey: string): Promise<PlanRow | null>;
|
|
209
|
+
create(data: CreatePlanData): Promise<PlanRow>;
|
|
210
|
+
update(planId: string, data: UpdatePlanData): Promise<PlanRow>;
|
|
211
|
+
softDelete(planId: string): Promise<void>;
|
|
212
|
+
hardDelete(planId: string): Promise<void>;
|
|
213
|
+
listVersions(planKey: string): Promise<PlanVersionRow[]>;
|
|
214
|
+
findVersionById(versionId: string): Promise<PlanVersionRow | null>;
|
|
215
|
+
findCurrentDraft(planKey: string): Promise<PlanVersionRow | null>;
|
|
216
|
+
findLatestLivePlanVersion(planKey: string): Promise<PlanVersionRow | null>;
|
|
217
|
+
findActivePlanVersion(planKey: string, asOf?: Date): Promise<PlanVersionRow | null>;
|
|
218
|
+
createPlanVersionDraft(data: CreatePlanVersionDraftData): Promise<PlanVersionRow>;
|
|
219
|
+
updatePlanVersionDraft(versionId: string, data: UpdatePlanVersionDraftData): Promise<PlanVersionRow>;
|
|
220
|
+
publishPlanVersionDraft(versionId: string, publishMeta: {
|
|
221
|
+
publishedByUserId: string | null;
|
|
222
|
+
publishedChanges: VersionChange[];
|
|
223
|
+
nonRegressive: boolean;
|
|
224
|
+
validFrom: Date;
|
|
225
|
+
validUntil: Date | null;
|
|
226
|
+
}): Promise<PlanVersionRow>;
|
|
227
|
+
deletePlanVersionDraft(versionId: string): Promise<void>;
|
|
228
|
+
terminate(versionId: string, endsAt: Date): Promise<PlanVersionRow>;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** No-op stub for `MfaPort`. */
|
|
232
|
+
declare class StubMfaPort {
|
|
233
|
+
private secrets;
|
|
234
|
+
getSecret(userId: string): Promise<string | null>;
|
|
235
|
+
setSecret(userId: string, secret: string | null): Promise<void>;
|
|
236
|
+
isEnabled(userId: string): Promise<boolean>;
|
|
237
|
+
}
|
|
238
|
+
/** No-op stub for `AuditPort`. Keeps calls as a list for asserts. */
|
|
239
|
+
declare class StubAuditPort {
|
|
240
|
+
readonly calls: Array<{
|
|
241
|
+
actor: unknown;
|
|
242
|
+
entity: string;
|
|
243
|
+
entityId: string;
|
|
244
|
+
action: string;
|
|
245
|
+
changes?: Record<string, unknown>;
|
|
246
|
+
}>;
|
|
247
|
+
write(input: {
|
|
248
|
+
actor: unknown;
|
|
249
|
+
entity: string;
|
|
250
|
+
entityId: string;
|
|
251
|
+
action: string;
|
|
252
|
+
changes?: Record<string, unknown>;
|
|
253
|
+
}): Promise<void>;
|
|
254
|
+
}
|
|
255
|
+
/** No-op stub for `RlsBypassPort` — calls the callback without wrapping. */
|
|
256
|
+
declare class StubRlsBypassPort {
|
|
257
|
+
runWithBypass<T>(fn: () => Promise<T>): Promise<T>;
|
|
258
|
+
}
|
|
259
|
+
interface CreateSaasPlatformTestModuleOptions {
|
|
260
|
+
planCatalog: PlanCatalog;
|
|
261
|
+
/** Default `'starter'` if the catalog has at least one plan. */
|
|
262
|
+
defaultPlanId?: string;
|
|
263
|
+
/** QuotaProvider classes for the `EnforceQuotaInterceptor`. */
|
|
264
|
+
quotaProviders?: Array<Type<QuotaProvider>>;
|
|
265
|
+
/** Overrides — if the test needs a different adapter. */
|
|
266
|
+
overrides?: Partial<{
|
|
267
|
+
mfa: SaasPlatformModuleOptions['adapters']['mfa'];
|
|
268
|
+
audit: SaasPlatformModuleOptions['adapters']['audit'];
|
|
269
|
+
rlsBypass: SaasPlatformModuleOptions['adapters']['rlsBypass'];
|
|
270
|
+
}>;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Returns a `DynamicModule` that sets up the SaasPlatformModule with stub
|
|
274
|
+
* adapters + the given PlanCatalog in static entitlement mode.
|
|
275
|
+
* Tests can drop this into `Test.createTestingModule({ imports: [...] })`.
|
|
276
|
+
*/
|
|
277
|
+
declare function createSaasPlatformTestModule(options: CreateSaasPlatformTestModuleOptions): DynamicModule;
|
|
278
|
+
|
|
279
|
+
export { type CreateSaasPlatformTestModuleOptions, FakeBundleRepository, FakeBusinessTypeRepository, FakeMarketingProjectionRepository, FakePlanRepository, FakePlanVersionRepository, FakeSubscriptionBundleRepository, FakeSubscriptionContractRepository, FakeSubscriptionRepository, FakeTransactionRunner, StubAuditPort, StubMfaPort, StubRlsBypassPort, createSaasPlatformTestModule };
|