@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.
Files changed (96) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +53 -0
  3. package/dist/admin/index.cjs +1283 -0
  4. package/dist/admin/index.d.cts +225 -0
  5. package/dist/admin/index.d.ts +225 -0
  6. package/dist/admin/index.js +58 -0
  7. package/dist/admin-audit.service-9IqXMlZm.d.cts +30 -0
  8. package/dist/admin-audit.service-9IqXMlZm.d.ts +30 -0
  9. package/dist/admin-manifest.config-DyrQNT7M.d.cts +10 -0
  10. package/dist/admin-manifest.config-DyrQNT7M.d.ts +10 -0
  11. package/dist/aggregation-CJ3qQf92.d.cts +288 -0
  12. package/dist/aggregation-Dvz9e8X2.d.ts +288 -0
  13. package/dist/billing/index.cjs +5205 -0
  14. package/dist/billing/index.d.cts +1326 -0
  15. package/dist/billing/index.d.ts +1326 -0
  16. package/dist/billing/index.js +197 -0
  17. package/dist/catalog/index.cjs +6449 -0
  18. package/dist/catalog/index.d.cts +1073 -0
  19. package/dist/catalog/index.d.ts +1073 -0
  20. package/dist/catalog/index.js +158 -0
  21. package/dist/checkout-offer/index.cjs +807 -0
  22. package/dist/checkout-offer/index.d.cts +120 -0
  23. package/dist/checkout-offer/index.d.ts +120 -0
  24. package/dist/checkout-offer/index.js +20 -0
  25. package/dist/chunk-2FR6ZL7R.js +0 -0
  26. package/dist/chunk-2PNX2QL2.js +347 -0
  27. package/dist/chunk-2VNZYRDV.js +326 -0
  28. package/dist/chunk-7MLGHWKO.js +12 -0
  29. package/dist/chunk-ABB2CRAS.js +31 -0
  30. package/dist/chunk-AJ3EPELS.js +157 -0
  31. package/dist/chunk-E56W4U2P.js +90 -0
  32. package/dist/chunk-HRU3CP57.js +71 -0
  33. package/dist/chunk-L4SUWH5B.js +14 -0
  34. package/dist/chunk-M6CZQYPY.js +314 -0
  35. package/dist/chunk-MDIZUVIK.js +87 -0
  36. package/dist/chunk-MV6LW5UB.js +217 -0
  37. package/dist/chunk-NYLON2VC.js +6 -0
  38. package/dist/chunk-P6MYZMXQ.js +951 -0
  39. package/dist/chunk-Q53N43LQ.js +13 -0
  40. package/dist/chunk-QJVPRD3R.js +573 -0
  41. package/dist/chunk-RIGLQ4BV.js +3367 -0
  42. package/dist/chunk-SABTXESR.js +24 -0
  43. package/dist/chunk-SHUYVCID.js +6 -0
  44. package/dist/chunk-SYEVGPFC.js +686 -0
  45. package/dist/chunk-V7HUZ6UQ.js +5923 -0
  46. package/dist/chunk-WPUVJRIN.js +29 -0
  47. package/dist/chunk-XCZ3TTFK.js +22 -0
  48. package/dist/chunk-XSGU2Y2O.js +1611 -0
  49. package/dist/chunk-YNEW3L63.js +190 -0
  50. package/dist/chunk-YXGHPILG.js +614 -0
  51. package/dist/chunk-ZL4LLE2Z.js +465 -0
  52. package/dist/di-CcNeq9v-.d.cts +16 -0
  53. package/dist/di-CcNeq9v-.d.ts +16 -0
  54. package/dist/discovery/index.cjs +588 -0
  55. package/dist/discovery/index.d.cts +299 -0
  56. package/dist/discovery/index.d.ts +299 -0
  57. package/dist/discovery/index.js +49 -0
  58. package/dist/discovery.scanner-CUYLKlYT.d.cts +59 -0
  59. package/dist/discovery.scanner-CUYLKlYT.d.ts +59 -0
  60. package/dist/entitlement/index.cjs +805 -0
  61. package/dist/entitlement/index.d.cts +60 -0
  62. package/dist/entitlement/index.d.ts +60 -0
  63. package/dist/entitlement/index.js +57 -0
  64. package/dist/index.cjs +15918 -0
  65. package/dist/index.d.cts +85 -0
  66. package/dist/index.d.ts +85 -0
  67. package/dist/index.js +956 -0
  68. package/dist/plan-resolution-CFCoUkrE.d.cts +41 -0
  69. package/dist/plan-resolution-CFCoUkrE.d.ts +41 -0
  70. package/dist/platform/index.cjs +2772 -0
  71. package/dist/platform/index.d.cts +118 -0
  72. package/dist/platform/index.d.ts +118 -0
  73. package/dist/platform/index.js +37 -0
  74. package/dist/promo/index.cjs +995 -0
  75. package/dist/promo/index.d.cts +121 -0
  76. package/dist/promo/index.d.ts +121 -0
  77. package/dist/promo/index.js +58 -0
  78. package/dist/registration/index.cjs +1687 -0
  79. package/dist/registration/index.d.cts +444 -0
  80. package/dist/registration/index.d.ts +444 -0
  81. package/dist/registration/index.js +82 -0
  82. package/dist/saas-platform.module-CgZ2omgg.d.cts +180 -0
  83. package/dist/saas-platform.module-DT9TnCzk.d.ts +180 -0
  84. package/dist/service-DX8KbGXl.d.cts +109 -0
  85. package/dist/service-DX8KbGXl.d.ts +109 -0
  86. package/dist/subscription-contract/index.cjs +471 -0
  87. package/dist/subscription-contract/index.d.cts +17 -0
  88. package/dist/subscription-contract/index.d.ts +17 -0
  89. package/dist/subscription-contract/index.js +21 -0
  90. package/dist/subscription-contract.service--cm47ZJJ.d.cts +39 -0
  91. package/dist/subscription-contract.service--cm47ZJJ.d.ts +39 -0
  92. package/dist/testing/index.cjs +3942 -0
  93. package/dist/testing/index.d.cts +279 -0
  94. package/dist/testing/index.d.ts +279 -0
  95. package/dist/testing/index.js +1195 -0
  96. 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 };