@saasicat/nest 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/dist/_entries.cjs +17678 -0
  2. package/dist/admin/index.cjs +4 -1274
  3. package/dist/admin/index.d.cts +10 -171
  4. package/dist/admin/index.d.ts +10 -171
  5. package/dist/admin/index.js +13 -4
  6. package/dist/admin-resources.module-BZN_yBFj.d.cts +40 -0
  7. package/dist/admin-resources.module-Cms8MY6h.d.ts +40 -0
  8. package/dist/admin-stats.module-15ac5KY3.d.ts +201 -0
  9. package/dist/admin-stats.module-v4SVvWzj.d.cts +201 -0
  10. package/dist/billing/index.cjs +4 -5128
  11. package/dist/billing/index.d.cts +8 -719
  12. package/dist/billing/index.d.ts +8 -719
  13. package/dist/billing/index.js +7 -10
  14. package/dist/catalog/index.cjs +4 -5624
  15. package/dist/catalog/index.d.cts +7 -427
  16. package/dist/catalog/index.d.ts +7 -427
  17. package/dist/catalog/index.js +14 -12
  18. package/dist/catalog.module-BKObceo0.d.ts +103 -0
  19. package/dist/catalog.module-Dw2Hrbyu.d.cts +103 -0
  20. package/dist/checkout-offer/index.cjs +4 -807
  21. package/dist/checkout-offer/index.d.cts +5 -84
  22. package/dist/checkout-offer/index.d.ts +5 -84
  23. package/dist/checkout-offer/index.js +3 -2
  24. package/dist/checkout-offer.module-Y0hDOVrq.d.ts +85 -0
  25. package/dist/checkout-offer.module-ZYF5IsDY.d.cts +85 -0
  26. package/dist/{chunk-5DDCSGZC.js → chunk-33TP26MR.js} +144 -356
  27. package/dist/{chunk-CK54YKVK.js → chunk-75JABGZK.js} +16 -16
  28. package/dist/{chunk-HRU3CP57.js → chunk-BOADBIGB.js} +1 -1
  29. package/dist/{chunk-ZLUGQE5D.js → chunk-E7AJ42AB.js} +8 -8
  30. package/dist/{chunk-6LP7CDS2.js → chunk-F3VV6EXG.js} +18 -13
  31. package/dist/{chunk-PLDHI6NA.js → chunk-FVT7KTUI.js} +258 -3
  32. package/dist/{chunk-RZU4HIAN.js → chunk-GBDUH6DO.js} +26 -23
  33. package/dist/{chunk-K3OPNVFR.js → chunk-HBFU5AN3.js} +5 -9
  34. package/dist/chunk-HWGIMNWR.js +0 -0
  35. package/dist/chunk-IRWZC3GC.js +755 -0
  36. package/dist/chunk-L4KTQ6CL.js +20 -0
  37. package/dist/{chunk-T5SXXAKG.js → chunk-LZHLGUQI.js} +1 -1
  38. package/dist/{chunk-YHHE4KDA.js → chunk-PUXLPUVH.js} +351 -21
  39. package/dist/chunk-TXDH4FUA.js +211 -0
  40. package/dist/chunk-UF53DWWR.js +0 -0
  41. package/dist/{chunk-AJ3EPELS.js → chunk-V34AA7C7.js} +1 -1
  42. package/dist/chunk-WJ6BMDL3.js +313 -0
  43. package/dist/{chunk-M6CZQYPY.js → chunk-Z3FWN3HJ.js} +2 -2
  44. package/dist/discovery/index.cjs +4 -588
  45. package/dist/discovery/index.js +3 -3
  46. package/dist/{enforce-quota.interceptor-cv7r80VM.d.cts → enforce-quota.interceptor-BhkX5nq2.d.cts} +127 -12
  47. package/dist/{enforce-quota.interceptor-Dj1Kusr5.d.ts → enforce-quota.interceptor-CcqZc0Yu.d.ts} +127 -12
  48. package/dist/entitlement/index.cjs +4 -780
  49. package/dist/entitlement/index.d.cts +3 -1
  50. package/dist/entitlement/index.d.ts +3 -1
  51. package/dist/entitlement/index.js +2 -3
  52. package/dist/index.cjs +4 -14975
  53. package/dist/index.d.cts +22 -54
  54. package/dist/index.d.ts +22 -54
  55. package/dist/index.js +88 -369
  56. package/dist/module-CSN8j8Tp.d.cts +39 -0
  57. package/dist/module-DgYJur6f.d.ts +39 -0
  58. package/dist/plan-versions.service-BB36Q1bg.d.ts +329 -0
  59. package/dist/plan-versions.service-CUR2Whyr.d.cts +329 -0
  60. package/dist/platform/index.cjs +4 -2779
  61. package/dist/platform/index.d.cts +50 -8
  62. package/dist/platform/index.d.ts +50 -8
  63. package/dist/platform/index.js +116 -12
  64. package/dist/promo/index.cjs +4 -995
  65. package/dist/promo/index.d.cts +48 -46
  66. package/dist/promo/index.d.ts +48 -46
  67. package/dist/promo/index.js +10 -2
  68. package/dist/rate-limit.guard-dntqAlfZ.d.cts +19 -0
  69. package/dist/rate-limit.guard-dntqAlfZ.d.ts +19 -0
  70. package/dist/registration/index.cjs +4 -1655
  71. package/dist/registration/index.js +1 -1
  72. package/dist/setup.service-BPwJ7My_.d.cts +45 -0
  73. package/dist/setup.service-D2mrdDDB.d.ts +45 -0
  74. package/dist/subscription-bundle-preview.service-BkJXHAeP.d.cts +438 -0
  75. package/dist/subscription-bundle-preview.service-DBbrE_cw.d.ts +438 -0
  76. package/dist/subscription-bundles.module-BQmoozm5.d.ts +291 -0
  77. package/dist/subscription-bundles.module-DCpV-Pb5.d.cts +291 -0
  78. package/dist/subscription-contract/index.cjs +4 -471
  79. package/dist/subscription-contract/index.d.cts +5 -13
  80. package/dist/subscription-contract/index.d.ts +5 -13
  81. package/dist/subscription-contract.module-CG9fPKAr.d.cts +14 -0
  82. package/dist/subscription-contract.module-CVebZzHR.d.ts +14 -0
  83. package/dist/testing/index.cjs +4 -3756
  84. package/dist/testing/index.d.cts +9 -4
  85. package/dist/testing/index.d.ts +9 -4
  86. package/dist/testing/index.js +13 -7
  87. package/package.json +5 -5
  88. package/dist/admin-manifest.config-DyrQNT7M.d.cts +0 -10
  89. package/dist/admin-manifest.config-DyrQNT7M.d.ts +0 -10
  90. package/dist/admin-manifest.service-Cp6uwvmt.d.cts +0 -33
  91. package/dist/admin-manifest.service-UGgzR1x-.d.ts +0 -33
  92. package/dist/chunk-52UTQRD3.js +0 -326
  93. package/dist/chunk-N3WANKSA.js +0 -20
  94. package/dist/chunk-Q53N43LQ.js +0 -13
  95. /package/dist/{chunk-2FR6ZL7R.js → chunk-DBZGV3HC.js} +0 -0
@@ -1,2779 +1,4 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
9
- var __export = (target, all) => {
10
- for (var name in all)
11
- __defProp(target, name, { get: all[name], enumerable: true });
12
- };
13
- var __copyProps = (to, from3, except, desc) => {
14
- if (from3 && typeof from3 === "object" || typeof from3 === "function") {
15
- for (let key of __getOwnPropNames(from3))
16
- if (!__hasOwnProp.call(to, key) && key !== except)
17
- __defProp(to, key, { get: () => from3[key], enumerable: !(desc = __getOwnPropDesc(from3, key)) || desc.enumerable });
18
- }
19
- return to;
20
- };
21
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
- // If the importer is in node compatibility mode or this is not an ESM
23
- // file that has been converted to a CommonJS file using a Babel-
24
- // compatible transform (i.e. "__esModule" has not been set), then set
25
- // "default" to the CommonJS "module.exports" for node compatibility.
26
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
- mod
28
- ));
29
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
-
31
- // src/platform/index.ts
32
- var platform_exports = {};
33
- __export(platform_exports, {
34
- AdminManifestService: () => AdminManifestService,
35
- EnforceQuotaInterceptor: () => EnforceQuotaInterceptor,
36
- PLAN_RESOLVER_PORT_TOKEN: () => PLAN_RESOLVER_PORT_TOKEN,
37
- QUOTA_PROVIDERS_TOKEN: () => QUOTA_PROVIDERS_TOKEN,
38
- STATIC_FEATURE_GUARD_CONFIG_TOKEN: () => STATIC_FEATURE_GUARD_CONFIG_TOKEN,
39
- SaasPlatformModule: () => SaasPlatformModule,
40
- StaticEntitlementService: () => StaticEntitlementService,
41
- StaticFeatureGuard: () => StaticFeatureGuard,
42
- StaticPlanResolver: () => StaticPlanResolver,
43
- TenantManifestService: () => TenantManifestService,
44
- buildTenantManifestController: () => buildTenantManifestController
45
- });
46
- module.exports = __toCommonJS(platform_exports);
47
-
48
- // src/platform/saas-platform.module.ts
49
- var import_common22 = require("@nestjs/common");
50
- var import_core6 = require("@nestjs/core");
51
- var import_types = require("@saasicat/types");
52
-
53
- // src/core/di.ts
54
- function asProvider(token, impl) {
55
- if (typeof impl === "object" && impl !== null && "useFactory" in impl && typeof impl.useFactory === "function") {
56
- const factory = impl;
57
- return {
58
- provide: token,
59
- useFactory: factory.useFactory,
60
- inject: factory.inject
61
- };
62
- }
63
- return {
64
- provide: token,
65
- useValue: impl
66
- };
67
- }
68
- __name(asProvider, "asProvider");
69
-
70
- // src/admin/module.ts
71
- var import_common6 = require("@nestjs/common");
72
-
73
- // src/admin/admin-audit.service.ts
74
- var os = __toESM(require("os"), 1);
75
- var import_common = require("@nestjs/common");
76
-
77
- // src/admin/tokens.ts
78
- var MFA_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/MfaPort");
79
- var AUDIT_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/AuditPort");
80
- var RLS_BYPASS_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/RlsBypassPort");
81
-
82
- // src/admin/admin-audit.service.ts
83
- function _ts_decorate(decorators, target, key, desc) {
84
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
85
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
86
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
87
- return c > 3 && r && Object.defineProperty(target, key, r), r;
88
- }
89
- __name(_ts_decorate, "_ts_decorate");
90
- function _ts_metadata(k, v) {
91
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
92
- }
93
- __name(_ts_metadata, "_ts_metadata");
94
- function _ts_param(paramIndex, decorator) {
95
- return function(target, key) {
96
- decorator(target, key, paramIndex);
97
- };
98
- }
99
- __name(_ts_param, "_ts_param");
100
- var AdminAuditService = class {
101
- static {
102
- __name(this, "AdminAuditService");
103
- }
104
- audit;
105
- constructor(audit) {
106
- this.audit = audit;
107
- }
108
- async log(input) {
109
- await this.audit.write({
110
- actor: input.actor,
111
- entity: input.entity,
112
- entityId: input.entityId,
113
- action: input.action,
114
- changes: {
115
- ...input.changes ?? {},
116
- actor: this.actorTag(input.actor)
117
- }
118
- });
119
- }
120
- /**
121
- * Standardized actor tag for the `changes.actor` column:
122
- * `<source>:<email>:<context>`.
123
- */
124
- actorTag(actor) {
125
- return `${actor.source}:${actor.email}:${actor.context}`;
126
- }
127
- /** Helper: build an AdminActor from an authenticated web request. */
128
- fromWebRequest(user, sessionId) {
129
- return {
130
- userId: user.id,
131
- email: user.email,
132
- source: "web",
133
- context: sessionId ?? "unknown"
134
- };
135
- }
136
- /** Helper: build an AdminActor from a CLI identity (hostname as context). */
137
- fromCli(user) {
138
- return {
139
- userId: user.id,
140
- email: user.email,
141
- source: "cli",
142
- context: os.hostname()
143
- };
144
- }
145
- };
146
- AdminAuditService = _ts_decorate([
147
- (0, import_common.Injectable)(),
148
- _ts_param(0, (0, import_common.Inject)(AUDIT_PORT_TOKEN)),
149
- _ts_metadata("design:type", Function),
150
- _ts_metadata("design:paramtypes", [
151
- typeof AuditPort === "undefined" ? Object : AuditPort
152
- ])
153
- ], AdminAuditService);
154
-
155
- // src/admin/admin-bypass-rls.interceptor.ts
156
- var import_common2 = require("@nestjs/common");
157
- var import_rxjs = require("rxjs");
158
- var import_operators = require("rxjs/operators");
159
- function _ts_decorate2(decorators, target, key, desc) {
160
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
161
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
162
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
163
- return c > 3 && r && Object.defineProperty(target, key, r), r;
164
- }
165
- __name(_ts_decorate2, "_ts_decorate");
166
- function _ts_metadata2(k, v) {
167
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
168
- }
169
- __name(_ts_metadata2, "_ts_metadata");
170
- function _ts_param2(paramIndex, decorator) {
171
- return function(target, key) {
172
- decorator(target, key, paramIndex);
173
- };
174
- }
175
- __name(_ts_param2, "_ts_param");
176
- var AdminBypassRlsInterceptor = class {
177
- static {
178
- __name(this, "AdminBypassRlsInterceptor");
179
- }
180
- port;
181
- constructor(port) {
182
- this.port = port;
183
- }
184
- intercept(_context, next) {
185
- return (0, import_rxjs.defer)(() => (0, import_rxjs.from)(this.port.runWithBypass(async () => {
186
- return next.handle();
187
- })).pipe((0, import_operators.switchAll)()));
188
- }
189
- };
190
- AdminBypassRlsInterceptor = _ts_decorate2([
191
- (0, import_common2.Injectable)(),
192
- _ts_param2(0, (0, import_common2.Inject)(RLS_BYPASS_PORT_TOKEN)),
193
- _ts_metadata2("design:type", Function),
194
- _ts_metadata2("design:paramtypes", [
195
- typeof RlsBypassPort === "undefined" ? Object : RlsBypassPort
196
- ])
197
- ], AdminBypassRlsInterceptor);
198
-
199
- // src/admin/mfa.ts
200
- var import_common3 = require("@nestjs/common");
201
- var import_otplib = require("otplib");
202
- function _ts_decorate3(decorators, target, key, desc) {
203
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
204
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
205
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
206
- return c > 3 && r && Object.defineProperty(target, key, r), r;
207
- }
208
- __name(_ts_decorate3, "_ts_decorate");
209
- function _ts_metadata3(k, v) {
210
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
211
- }
212
- __name(_ts_metadata3, "_ts_metadata");
213
- function _ts_param3(paramIndex, decorator) {
214
- return function(target, key) {
215
- decorator(target, key, paramIndex);
216
- };
217
- }
218
- __name(_ts_param3, "_ts_param");
219
- var CLOCK_DRIFT_TOLERANCE_SECONDS = 30;
220
- var OTPLIB_TOKEN_ERROR_NAMES = /* @__PURE__ */ new Set([
221
- "TokenError",
222
- "TokenFormatError",
223
- "TokenLengthError"
224
- ]);
225
- function isOtplibTokenError(error) {
226
- return error instanceof Error && OTPLIB_TOKEN_ERROR_NAMES.has(error.name);
227
- }
228
- __name(isOtplibTokenError, "isOtplibTokenError");
229
- var MfaService = class _MfaService {
230
- static {
231
- __name(this, "MfaService");
232
- }
233
- mfa;
234
- logger = new import_common3.Logger(_MfaService.name);
235
- constructor(mfa) {
236
- this.mfa = mfa;
237
- }
238
- /**
239
- * Generates a new TOTP secret, persists it via `MfaPort.setSecret`
240
- * and returns the secret + the otpauth URI for the QR code.
241
- *
242
- * `issuer` and `label` shape the display in the authenticator (e.g.
243
- * "DemoApp:taci@example.com").
244
- */
245
- async setup(userId, label, issuer) {
246
- const secret = (0, import_otplib.generateSecret)();
247
- const otpauthUri = (0, import_otplib.generateURI)({
248
- issuer,
249
- label,
250
- secret
251
- });
252
- await this.mfa.setSecret(userId, secret);
253
- return {
254
- secret,
255
- otpauthUri
256
- };
257
- }
258
- /**
259
- * Verifies the entered code against the stored secret,
260
- * clock-drift-tolerant by ±1 time step.
261
- */
262
- async verify(input) {
263
- const secret = await this.mfa.getSecret(input.userId);
264
- if (!secret) return false;
265
- try {
266
- const result = await (0, import_otplib.verify)({
267
- secret,
268
- token: input.code.trim(),
269
- epochTolerance: CLOCK_DRIFT_TOLERANCE_SECONDS
270
- });
271
- return result.valid;
272
- } catch (error) {
273
- if (isOtplibTokenError(error)) {
274
- return false;
275
- }
276
- const message = error instanceof Error ? error.message : String(error);
277
- this.logger.warn(`TOTP-Verify f\xFCr User ${input.userId} unerwartet fehlgeschlagen: ${message}`);
278
- return false;
279
- }
280
- }
281
- /**
282
- * Deletes a user's secret — used by `ahp admin mfa-reset`.
283
- */
284
- async disable(userId) {
285
- await this.mfa.setSecret(userId, null);
286
- }
287
- /** Delegates to `MfaPort.isEnabled`. */
288
- async isEnabled(userId) {
289
- return this.mfa.isEnabled(userId);
290
- }
291
- };
292
- MfaService = _ts_decorate3([
293
- (0, import_common3.Injectable)(),
294
- _ts_param3(0, (0, import_common3.Inject)(MFA_PORT_TOKEN)),
295
- _ts_metadata3("design:type", Function),
296
- _ts_metadata3("design:paramtypes", [
297
- typeof MfaPort === "undefined" ? Object : MfaPort
298
- ])
299
- ], MfaService);
300
-
301
- // src/admin/mfa.guard.ts
302
- var import_common4 = require("@nestjs/common");
303
- var import_core = require("@nestjs/core");
304
- function _ts_decorate4(decorators, target, key, desc) {
305
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
306
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
307
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
308
- return c > 3 && r && Object.defineProperty(target, key, r), r;
309
- }
310
- __name(_ts_decorate4, "_ts_decorate");
311
- function _ts_metadata4(k, v) {
312
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
313
- }
314
- __name(_ts_metadata4, "_ts_metadata");
315
- function _ts_param4(paramIndex, decorator) {
316
- return function(target, key) {
317
- decorator(target, key, paramIndex);
318
- };
319
- }
320
- __name(_ts_param4, "_ts_param");
321
- var REQUIRE_MFA_KEY = "saas-platform/require-mfa";
322
- var MfaGuard = class _MfaGuard {
323
- static {
324
- __name(this, "MfaGuard");
325
- }
326
- reflector;
327
- mfaService;
328
- logger = new import_common4.Logger(_MfaGuard.name);
329
- constructor(reflector, mfaService) {
330
- this.reflector = reflector;
331
- this.mfaService = mfaService;
332
- }
333
- async canActivate(context) {
334
- const required = this.reflector.getAllAndOverride(REQUIRE_MFA_KEY, [
335
- context.getHandler(),
336
- context.getClass()
337
- ]);
338
- if (!required) return true;
339
- if (process.env.SAAS_PLATFORM_SKIP_MFA === "1" && process.env.NODE_ENV !== "production") {
340
- return true;
341
- }
342
- const request = context.switchToHttp().getRequest();
343
- const user = request.user;
344
- if (!user) throw new import_common4.UnauthorizedException({
345
- reason: "NOT_AUTHENTICATED"
346
- });
347
- const enabled = await this.mfaService.isEnabled(user.id);
348
- if (!enabled) {
349
- throw new import_common4.UnauthorizedException({
350
- reason: "MFA_NOT_SET_UP",
351
- message: "Bitte zuerst MFA-Setup \xFCber das CLI ausf\xFChren."
352
- });
353
- }
354
- const headerVal = request.headers?.["x-mfa-code"];
355
- const code = Array.isArray(headerVal) ? headerVal[0] : headerVal;
356
- if (!code) {
357
- throw new import_common4.UnauthorizedException({
358
- reason: "MFA_REQUIRED",
359
- message: "TOTP-Code im Header X-Mfa-Code erforderlich."
360
- });
361
- }
362
- const valid = await this.mfaService.verify({
363
- userId: user.id,
364
- code: String(code)
365
- });
366
- if (!valid) {
367
- throw new import_common4.UnauthorizedException({
368
- reason: "MFA_FAILED",
369
- message: "TOTP-Code ung\xFCltig."
370
- });
371
- }
372
- return true;
373
- }
374
- };
375
- MfaGuard = _ts_decorate4([
376
- (0, import_common4.Injectable)(),
377
- _ts_param4(0, (0, import_common4.Inject)(import_core.Reflector)),
378
- _ts_metadata4("design:type", Function),
379
- _ts_metadata4("design:paramtypes", [
380
- typeof import_core.Reflector === "undefined" ? Object : import_core.Reflector,
381
- typeof MfaService === "undefined" ? Object : MfaService
382
- ])
383
- ], MfaGuard);
384
-
385
- // src/admin/super-admin.guard.ts
386
- var import_common5 = require("@nestjs/common");
387
- function _ts_decorate5(decorators, target, key, desc) {
388
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
389
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
390
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
391
- return c > 3 && r && Object.defineProperty(target, key, r), r;
392
- }
393
- __name(_ts_decorate5, "_ts_decorate");
394
- var SuperAdminGuard = class {
395
- static {
396
- __name(this, "SuperAdminGuard");
397
- }
398
- canActivate(context) {
399
- const request = context.switchToHttp().getRequest();
400
- if (!request.user) throw new import_common5.ForbiddenException("Nicht authentifiziert");
401
- const role = request.user.platformRole ?? request.user.role;
402
- if (role !== "SUPER_ADMIN") {
403
- throw new import_common5.ForbiddenException("Nur SUPER_ADMIN-Rolle erlaubt");
404
- }
405
- return true;
406
- }
407
- };
408
- SuperAdminGuard = _ts_decorate5([
409
- (0, import_common5.Injectable)()
410
- ], SuperAdminGuard);
411
-
412
- // src/admin/module.ts
413
- function _ts_decorate6(decorators, target, key, desc) {
414
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
415
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
416
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
417
- return c > 3 && r && Object.defineProperty(target, key, r), r;
418
- }
419
- __name(_ts_decorate6, "_ts_decorate");
420
- var AdminModule = class _AdminModule {
421
- static {
422
- __name(this, "AdminModule");
423
- }
424
- static forRoot(options) {
425
- const providers = [
426
- asProvider(MFA_PORT_TOKEN, options.mfaPort),
427
- asProvider(AUDIT_PORT_TOKEN, options.auditPort),
428
- asProvider(RLS_BYPASS_PORT_TOKEN, options.rlsBypassPort),
429
- MfaService,
430
- AdminAuditService,
431
- SuperAdminGuard,
432
- MfaGuard,
433
- AdminBypassRlsInterceptor
434
- ];
435
- return {
436
- module: _AdminModule,
437
- global: options.global ?? false,
438
- providers,
439
- exports: [
440
- MFA_PORT_TOKEN,
441
- AUDIT_PORT_TOKEN,
442
- RLS_BYPASS_PORT_TOKEN,
443
- MfaService,
444
- AdminAuditService,
445
- SuperAdminGuard,
446
- MfaGuard,
447
- AdminBypassRlsInterceptor
448
- ]
449
- };
450
- }
451
- };
452
- AdminModule = _ts_decorate6([
453
- (0, import_common6.Module)({})
454
- ], AdminModule);
455
-
456
- // src/admin/admin-manifest.module.ts
457
- var import_common9 = require("@nestjs/common");
458
-
459
- // src/admin/admin-manifest.config.ts
460
- var ADMIN_MANIFEST_CONFIG = /* @__PURE__ */ Symbol("ADMIN_MANIFEST_CONFIG");
461
-
462
- // src/admin/admin-manifest.service.ts
463
- var import_common7 = require("@nestjs/common");
464
- var import_node_crypto = require("crypto");
465
- function _ts_decorate7(decorators, target, key, desc) {
466
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
467
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
468
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
469
- return c > 3 && r && Object.defineProperty(target, key, r), r;
470
- }
471
- __name(_ts_decorate7, "_ts_decorate");
472
- function _ts_metadata5(k, v) {
473
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
474
- }
475
- __name(_ts_metadata5, "_ts_metadata");
476
- function _ts_param5(paramIndex, decorator) {
477
- return function(target, key) {
478
- decorator(target, key, paramIndex);
479
- };
480
- }
481
- __name(_ts_param5, "_ts_param");
482
- var PLATFORM_CORE_CONTRIBUTION_TOKEN = /* @__PURE__ */ Symbol("PLATFORM_CORE_CONTRIBUTION");
483
- var AdminManifestService = class _AdminManifestService {
484
- static {
485
- __name(this, "AdminManifestService");
486
- }
487
- config;
488
- logger = new import_common7.Logger(_AdminManifestService.name);
489
- contributions = [];
490
- cached = null;
491
- constructor(config, platformCore = null) {
492
- this.config = config;
493
- if (platformCore) {
494
- this.contributions.push(platformCore);
495
- }
496
- }
497
- register(contribution) {
498
- this.contributions.push(contribution);
499
- this.cached = null;
500
- }
501
- getManifest() {
502
- if (this.cached) return this.cached;
503
- this.cached = this.build();
504
- this.logger.log(`Manifest built: ${this.contributions.length} contribution(s), hash=${this.cached.build.manifestHash.slice(0, 24)}\u2026`);
505
- return this.cached;
506
- }
507
- rebuild() {
508
- this.cached = null;
509
- return this.getManifest();
510
- }
511
- build() {
512
- const merged = this.mergeContributions(this.contributions);
513
- const draft = {
514
- schemaVersion: 1,
515
- project: this.config.project,
516
- build: {
517
- ...this.config.build,
518
- manifestHash: "sha256-pending"
519
- },
520
- planCatalogSnapshot: this.config.planCatalogSnapshot,
521
- capabilities: merged.capabilities,
522
- navigation: merged.navigation,
523
- dashboard: merged.dashboard,
524
- tenants: merged.tenants,
525
- audit: merged.audit
526
- };
527
- return {
528
- ...draft,
529
- build: {
530
- ...draft.build,
531
- manifestHash: this.computeHash(draft)
532
- }
533
- };
534
- }
535
- mergeContributions(contributions) {
536
- const capabilities = {};
537
- const standardPages = {};
538
- const projectPages = [];
539
- const kpiCards = [];
540
- const tenantColumns = [];
541
- const tenantActions = [];
542
- const auditActions = [];
543
- for (const c of contributions) {
544
- if (c.capabilities) Object.assign(capabilities, c.capabilities);
545
- if (c.navigation?.standardPages) Object.assign(standardPages, c.navigation.standardPages);
546
- if (c.navigation?.projectPages) projectPages.push(...c.navigation.projectPages);
547
- if (c.dashboard?.kpiCards) kpiCards.push(...c.dashboard.kpiCards);
548
- if (c.tenants?.columns) tenantColumns.push(...c.tenants.columns);
549
- if (c.tenants?.actions) tenantActions.push(...c.tenants.actions);
550
- if (c.audit?.actions) auditActions.push(...c.audit.actions);
551
- }
552
- kpiCards.sort((a, b) => (b.slotPriority ?? 0) - (a.slotPriority ?? 0));
553
- return {
554
- capabilities,
555
- navigation: {
556
- standardPages,
557
- projectPages
558
- },
559
- dashboard: kpiCards.length > 0 ? {
560
- kpiCards
561
- } : void 0,
562
- tenants: tenantColumns.length > 0 || tenantActions.length > 0 ? {
563
- columns: tenantColumns,
564
- actions: tenantActions
565
- } : void 0,
566
- audit: auditActions.length > 0 ? {
567
- actions: auditActions
568
- } : void 0
569
- };
570
- }
571
- /**
572
- * Deterministic SHA-256 hash over the manifest. Uses the insertion
573
- * order of the object keys — so that semantically meaningful reorders (e.g.
574
- * the order of the sidebar items in `navigation.standardPages`) change the
575
- * hash and invalidate the browser ETag cache. The manifest's own
576
- * `manifestHash` field is hidden before serialization.
577
- */
578
- computeHash(manifest) {
579
- const canonical = stableStringify({
580
- ...manifest,
581
- build: {
582
- ...manifest.build,
583
- manifestHash: void 0
584
- }
585
- });
586
- const digest = (0, import_node_crypto.createHash)("sha256").update(canonical).digest("base64url");
587
- return `sha256-${digest}`;
588
- }
589
- };
590
- AdminManifestService = _ts_decorate7([
591
- (0, import_common7.Injectable)(),
592
- _ts_param5(0, (0, import_common7.Inject)(ADMIN_MANIFEST_CONFIG)),
593
- _ts_param5(1, (0, import_common7.Optional)()),
594
- _ts_param5(1, (0, import_common7.Inject)(PLATFORM_CORE_CONTRIBUTION_TOKEN)),
595
- _ts_metadata5("design:type", Function),
596
- _ts_metadata5("design:paramtypes", [
597
- typeof AdminManifestConfig === "undefined" ? Object : AdminManifestConfig,
598
- Object
599
- ])
600
- ], AdminManifestService);
601
- function stableStringify(value) {
602
- if (value === null) return "null";
603
- if (value === void 0) return "null";
604
- if (typeof value === "number") return Number.isFinite(value) ? String(value) : "null";
605
- if (typeof value === "boolean") return value ? "true" : "false";
606
- if (typeof value === "string") return JSON.stringify(value);
607
- if (Array.isArray(value)) {
608
- return "[" + value.map(stableStringify).join(",") + "]";
609
- }
610
- if (typeof value === "object") {
611
- const obj = value;
612
- const keys = Object.keys(obj).filter((k) => obj[k] !== void 0);
613
- return "{" + keys.map((k) => JSON.stringify(k) + ":" + stableStringify(obj[k])).join(",") + "}";
614
- }
615
- return JSON.stringify(value);
616
- }
617
- __name(stableStringify, "stableStringify");
618
-
619
- // src/admin/admin-public-boot.controller.ts
620
- var import_common8 = require("@nestjs/common");
621
- function _ts_decorate8(decorators, target, key, desc) {
622
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
623
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
624
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
625
- return c > 3 && r && Object.defineProperty(target, key, r), r;
626
- }
627
- __name(_ts_decorate8, "_ts_decorate");
628
- function _ts_metadata6(k, v) {
629
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
630
- }
631
- __name(_ts_metadata6, "_ts_metadata");
632
- function _ts_param6(paramIndex, decorator) {
633
- return function(target, key) {
634
- decorator(target, key, paramIndex);
635
- };
636
- }
637
- __name(_ts_param6, "_ts_param");
638
- var AdminPublicBootController = class {
639
- static {
640
- __name(this, "AdminPublicBootController");
641
- }
642
- config;
643
- constructor(config) {
644
- this.config = config;
645
- }
646
- getBoot() {
647
- return {
648
- project: {
649
- key: this.config.project.key,
650
- displayName: this.config.project.displayName,
651
- label: this.config.project.label,
652
- icon: this.config.project.icon,
653
- logoUrl: this.config.project.logoUrl,
654
- environment: this.config.project.environment
655
- }
656
- };
657
- }
658
- };
659
- _ts_decorate8([
660
- (0, import_common8.Get)("boot"),
661
- _ts_metadata6("design:type", Function),
662
- _ts_metadata6("design:paramtypes", []),
663
- _ts_metadata6("design:returntype", typeof PublicBootResponse === "undefined" ? Object : PublicBootResponse)
664
- ], AdminPublicBootController.prototype, "getBoot", null);
665
- AdminPublicBootController = _ts_decorate8([
666
- (0, import_common8.Controller)("admin"),
667
- _ts_param6(0, (0, import_common8.Inject)(ADMIN_MANIFEST_CONFIG)),
668
- _ts_metadata6("design:type", Function),
669
- _ts_metadata6("design:paramtypes", [
670
- typeof AdminManifestConfig === "undefined" ? Object : AdminManifestConfig
671
- ])
672
- ], AdminPublicBootController);
673
-
674
- // src/admin/manifest-core.ts
675
- var PLATFORM_CORE_MANIFEST_CONTRIBUTION = {
676
- navigation: {
677
- // Key order is render order in the sidebar (within the
678
- // section, see `DEFAULT_NAV_SECTIONS` in the nav-builder). The order per
679
- // section is deliberately curated:
680
- // Overview: dashboard
681
- // Product catalog: discovery → bundles → plans →
682
- // marketingCatalog → promoCodes
683
- // (promoCodes is appended by the app contributions
684
- // and therefore lands at the end of the section)
685
- // Customers: tenants → users → pilots
686
- // System: audit
687
- standardPages: {
688
- dashboard: {
689
- enabled: true,
690
- requiredCapability: "dashboard.read"
691
- },
692
- // SPEC_V2 §3.3 — discovery snapshot of the running backend.
693
- // Apps must set `discovery.read` in their manifest capabilities
694
- // to `true` so that the NavBuilder wires the page into the
695
- // sidebar; otherwise it is filtered out automatically.
696
- discovery: {
697
- enabled: true,
698
- requiredCapability: "discovery.read"
699
- },
700
- // SPEC_V2 §11.1 M3 — Bundle editor in SuperAdmin.
701
- // Apps with a DB-driven catalog set `bundles.read = true`.
702
- bundles: {
703
- enabled: true,
704
- requiredCapability: "bundles.read"
705
- },
706
- plans: {
707
- enabled: true,
708
- requiredCapability: "plans.read"
709
- },
710
- // SPEC_V2 §11.1 M3 — marketing catalog (locale pivot).
711
- // Apps that market a public catalog set
712
- // `marketingProjections.read = true`.
713
- marketingCatalog: {
714
- enabled: true,
715
- requiredCapability: "marketingProjections.read"
716
- },
717
- tenants: {
718
- enabled: true,
719
- requiredCapability: "tenants.read"
720
- },
721
- users: {
722
- enabled: true,
723
- requiredCapability: "users.read"
724
- },
725
- pilots: {
726
- enabled: true,
727
- requiredCapability: "pilots.read"
728
- },
729
- // Audit log deliberately at the end — rarely needed, should
730
- // not obscure the productive pages.
731
- audit: {
732
- enabled: true,
733
- requiredCapability: "audit.read"
734
- },
735
- // Platform email sender (system/registration mails). Apps
736
- // set `platformEmail.read = true` so that the NavBuilder wires the page
737
- // in; otherwise it is filtered out automatically.
738
- platformEmail: {
739
- enabled: true,
740
- requiredCapability: "platformEmail.read"
741
- },
742
- // Platform email history (audit of the sent system mails). Apps
743
- // set `platformEmailHistory.read = true` so that the NavBuilder wires the
744
- // page in; otherwise it is filtered out automatically.
745
- platformEmailHistory: {
746
- enabled: true,
747
- requiredCapability: "platformEmailHistory.read"
748
- }
749
- }
750
- },
751
- tenants: {
752
- actions: [
753
- {
754
- id: "platform.tenants.suspend",
755
- label: "Mandant suspendieren",
756
- actionKey: "tenants.suspend",
757
- requiredCapability: "tenants.suspend",
758
- requiresMfa: true,
759
- confirmType: "typed-slug"
760
- },
761
- {
762
- id: "platform.tenants.reactivate",
763
- label: "Mandant reaktivieren",
764
- actionKey: "tenants.reactivate",
765
- requiredCapability: "tenants.reactivate",
766
- requiresMfa: true,
767
- confirmType: "simple"
768
- },
769
- {
770
- id: "platform.tenants.impersonate",
771
- label: "Impersonate",
772
- actionKey: "tenants.impersonate",
773
- requiredCapability: "tenants.impersonate",
774
- requiresMfa: true,
775
- confirmType: "simple"
776
- },
777
- {
778
- id: "platform.tenants.export",
779
- label: "DSGVO-Export",
780
- actionKey: "tenants.export",
781
- requiredCapability: "tenants.export",
782
- requiresMfa: true,
783
- confirmType: "simple"
784
- },
785
- {
786
- id: "platform.subscriptions.cancel",
787
- label: "Subscription k\xFCndigen",
788
- actionKey: "subscriptions.cancel",
789
- requiredCapability: "subscriptions.cancel",
790
- requiresMfa: true,
791
- confirmType: "typed-slug"
792
- },
793
- {
794
- id: "platform.pilots.grant",
795
- label: "Pilot-Status setzen",
796
- actionKey: "pilots.grant",
797
- requiredCapability: "pilots.grant",
798
- requiresMfa: true,
799
- confirmType: "simple"
800
- },
801
- {
802
- id: "platform.pilots.revoke",
803
- label: "Pilot-Status entziehen",
804
- actionKey: "pilots.revoke",
805
- requiredCapability: "pilots.revoke",
806
- requiresMfa: true,
807
- confirmType: "typed-slug"
808
- },
809
- {
810
- id: "platform.pilots.extend",
811
- label: "Pilot verl\xE4ngern",
812
- actionKey: "pilots.extend",
813
- requiredCapability: "pilots.extend",
814
- requiresMfa: true,
815
- confirmType: "date"
816
- }
817
- ]
818
- },
819
- audit: {
820
- actions: [
821
- {
822
- key: "TENANT_SUSPEND",
823
- label: "Mandant suspendiert",
824
- severity: "high"
825
- },
826
- {
827
- key: "TENANT_REACTIVATE",
828
- label: "Mandant reaktiviert",
829
- severity: "medium"
830
- },
831
- {
832
- key: "TENANT_IMPERSONATE",
833
- label: "Impersonation gestartet",
834
- severity: "high"
835
- },
836
- {
837
- key: "TENANT_EXPORT",
838
- label: "DSGVO-Export ausgel\xF6st",
839
- severity: "medium"
840
- },
841
- {
842
- key: "PILOT_CREATE",
843
- label: "Pilot-Tenant angelegt",
844
- severity: "medium"
845
- },
846
- {
847
- key: "PILOT_GRANT",
848
- label: "Pilot-Status gesetzt",
849
- severity: "medium"
850
- },
851
- {
852
- key: "PILOT_REVOKE",
853
- label: "Pilot-Status entzogen",
854
- severity: "medium"
855
- },
856
- {
857
- key: "PILOT_EXTEND",
858
- label: "Pilot verl\xE4ngert",
859
- severity: "low"
860
- },
861
- {
862
- key: "PLAN_VERSION_PUBLISH",
863
- label: "Plan-Version freigegeben",
864
- severity: "high"
865
- },
866
- {
867
- key: "PLAN_VERSION_TERMINATE",
868
- label: "Plan-Version terminiert",
869
- severity: "high"
870
- },
871
- {
872
- key: "SUBSCRIPTION_CANCEL",
873
- label: "Subscription gek\xFCndigt",
874
- severity: "high"
875
- },
876
- {
877
- key: "USER_RESET_PASSWORD",
878
- label: "Passwort-Reset ausgel\xF6st",
879
- severity: "medium"
880
- },
881
- {
882
- key: "USER_DEACTIVATE",
883
- label: "User deaktiviert",
884
- severity: "high"
885
- },
886
- {
887
- key: "USER_REASSIGN_ADMIN",
888
- label: "Admin-Rolle \xFCbertragen",
889
- severity: "high"
890
- },
891
- {
892
- key: "PLATFORM_EMAIL_PROVIDER_CREATE",
893
- label: "Plattform-Absender angelegt",
894
- severity: "medium"
895
- },
896
- {
897
- key: "PLATFORM_EMAIL_PROVIDER_UPDATE",
898
- label: "Plattform-Absender ge\xE4ndert",
899
- severity: "medium"
900
- },
901
- {
902
- key: "PLATFORM_EMAIL_PROVIDER_DELETE",
903
- label: "Plattform-Absender gel\xF6scht",
904
- severity: "medium"
905
- },
906
- {
907
- key: "PLATFORM_EMAIL_PROVIDER_TEST",
908
- label: "Plattform-Absender Test-Versand",
909
- severity: "low"
910
- },
911
- {
912
- key: "EMAIL_HISTORY_DELETE",
913
- label: "Plattform-E-Mail aus Verlauf entfernt",
914
- severity: "medium"
915
- },
916
- {
917
- key: "EMAIL_HISTORY_RESEND",
918
- label: "Plattform-E-Mail erneut versendet",
919
- severity: "low"
920
- }
921
- ]
922
- }
923
- };
924
-
925
- // src/admin/admin-manifest.module.ts
926
- function _ts_decorate9(decorators, target, key, desc) {
927
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
928
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
929
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
930
- return c > 3 && r && Object.defineProperty(target, key, r), r;
931
- }
932
- __name(_ts_decorate9, "_ts_decorate");
933
- function _ts_metadata7(k, v) {
934
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
935
- }
936
- __name(_ts_metadata7, "_ts_metadata");
937
- function _ts_param7(paramIndex, decorator) {
938
- return function(target, key) {
939
- decorator(target, key, paramIndex);
940
- };
941
- }
942
- __name(_ts_param7, "_ts_param");
943
- function asConfigProvider(impl) {
944
- if (typeof impl === "object" && impl !== null && "useFactory" in impl && typeof impl.useFactory === "function") {
945
- return {
946
- provide: ADMIN_MANIFEST_CONFIG,
947
- useFactory: impl.useFactory,
948
- inject: impl.inject
949
- };
950
- }
951
- return {
952
- provide: ADMIN_MANIFEST_CONFIG,
953
- useValue: impl
954
- };
955
- }
956
- __name(asConfigProvider, "asConfigProvider");
957
- function buildManifestController(guards, reloadGuards) {
958
- const reloadChain = [
959
- ...guards,
960
- ...reloadGuards
961
- ];
962
- let GeneratedAdminManifestController = class GeneratedAdminManifestController {
963
- static {
964
- __name(this, "GeneratedAdminManifestController");
965
- }
966
- manifest;
967
- // Explicit @Inject instead of type reflection: tsup/esbuild do not emit
968
- // `design:paramtypes` metadata, so Nest cannot otherwise resolve the
969
- // service type at the constructor.
970
- constructor(manifest) {
971
- this.manifest = manifest;
972
- }
973
- getManifest(ifNoneMatch, res) {
974
- const m = this.manifest.getManifest();
975
- const etag = `"${m.build.manifestHash}"`;
976
- res.header("ETag", etag);
977
- if (ifNoneMatch && ifNoneMatch === etag) {
978
- res.status(import_common9.HttpStatus.NOT_MODIFIED);
979
- return null;
980
- }
981
- return m;
982
- }
983
- reload() {
984
- const m = this.manifest.rebuild();
985
- return {
986
- manifestHash: m.build.manifestHash,
987
- reloadedAt: (/* @__PURE__ */ new Date()).toISOString()
988
- };
989
- }
990
- };
991
- _ts_decorate9([
992
- (0, import_common9.Get)("manifest"),
993
- (0, import_common9.Header)("Cache-Control", "private, max-age=60, must-revalidate"),
994
- _ts_param7(0, (0, import_common9.Headers)("if-none-match")),
995
- _ts_param7(1, (0, import_common9.Res)({
996
- passthrough: true
997
- })),
998
- _ts_metadata7("design:type", Function),
999
- _ts_metadata7("design:paramtypes", [
1000
- Object,
1001
- typeof HttpResponseLike === "undefined" ? Object : HttpResponseLike
1002
- ]),
1003
- _ts_metadata7("design:returntype", void 0)
1004
- ], GeneratedAdminManifestController.prototype, "getManifest", null);
1005
- _ts_decorate9([
1006
- (0, import_common9.Post)("manifest/reload"),
1007
- (0, import_common9.UseGuards)(...reloadChain),
1008
- (0, import_common9.HttpCode)(import_common9.HttpStatus.OK),
1009
- _ts_metadata7("design:type", Function),
1010
- _ts_metadata7("design:paramtypes", []),
1011
- _ts_metadata7("design:returntype", void 0)
1012
- ], GeneratedAdminManifestController.prototype, "reload", null);
1013
- GeneratedAdminManifestController = _ts_decorate9([
1014
- (0, import_common9.Controller)("admin"),
1015
- (0, import_common9.UseGuards)(...guards),
1016
- _ts_param7(0, (0, import_common9.Inject)(AdminManifestService)),
1017
- _ts_metadata7("design:type", Function),
1018
- _ts_metadata7("design:paramtypes", [
1019
- typeof AdminManifestService === "undefined" ? Object : AdminManifestService
1020
- ])
1021
- ], GeneratedAdminManifestController);
1022
- return GeneratedAdminManifestController;
1023
- }
1024
- __name(buildManifestController, "buildManifestController");
1025
- var AdminManifestModule = class _AdminManifestModule {
1026
- static {
1027
- __name(this, "AdminManifestModule");
1028
- }
1029
- static forRoot(options) {
1030
- const includeManifestController = options.includeManifestController ?? true;
1031
- const registerPlatformCore = options.registerPlatformCore ?? true;
1032
- const controllers = [
1033
- AdminPublicBootController
1034
- ];
1035
- if (includeManifestController) {
1036
- if (options.guards === void 0) {
1037
- throw new Error("AdminManifestModule.forRoot: `guards` ist Pflicht, wenn `includeManifestController` aktiv ist (Default `true`). Setze `guards: [JwtAuthGuard, SuperAdminGuard]` (oder analog) \u2014 oder `guards: []`, um den Endpoint absichtlich auth-frei zu betreiben. Alternative: `includeManifestController: false` und einen eigenen Wrapping-Controller registrieren.");
1038
- }
1039
- controllers.push(buildManifestController(options.guards, options.reloadGuards ?? []));
1040
- }
1041
- const providers = [
1042
- asConfigProvider(options.config),
1043
- AdminManifestService,
1044
- ...options.extraProviders ?? []
1045
- ];
1046
- if (registerPlatformCore) {
1047
- providers.push({
1048
- provide: PLATFORM_CORE_CONTRIBUTION_TOKEN,
1049
- useValue: PLATFORM_CORE_MANIFEST_CONTRIBUTION
1050
- });
1051
- }
1052
- return {
1053
- module: _AdminManifestModule,
1054
- global: options.global ?? false,
1055
- imports: options.imports ?? [],
1056
- controllers,
1057
- providers,
1058
- exports: [
1059
- ADMIN_MANIFEST_CONFIG,
1060
- AdminManifestService
1061
- ]
1062
- };
1063
- }
1064
- };
1065
- AdminManifestModule = _ts_decorate9([
1066
- (0, import_common9.Module)({})
1067
- ], AdminManifestModule);
1068
-
1069
- // src/billing/plan-catalog.module.ts
1070
- var import_common10 = require("@nestjs/common");
1071
-
1072
- // src/billing/plan-catalog-from-snapshot.ts
1073
- function buildPlanCatalogFromSnapshot(settings, snapshot) {
1074
- const liveByPlanKey = new Map(snapshot.livePlanVersions.map((v) => [
1075
- v.planId,
1076
- v
1077
- ]));
1078
- const plans = snapshot.plans.filter((p) => p.deletedAt === null).sort((a, b) => a.sortOrder - b.sortOrder).map((stem) => {
1079
- const live = liveByPlanKey.get(stem.planKey);
1080
- if (!live) {
1081
- return {
1082
- id: stem.planKey,
1083
- name: stem.label,
1084
- tagline: stem.description ?? void 0,
1085
- marketed: false,
1086
- monthlyNet: null,
1087
- yearlyNet: null,
1088
- features: [],
1089
- quotas: {}
1090
- };
1091
- }
1092
- return {
1093
- id: stem.planKey,
1094
- name: stem.label,
1095
- tagline: stem.description ?? void 0,
1096
- marketed: live.marketed,
1097
- monthlyNet: parseFloat(live.monthlyNet),
1098
- yearlyNet: parseFloat(live.yearlyNet),
1099
- features: live.features,
1100
- quotas: live.quotas ?? {}
1101
- };
1102
- });
1103
- const features = snapshot.featureEntries.filter((f) => f.deletedAt === null).sort((a, b) => a.sortOrder - b.sortOrder).map((row) => ({
1104
- key: row.featureKey,
1105
- label: row.label,
1106
- icon: row.icon ?? void 0,
1107
- tier: row.tier ?? void 0,
1108
- plannedOnly: row.plannedOnly
1109
- }));
1110
- return {
1111
- schemaVersion: 1,
1112
- projectKey: settings.projectKey,
1113
- ...settings.app ? {
1114
- app: settings.app
1115
- } : {},
1116
- currency: settings.currency,
1117
- vatRate: settings.vatRate,
1118
- ...settings.marketing ? {
1119
- marketing: settings.marketing
1120
- } : {},
1121
- features,
1122
- plans
1123
- };
1124
- }
1125
- __name(buildPlanCatalogFromSnapshot, "buildPlanCatalogFromSnapshot");
1126
-
1127
- // src/billing/plan-catalog.module.ts
1128
- function _ts_decorate10(decorators, target, key, desc) {
1129
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1130
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1131
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1132
- return c > 3 && r && Object.defineProperty(target, key, r), r;
1133
- }
1134
- __name(_ts_decorate10, "_ts_decorate");
1135
- var PLAN_CATALOG_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/nest/PLAN_CATALOG");
1136
- var PLAN_CATALOG_READ_SINK_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/nest/PLAN_CATALOG_READ_SINK");
1137
- var PlanCatalogModule = class _PlanCatalogModule {
1138
- static {
1139
- __name(this, "PlanCatalogModule");
1140
- }
1141
- static forRoot(options) {
1142
- return {
1143
- module: _PlanCatalogModule,
1144
- global: options.global ?? true,
1145
- imports: options.imports ?? [],
1146
- providers: [
1147
- ...options.extraProviders ?? [],
1148
- asProvider(PLAN_CATALOG_READ_SINK_TOKEN, options.sink),
1149
- {
1150
- provide: PLAN_CATALOG_TOKEN,
1151
- useFactory: /* @__PURE__ */ __name(async (sink) => {
1152
- const snapshot = await sink.loadSnapshot(options.projectKey);
1153
- return buildPlanCatalogFromSnapshot({
1154
- projectKey: options.projectKey,
1155
- app: options.app,
1156
- currency: options.currency,
1157
- vatRate: options.vatRate,
1158
- marketing: options.marketing
1159
- }, snapshot);
1160
- }, "useFactory"),
1161
- inject: [
1162
- PLAN_CATALOG_READ_SINK_TOKEN
1163
- ]
1164
- }
1165
- ],
1166
- exports: [
1167
- PLAN_CATALOG_TOKEN,
1168
- PLAN_CATALOG_READ_SINK_TOKEN
1169
- ]
1170
- };
1171
- }
1172
- /**
1173
- * Tests / in-memory setup: takes the catalog object directly, without a
1174
- * sink/DB lookup. For unit tests + test bootstraps.
1175
- */
1176
- static forRootWithCatalog(catalog, opts = {}) {
1177
- return {
1178
- module: _PlanCatalogModule,
1179
- providers: [
1180
- {
1181
- provide: PLAN_CATALOG_TOKEN,
1182
- useValue: catalog
1183
- }
1184
- ],
1185
- exports: [
1186
- PLAN_CATALOG_TOKEN
1187
- ],
1188
- global: opts.global ?? true
1189
- };
1190
- }
1191
- };
1192
- PlanCatalogModule = _ts_decorate10([
1193
- (0, import_common10.Module)({})
1194
- ], PlanCatalogModule);
1195
-
1196
- // src/discovery/discovery.module.ts
1197
- var import_common13 = require("@nestjs/common");
1198
- var import_core3 = require("@nestjs/core");
1199
-
1200
- // src/discovery/discovery.controller.ts
1201
- var import_common12 = require("@nestjs/common");
1202
-
1203
- // src/discovery/discovery.scanner.ts
1204
- var import_node_crypto2 = require("crypto");
1205
- var import_node_fs = require("fs");
1206
- var import_node_path = require("path");
1207
- var import_common11 = require("@nestjs/common");
1208
- var import_core2 = require("@nestjs/core");
1209
-
1210
- // src/discovery/tokens.ts
1211
- var IMPLEMENTS_CAPABILITY_KEY = "discovery:implements-capability";
1212
- var DEFINES_QUOTA_KEY = "discovery:defines-quota";
1213
- var ENFORCE_QUOTA_KEY = "discovery:enforce-quota";
1214
- var DISCOVERY_SNAPSHOT_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/DiscoverySnapshot");
1215
-
1216
- // src/discovery/discovery.scanner.ts
1217
- function _ts_decorate11(decorators, target, key, desc) {
1218
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1219
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1220
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1221
- return c > 3 && r && Object.defineProperty(target, key, r), r;
1222
- }
1223
- __name(_ts_decorate11, "_ts_decorate");
1224
- function _ts_metadata8(k, v) {
1225
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1226
- }
1227
- __name(_ts_metadata8, "_ts_metadata");
1228
- function _ts_param8(paramIndex, decorator) {
1229
- return function(target, key) {
1230
- decorator(target, key, paramIndex);
1231
- };
1232
- }
1233
- __name(_ts_param8, "_ts_param");
1234
- var DEFAULT_APP_INFO = {
1235
- key: "unknown",
1236
- version: "0.0.0"
1237
- };
1238
- var DISCOVERY_APP_INFO_TOKEN = /* @__PURE__ */ Symbol("DISCOVERY_APP_INFO");
1239
- var DISCOVERY_SNAPSHOT_PATH_TOKEN = /* @__PURE__ */ Symbol("DISCOVERY_SNAPSHOT_PATH");
1240
- var DiscoveryScanner = class _DiscoveryScanner {
1241
- static {
1242
- __name(this, "DiscoveryScanner");
1243
- }
1244
- discoveryService;
1245
- metadataScanner;
1246
- reflector;
1247
- appInfo;
1248
- snapshotPath;
1249
- logger = new import_common11.Logger(_DiscoveryScanner.name);
1250
- snapshot = null;
1251
- // Explicit @Inject instead of type reflection: in the platform package
1252
- // build tsup/esbuild do not emit `design:paramtypes` metadata, so Nest
1253
- // cannot otherwise resolve the DI targets at the constructor
1254
- // (UndefinedDependencyException at the consumer's boot). Pattern
1255
- // analogous to admin/admin-manifest.module.ts.
1256
- constructor(discoveryService, metadataScanner, reflector, appInfo = DEFAULT_APP_INFO, snapshotPath = null) {
1257
- this.discoveryService = discoveryService;
1258
- this.metadataScanner = metadataScanner;
1259
- this.reflector = reflector;
1260
- this.appInfo = appInfo;
1261
- this.snapshotPath = snapshotPath;
1262
- }
1263
- onApplicationBootstrap() {
1264
- this.snapshot = this.buildSnapshot();
1265
- this.logger.log(`Discovery snapshot built: ${this.snapshot.capabilities.length} capabilities, ${this.snapshot.features.length} features, ${this.snapshot.quotas.length} quotas (hash=${this.snapshot.hash.slice(0, 12)}\u2026)`);
1266
- if (this.snapshotPath) {
1267
- this.persistSnapshot(this.snapshotPath, this.snapshot);
1268
- }
1269
- }
1270
- persistSnapshot(path, snapshot) {
1271
- try {
1272
- (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(path), {
1273
- recursive: true
1274
- });
1275
- (0, import_node_fs.writeFileSync)(path, JSON.stringify(snapshot, null, 2) + "\n", "utf-8");
1276
- this.logger.log(`Discovery snapshot persisted to ${path}`);
1277
- } catch (err) {
1278
- this.logger.warn(`Discovery snapshot konnte nicht nach ${path} geschrieben werden: ${err.message}`);
1279
- }
1280
- }
1281
- /**
1282
- * Returns the boot-time snapshot. Before `onApplicationBootstrap` (e.g. in
1283
- * tests) the snapshot is built lazily.
1284
- */
1285
- getSnapshot() {
1286
- if (!this.snapshot) {
1287
- this.snapshot = this.buildSnapshot();
1288
- }
1289
- return this.snapshot;
1290
- }
1291
- /**
1292
- * Forces a re-scan (SuperAdmin "re-run discovery" or tests with
1293
- * dynamically registered providers). `scannedAt` is set anew and the
1294
- * snapshot — as at boot — is persisted to disk so that the last scan
1295
- * actually performed is preserved.
1296
- */
1297
- rebuildSnapshot() {
1298
- this.snapshot = this.buildSnapshot();
1299
- if (this.snapshotPath) {
1300
- this.persistSnapshot(this.snapshotPath, this.snapshot);
1301
- }
1302
- return this.snapshot;
1303
- }
1304
- buildSnapshot() {
1305
- const capabilitiesByKey = /* @__PURE__ */ new Map();
1306
- const quotasByKey = /* @__PURE__ */ new Map();
1307
- const enforceRefs = [];
1308
- const wrappers = [
1309
- ...this.discoveryService.getProviders(),
1310
- ...this.discoveryService.getControllers()
1311
- ];
1312
- for (const wrapper of wrappers) {
1313
- const instance = wrapper.instance;
1314
- if (!instance || typeof instance !== "object") continue;
1315
- const ctor = instance.constructor;
1316
- if (!ctor) continue;
1317
- const quotaOpts = this.reflector.get(DEFINES_QUOTA_KEY, ctor);
1318
- if (quotaOpts) {
1319
- this.registerQuota(quotasByKey, quotaOpts, ctor.name);
1320
- }
1321
- const prototype = Object.getPrototypeOf(instance);
1322
- const methodNames = this.metadataScanner.getAllMethodNames(prototype);
1323
- for (const methodName of methodNames) {
1324
- const methodRef = instance[methodName];
1325
- if (typeof methodRef !== "function") continue;
1326
- const capMeta = this.reflector.get(IMPLEMENTS_CAPABILITY_KEY, methodRef);
1327
- if (capMeta) {
1328
- this.registerCapability(capabilitiesByKey, capMeta, `${ctor.name}.${methodName}`);
1329
- }
1330
- const enforceMeta = this.reflector.get(ENFORCE_QUOTA_KEY, methodRef);
1331
- if (enforceMeta && capMeta) {
1332
- enforceRefs.push({
1333
- capabilityKey: capMeta.capabilityKey,
1334
- quotaKey: enforceMeta.quotaKey
1335
- });
1336
- }
1337
- }
1338
- }
1339
- const capabilities = sortByKey([
1340
- ...capabilitiesByKey.values()
1341
- ], (c) => c.capabilityKey);
1342
- const features = aggregateFeatures(capabilities);
1343
- const quotas = sortByKey(decorateQuotasWithEnforcers([
1344
- ...quotasByKey.values()
1345
- ], enforceRefs), (q) => q.quotaKey);
1346
- const snapshot = {
1347
- schemaVersion: 1,
1348
- scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
1349
- app: {
1350
- ...this.appInfo
1351
- },
1352
- capabilities,
1353
- features,
1354
- quotas,
1355
- hash: ""
1356
- };
1357
- snapshot.hash = computeSnapshotHash(snapshot);
1358
- return snapshot;
1359
- }
1360
- registerCapability(target, meta, declaredAt) {
1361
- const existing = target.get(meta.capabilityKey);
1362
- if (existing) {
1363
- this.logger.warn(`Capability '${meta.capabilityKey}' wird an mehreren Stellen deklariert (${existing.declaredAt} und ${declaredAt}); erste Deklaration gewinnt`);
1364
- return;
1365
- }
1366
- target.set(meta.capabilityKey, {
1367
- capabilityKey: meta.capabilityKey,
1368
- label: meta.label ?? null,
1369
- feature: meta.feature ?? null,
1370
- status: meta.status ?? "active",
1371
- kind: meta.kind ?? "endpoint",
1372
- owner: meta.owner ?? null,
1373
- replacementKey: meta.replacementKey ?? null,
1374
- removalPlannedAt: meta.removalPlannedAt ?? null,
1375
- reason: meta.reason ?? null,
1376
- requires: normalizeKeyList(meta.requires),
1377
- replaces: normalizeKeyList(meta.replaces),
1378
- declaredAt
1379
- });
1380
- }
1381
- registerQuota(target, opts, declaredAt) {
1382
- const existing = target.get(opts.key);
1383
- if (existing) {
1384
- this.logger.warn(`Quota '${opts.key}' wird an mehreren Stellen definiert (${existing.declaredAt} und ${declaredAt}); erste Definition gewinnt`);
1385
- return;
1386
- }
1387
- target.set(opts.key, {
1388
- quotaKey: opts.key,
1389
- label: opts.label,
1390
- unit: opts.unit,
1391
- policy: opts.policy,
1392
- feature: opts.feature ?? null,
1393
- replaces: normalizeKeyList(opts.replaces),
1394
- declaredAt,
1395
- enforcedBy: []
1396
- });
1397
- }
1398
- };
1399
- DiscoveryScanner = _ts_decorate11([
1400
- (0, import_common11.Injectable)(),
1401
- _ts_param8(0, (0, import_common11.Inject)(import_core2.DiscoveryService)),
1402
- _ts_param8(1, (0, import_common11.Inject)(import_core2.MetadataScanner)),
1403
- _ts_param8(2, (0, import_common11.Inject)(import_core2.Reflector)),
1404
- _ts_param8(3, (0, import_common11.Optional)()),
1405
- _ts_param8(3, (0, import_common11.Inject)(DISCOVERY_APP_INFO_TOKEN)),
1406
- _ts_param8(4, (0, import_common11.Optional)()),
1407
- _ts_param8(4, (0, import_common11.Inject)(DISCOVERY_SNAPSHOT_PATH_TOKEN)),
1408
- _ts_metadata8("design:type", Function),
1409
- _ts_metadata8("design:paramtypes", [
1410
- typeof import_core2.DiscoveryService === "undefined" ? Object : import_core2.DiscoveryService,
1411
- typeof import_core2.MetadataScanner === "undefined" ? Object : import_core2.MetadataScanner,
1412
- typeof import_core2.Reflector === "undefined" ? Object : import_core2.Reflector,
1413
- typeof DiscoveryAppInfo === "undefined" ? Object : DiscoveryAppInfo,
1414
- Object
1415
- ])
1416
- ], DiscoveryScanner);
1417
- function aggregateFeatures(capabilities) {
1418
- const byFeature = /* @__PURE__ */ new Map();
1419
- for (const cap of capabilities) {
1420
- if (!cap.feature) continue;
1421
- const acc = byFeature.get(cap.feature) ?? {
1422
- capabilityKeys: [],
1423
- requires: /* @__PURE__ */ new Set(),
1424
- replaces: /* @__PURE__ */ new Set()
1425
- };
1426
- acc.capabilityKeys.push(cap.capabilityKey);
1427
- for (const key of cap.requires ?? []) acc.requires.add(key);
1428
- for (const key of cap.replaces ?? []) acc.replaces.add(key);
1429
- byFeature.set(cap.feature, acc);
1430
- }
1431
- return sortByKey([
1432
- ...byFeature.entries()
1433
- ].map(([featureKey, acc]) => {
1434
- acc.requires.delete(featureKey);
1435
- acc.replaces.delete(featureKey);
1436
- return {
1437
- featureKey,
1438
- capabilityKeys: [
1439
- ...acc.capabilityKeys
1440
- ].sort((a, b) => a.localeCompare(b)),
1441
- requires: setToSortedListOrNull(acc.requires),
1442
- replaces: setToSortedListOrNull(acc.replaces)
1443
- };
1444
- }), (f) => f.featureKey);
1445
- }
1446
- __name(aggregateFeatures, "aggregateFeatures");
1447
- function normalizeKeyList(keys) {
1448
- if (!keys || keys.length === 0) return null;
1449
- return [
1450
- ...new Set(keys)
1451
- ].sort((a, b) => a.localeCompare(b));
1452
- }
1453
- __name(normalizeKeyList, "normalizeKeyList");
1454
- function setToSortedListOrNull(keys) {
1455
- if (keys.size === 0) return null;
1456
- return [
1457
- ...keys
1458
- ].sort((a, b) => a.localeCompare(b));
1459
- }
1460
- __name(setToSortedListOrNull, "setToSortedListOrNull");
1461
- function decorateQuotasWithEnforcers(quotas, enforceRefs) {
1462
- const enforcedByQuota = /* @__PURE__ */ new Map();
1463
- for (const ref of enforceRefs) {
1464
- const set = enforcedByQuota.get(ref.quotaKey) ?? /* @__PURE__ */ new Set();
1465
- set.add(ref.capabilityKey);
1466
- enforcedByQuota.set(ref.quotaKey, set);
1467
- }
1468
- return quotas.map((q) => ({
1469
- ...q,
1470
- enforcedBy: [
1471
- ...enforcedByQuota.get(q.quotaKey) ?? /* @__PURE__ */ new Set()
1472
- ].sort((a, b) => a.localeCompare(b))
1473
- }));
1474
- }
1475
- __name(decorateQuotasWithEnforcers, "decorateQuotasWithEnforcers");
1476
- function sortByKey(items, keyFn) {
1477
- return [
1478
- ...items
1479
- ].sort((a, b) => keyFn(a).localeCompare(keyFn(b)));
1480
- }
1481
- __name(sortByKey, "sortByKey");
1482
- function computeSnapshotHash(snapshot) {
1483
- const stableInput = {
1484
- schemaVersion: snapshot.schemaVersion,
1485
- appKey: snapshot.app.key,
1486
- capabilities: snapshot.capabilities.map((c) => ({
1487
- ...c
1488
- })),
1489
- features: snapshot.features,
1490
- quotas: snapshot.quotas
1491
- };
1492
- const json = canonicalStringify(stableInput);
1493
- return `sha256-${(0, import_node_crypto2.createHash)("sha256").update(json).digest("hex")}`;
1494
- }
1495
- __name(computeSnapshotHash, "computeSnapshotHash");
1496
- function canonicalStringify(value) {
1497
- return JSON.stringify(value, (_key, val) => {
1498
- if (val && typeof val === "object" && !Array.isArray(val)) {
1499
- const sorted = {};
1500
- for (const k of Object.keys(val).sort()) {
1501
- sorted[k] = val[k];
1502
- }
1503
- return sorted;
1504
- }
1505
- return val;
1506
- });
1507
- }
1508
- __name(canonicalStringify, "canonicalStringify");
1509
-
1510
- // src/discovery/discovery.controller.ts
1511
- function _ts_decorate12(decorators, target, key, desc) {
1512
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1513
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1514
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1515
- return c > 3 && r && Object.defineProperty(target, key, r), r;
1516
- }
1517
- __name(_ts_decorate12, "_ts_decorate");
1518
- function _ts_metadata9(k, v) {
1519
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1520
- }
1521
- __name(_ts_metadata9, "_ts_metadata");
1522
- function _ts_param9(paramIndex, decorator) {
1523
- return function(target, key) {
1524
- decorator(target, key, paramIndex);
1525
- };
1526
- }
1527
- __name(_ts_param9, "_ts_param");
1528
- function buildDiscoveryController(guards) {
1529
- let GeneratedDiscoveryController = class GeneratedDiscoveryController {
1530
- static {
1531
- __name(this, "GeneratedDiscoveryController");
1532
- }
1533
- scanner;
1534
- // Explicit @Inject instead of type reflection: tsup/esbuild do not emit
1535
- // any `design:paramtypes` metadata, so otherwise Nest cannot resolve the
1536
- // service type on the constructor.
1537
- constructor(scanner) {
1538
- this.scanner = scanner;
1539
- }
1540
- getDiscovery(ifNoneMatch, res) {
1541
- const snapshot = this.scanner.getSnapshot();
1542
- const etag = `"${snapshot.hash}-${snapshot.scannedAt}"`;
1543
- res.header("ETag", etag);
1544
- if (ifNoneMatch && ifNoneMatch === etag) {
1545
- res.status(import_common12.HttpStatus.NOT_MODIFIED);
1546
- return null;
1547
- }
1548
- return snapshot;
1549
- }
1550
- /**
1551
- * Forces a fresh code scan and returns the new snapshot.
1552
- * `scannedAt` is updated; the Capability/Feature/Quota data
1553
- * are identical within the running process (code is stable at runtime).
1554
- */
1555
- rescanDiscovery() {
1556
- return this.scanner.rebuildSnapshot();
1557
- }
1558
- };
1559
- _ts_decorate12([
1560
- (0, import_common12.Get)("discovery"),
1561
- (0, import_common12.Header)("Cache-Control", "private, no-cache"),
1562
- _ts_param9(0, (0, import_common12.Headers)("if-none-match")),
1563
- _ts_param9(1, (0, import_common12.Res)({
1564
- passthrough: true
1565
- })),
1566
- _ts_metadata9("design:type", Function),
1567
- _ts_metadata9("design:paramtypes", [
1568
- Object,
1569
- typeof HttpResponseLike === "undefined" ? Object : HttpResponseLike
1570
- ]),
1571
- _ts_metadata9("design:returntype", Object)
1572
- ], GeneratedDiscoveryController.prototype, "getDiscovery", null);
1573
- _ts_decorate12([
1574
- (0, import_common12.Post)("discovery/rescan"),
1575
- _ts_metadata9("design:type", Function),
1576
- _ts_metadata9("design:paramtypes", []),
1577
- _ts_metadata9("design:returntype", typeof DiscoverySnapshot === "undefined" ? Object : DiscoverySnapshot)
1578
- ], GeneratedDiscoveryController.prototype, "rescanDiscovery", null);
1579
- GeneratedDiscoveryController = _ts_decorate12([
1580
- (0, import_common12.Controller)("admin"),
1581
- (0, import_common12.UseGuards)(...guards),
1582
- _ts_param9(0, (0, import_common12.Inject)(DiscoveryScanner)),
1583
- _ts_metadata9("design:type", Function),
1584
- _ts_metadata9("design:paramtypes", [
1585
- typeof DiscoveryScanner === "undefined" ? Object : DiscoveryScanner
1586
- ])
1587
- ], GeneratedDiscoveryController);
1588
- return GeneratedDiscoveryController;
1589
- }
1590
- __name(buildDiscoveryController, "buildDiscoveryController");
1591
-
1592
- // src/discovery/discovery.module.ts
1593
- function _ts_decorate13(decorators, target, key, desc) {
1594
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1595
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1596
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1597
- return c > 3 && r && Object.defineProperty(target, key, r), r;
1598
- }
1599
- __name(_ts_decorate13, "_ts_decorate");
1600
- var DiscoveryModule = class _DiscoveryModule {
1601
- static {
1602
- __name(this, "DiscoveryModule");
1603
- }
1604
- static forRoot(options) {
1605
- const isGlobal = options.isGlobal ?? true;
1606
- const controllers = [];
1607
- if (options.controller) {
1608
- controllers.push(buildDiscoveryController(options.controller.guards));
1609
- }
1610
- const providers = [
1611
- {
1612
- provide: DISCOVERY_APP_INFO_TOKEN,
1613
- useValue: options.app
1614
- },
1615
- {
1616
- provide: DISCOVERY_SNAPSHOT_PATH_TOKEN,
1617
- useValue: options.snapshotPath ?? null
1618
- },
1619
- DiscoveryScanner,
1620
- {
1621
- provide: DISCOVERY_SNAPSHOT_TOKEN,
1622
- useFactory: /* @__PURE__ */ __name((scanner) => scanner.getSnapshot(), "useFactory"),
1623
- inject: [
1624
- DiscoveryScanner
1625
- ]
1626
- }
1627
- ];
1628
- return {
1629
- module: _DiscoveryModule,
1630
- imports: [
1631
- import_core3.DiscoveryModule,
1632
- ...options.imports ?? []
1633
- ],
1634
- controllers,
1635
- providers,
1636
- exports: [
1637
- DiscoveryScanner,
1638
- DISCOVERY_SNAPSHOT_TOKEN
1639
- ],
1640
- global: isGlobal
1641
- };
1642
- }
1643
- };
1644
- DiscoveryModule = _ts_decorate13([
1645
- (0, import_common13.Module)({})
1646
- ], DiscoveryModule);
1647
-
1648
- // src/entitlement/module.ts
1649
- var import_common15 = require("@nestjs/common");
1650
-
1651
- // src/entitlement/service.ts
1652
- var import_common14 = require("@nestjs/common");
1653
-
1654
- // src/catalog/tokens.ts
1655
- var BUNDLE_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol("BUNDLE_REPOSITORY");
1656
-
1657
- // src/billing/subscription-bundles.tokens.ts
1658
- var SUBSCRIPTION_BUNDLE_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/SubscriptionBundleRepository");
1659
-
1660
- // src/subscription-contract/tokens.ts
1661
- var SUBSCRIPTION_CONTRACT_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/SubscriptionContractRepository");
1662
-
1663
- // src/billing/plan-helpers.ts
1664
- function isFeaturePlannedOnly(catalog, featureKey) {
1665
- const def = (catalog.features ?? []).find((f) => f.key === featureKey);
1666
- return def?.plannedOnly === true;
1667
- }
1668
- __name(isFeaturePlannedOnly, "isFeaturePlannedOnly");
1669
-
1670
- // src/entitlement/aggregation.ts
1671
- function filterActiveSubscriptionBundles(bundles, now) {
1672
- return bundles.filter((b) => b.canceledEffectiveAt === null || b.canceledEffectiveAt > now);
1673
- }
1674
- __name(filterActiveSubscriptionBundles, "filterActiveSubscriptionBundles");
1675
- function aggregateSubscriptionBundleQuotas(bundles) {
1676
- const sums = {};
1677
- for (const bundle of bundles) {
1678
- for (const [key, value] of Object.entries(bundle.quotas)) {
1679
- if (sums[key] === -1 || value === -1) {
1680
- sums[key] = -1;
1681
- } else {
1682
- sums[key] = (sums[key] ?? 0) + value;
1683
- }
1684
- }
1685
- }
1686
- return sums;
1687
- }
1688
- __name(aggregateSubscriptionBundleQuotas, "aggregateSubscriptionBundleQuotas");
1689
- function collectSubscriptionBundleFeatures(bundles) {
1690
- const set = /* @__PURE__ */ new Set();
1691
- for (const bundle of bundles) {
1692
- for (const f of bundle.features) set.add(f);
1693
- }
1694
- return Array.from(set);
1695
- }
1696
- __name(collectSubscriptionBundleFeatures, "collectSubscriptionBundleFeatures");
1697
- function aggregateContractLineItemEntitlements(lineItems, fallbackPlan = "UNKNOWN") {
1698
- const planItem = lineItems.find((item) => item.kind === "plan");
1699
- const plan = planItem?.sourceKey ?? fallbackPlan;
1700
- const quotas = {};
1701
- const features = /* @__PURE__ */ new Set();
1702
- for (const item of lineItems) {
1703
- for (const feature of item.featuresSnapshot) {
1704
- features.add(feature);
1705
- }
1706
- for (const [key, value] of Object.entries(item.quotaEffectsSnapshot)) {
1707
- if (quotas[key] === -1 || value === -1) {
1708
- quotas[key] = -1;
1709
- } else {
1710
- quotas[key] = (quotas[key] ?? 0) + value;
1711
- }
1712
- }
1713
- }
1714
- return {
1715
- plan,
1716
- quotas,
1717
- features
1718
- };
1719
- }
1720
- __name(aggregateContractLineItemEntitlements, "aggregateContractLineItemEntitlements");
1721
- function filterPlannedOnlyFeatures(features, catalog) {
1722
- const out = /* @__PURE__ */ new Set();
1723
- for (const f of features) {
1724
- if (!isFeaturePlannedOnly(catalog, f)) {
1725
- out.add(f);
1726
- }
1727
- }
1728
- return out;
1729
- }
1730
- __name(filterPlannedOnlyFeatures, "filterPlannedOnlyFeatures");
1731
- function applyCustomLimits(base, custom) {
1732
- if (!custom) return base;
1733
- const quotas = {
1734
- ...base.quotas
1735
- };
1736
- if (custom.quotas) {
1737
- for (const [k, v] of Object.entries(custom.quotas)) {
1738
- quotas[k] = v;
1739
- }
1740
- }
1741
- const features = new Set(base.features);
1742
- if (custom.features) {
1743
- for (const f of custom.features) features.add(f);
1744
- }
1745
- return {
1746
- plan: base.plan,
1747
- quotas,
1748
- features
1749
- };
1750
- }
1751
- __name(applyCustomLimits, "applyCustomLimits");
1752
- function aggregateLimits(input, catalog, now) {
1753
- const activeBundles = filterActiveSubscriptionBundles(input.subscriptionBundles ?? [], now);
1754
- const subBundleQuotas = aggregateSubscriptionBundleQuotas(activeBundles);
1755
- const subBundleFeatures = collectSubscriptionBundleFeatures(activeBundles);
1756
- const quotas = {};
1757
- for (const [k, v] of Object.entries(input.planVersion.quotas)) {
1758
- quotas[k] = v;
1759
- }
1760
- for (const [k, v] of Object.entries(subBundleQuotas)) {
1761
- if (quotas[k] === -1 || v === -1) {
1762
- quotas[k] = -1;
1763
- } else {
1764
- quotas[k] = (quotas[k] ?? 0) + v;
1765
- }
1766
- }
1767
- const features = /* @__PURE__ */ new Set([
1768
- ...input.planVersion.features,
1769
- ...subBundleFeatures
1770
- ]);
1771
- const withCustom = applyCustomLimits({
1772
- plan: input.plan,
1773
- quotas,
1774
- features
1775
- }, input.customLimits);
1776
- const filteredFeatures = filterPlannedOnlyFeatures(withCustom.features, catalog);
1777
- return {
1778
- plan: withCustom.plan,
1779
- quotas: withCustom.quotas,
1780
- features: filteredFeatures
1781
- };
1782
- }
1783
- __name(aggregateLimits, "aggregateLimits");
1784
-
1785
- // src/entitlement/feature-aliases.ts
1786
- function buildReplacedByIndex(features) {
1787
- const index = /* @__PURE__ */ new Map();
1788
- for (const feature of features) {
1789
- for (const oldKey of feature.replaces ?? []) {
1790
- if (oldKey === feature.featureKey) continue;
1791
- const list = index.get(oldKey) ?? [];
1792
- list.push(feature.featureKey);
1793
- index.set(oldKey, list);
1794
- }
1795
- }
1796
- for (const list of index.values()) list.sort((a, b) => a.localeCompare(b));
1797
- assertNoReplacesCycle(index);
1798
- return index;
1799
- }
1800
- __name(buildReplacedByIndex, "buildReplacedByIndex");
1801
- function expandReplacedFeatures(granted, index) {
1802
- const expanded = new Set(granted);
1803
- const worklist = [
1804
- ...granted
1805
- ];
1806
- while (worklist.length > 0) {
1807
- const key = worklist.pop();
1808
- for (const successor of index.get(key) ?? []) {
1809
- if (expanded.has(successor)) continue;
1810
- expanded.add(successor);
1811
- worklist.push(successor);
1812
- }
1813
- }
1814
- return expanded;
1815
- }
1816
- __name(expandReplacedFeatures, "expandReplacedFeatures");
1817
- function assertNoReplacesCycle(index) {
1818
- const DONE = 2;
1819
- const IN_PROGRESS = 1;
1820
- const state = /* @__PURE__ */ new Map();
1821
- const visit = /* @__PURE__ */ __name((key, path) => {
1822
- const s = state.get(key);
1823
- if (s === DONE) return;
1824
- if (s === IN_PROGRESS) {
1825
- const cycleStart = path.indexOf(key);
1826
- const cycle = [
1827
- ...path.slice(cycleStart),
1828
- key
1829
- ].join(" \u2192 ");
1830
- throw new Error(`replaces-Zyklus in den Discovery-Metadaten: ${cycle}. Feature-Ersetzungen m\xFCssen azyklisch sein \u2014 replaces-Deklarationen im Code korrigieren (#39).`);
1831
- }
1832
- state.set(key, IN_PROGRESS);
1833
- for (const successor of index.get(key) ?? []) {
1834
- visit(successor, [
1835
- ...path,
1836
- key
1837
- ]);
1838
- }
1839
- state.set(key, DONE);
1840
- }, "visit");
1841
- for (const key of index.keys()) visit(key, []);
1842
- }
1843
- __name(assertNoReplacesCycle, "assertNoReplacesCycle");
1844
-
1845
- // src/entitlement/limit-exceeded-error.ts
1846
- var LIMIT_EXCEEDED = "LIMIT_EXCEEDED";
1847
- var LimitExceededError = class extends Error {
1848
- static {
1849
- __name(this, "LimitExceededError");
1850
- }
1851
- dimension;
1852
- max;
1853
- used;
1854
- code = LIMIT_EXCEEDED;
1855
- constructor(dimension, max, used) {
1856
- super(`Limit f\xFCr ${dimension} erreicht: ${used}/${max}.`), this.dimension = dimension, this.max = max, this.used = used;
1857
- this.name = "LimitExceededError";
1858
- }
1859
- };
1860
-
1861
- // src/entitlement/plan-resolution.ts
1862
- function resolveEntitlementPlan(input, config, now) {
1863
- if (input.isPilot && config.pilotEntitlementPlan !== void 0) {
1864
- return config.pilotEntitlementPlan;
1865
- }
1866
- if (input.status === "TRIAL") {
1867
- return input.trialEntitlementPlan ?? config.defaultTrialEntitlementPlan ?? input.plan;
1868
- }
1869
- if (input.status === "PENDING_SALES" && config.pendingSalesEntitlementPlan !== void 0) {
1870
- return config.pendingSalesEntitlementPlan;
1871
- }
1872
- if (input.pendingPlan && input.pendingEffectiveAt && input.pendingEffectiveAt <= now) {
1873
- return input.pendingPlan;
1874
- }
1875
- return input.plan;
1876
- }
1877
- __name(resolveEntitlementPlan, "resolveEntitlementPlan");
1878
-
1879
- // src/entitlement/tokens.ts
1880
- var ENTITLEMENT_SERVICE_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/EntitlementService");
1881
- var SUBSCRIPTION_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/SubscriptionRepository");
1882
- var PLAN_VERSION_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/PlanVersionRepository");
1883
- var TRANSACTION_RUNNER_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/TransactionRunner");
1884
- var ENTITLEMENT_RESOLUTION_CONFIG_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/EntitlementResolutionConfig");
1885
-
1886
- // src/entitlement/service.ts
1887
- function _ts_decorate14(decorators, target, key, desc) {
1888
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1889
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1890
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1891
- return c > 3 && r && Object.defineProperty(target, key, r), r;
1892
- }
1893
- __name(_ts_decorate14, "_ts_decorate");
1894
- function _ts_metadata10(k, v) {
1895
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1896
- }
1897
- __name(_ts_metadata10, "_ts_metadata");
1898
- function _ts_param10(paramIndex, decorator) {
1899
- return function(target, key) {
1900
- decorator(target, key, paramIndex);
1901
- };
1902
- }
1903
- __name(_ts_param10, "_ts_param");
1904
- var CACHE_TTL_MS = 6e4;
1905
- var CACHE_MAX_ENTRIES = 1e3;
1906
- var EntitlementService = class {
1907
- static {
1908
- __name(this, "EntitlementService");
1909
- }
1910
- catalog;
1911
- subscriptions;
1912
- planVersions;
1913
- tx;
1914
- resolutionConfig;
1915
- subscriptionBundles;
1916
- bundles;
1917
- subscriptionContracts;
1918
- discoverySnapshot;
1919
- // Map preserves insert order — on a hit the entry is removed and
1920
- // re-inserted (LRU). The TTL is a safety net for forgotten
1921
- // invalidations — 60s is enough, because limits generally only change
1922
- // through mutations, not through time (a pending plan's effective date
1923
- // is day-granular).
1924
- cache = /* @__PURE__ */ new Map();
1925
- // #39 — replaces alias index, built lazily from the boot-static snapshot;
1926
- // throws on replaces cycles (a clear error instead of an infinite loop).
1927
- replacedByIndex = null;
1928
- constructor(catalog, subscriptions, planVersions, tx, resolutionConfig = null, subscriptionBundles = null, bundles = null, subscriptionContracts = null, discoverySnapshot = null) {
1929
- this.catalog = catalog;
1930
- this.subscriptions = subscriptions;
1931
- this.planVersions = planVersions;
1932
- this.tx = tx;
1933
- this.resolutionConfig = resolutionConfig;
1934
- this.subscriptionBundles = subscriptionBundles;
1935
- this.bundles = bundles;
1936
- this.subscriptionContracts = subscriptionContracts;
1937
- this.discoverySnapshot = discoverySnapshot;
1938
- }
1939
- // ---------------------------------------------------------------------
1940
- // Read path — for FeatureGuard, sidebar hooks, GET /billing/entitlement
1941
- // ---------------------------------------------------------------------
1942
- async computeLimits(tenantId, now = /* @__PURE__ */ new Date()) {
1943
- const cached = this.readCache(tenantId, now.getTime());
1944
- if (cached) return cached;
1945
- const sub = await this.subscriptions.findByTenantId(tenantId);
1946
- if (!sub) {
1947
- throw new import_common14.NotFoundException(`Keine Subscription f\xFCr Tenant ${tenantId}`);
1948
- }
1949
- const limits = await this.deriveLimits(sub, now);
1950
- this.writeCache(tenantId, limits, now.getTime());
1951
- return limits;
1952
- }
1953
- /**
1954
- * Clears a tenant's cache entry. MUST be called as soon as a mutation
1955
- * changes the subscription, a bundle booking, or a PlanVersion binding —
1956
- * otherwise FeatureGuard and the sidebar stay on stale state for up to
1957
- * 60s.
1958
- */
1959
- invalidateTenant(tenantId) {
1960
- this.cache.delete(tenantId);
1961
- }
1962
- /** Clears the entire cache. Only for tests / bootstrap. */
1963
- invalidateAll() {
1964
- this.cache.clear();
1965
- }
1966
- /**
1967
- * Aggregator path — also callable directly by consumers that already
1968
- * have a `SubscriptionRecord` (e.g. within a transaction).
1969
- */
1970
- async deriveLimits(sub, now, tx) {
1971
- const contractLimits = await this.deriveLimitsFromContract(sub.tenantId, now);
1972
- if (contractLimits) return this.withReplacedFeatureAliases(contractLimits);
1973
- const effectivePlan = resolveEntitlementPlan(sub, this.resolutionConfig ?? {}, now);
1974
- const planVersion = effectivePlan === sub.plan ? sub.planVersion : await this.findActivePlanVersionOrFallback(effectivePlan, now, tx);
1975
- const subscriptionBundles = await this.loadSubscriptionBundleSnapshots(sub.id, now);
1976
- return this.withReplacedFeatureAliases(aggregateLimits({
1977
- plan: effectivePlan,
1978
- planVersion,
1979
- subscriptionBundles,
1980
- customLimits: sub.customLimits ?? null
1981
- }, this.catalog, now));
1982
- }
1983
- /**
1984
- * Grandfathering (#39): granted old feature keys transitively grant
1985
- * their successors from the `replaces` chains of the discovery snapshot.
1986
- * A no-op without a snapshot or without replaces declarations.
1987
- */
1988
- withReplacedFeatureAliases(limits) {
1989
- if (!this.discoverySnapshot) return limits;
1990
- if (!this.replacedByIndex) {
1991
- this.replacedByIndex = buildReplacedByIndex(this.discoverySnapshot.features);
1992
- }
1993
- if (this.replacedByIndex.size === 0) return limits;
1994
- return {
1995
- ...limits,
1996
- features: expandReplacedFeatures(limits.features, this.replacedByIndex)
1997
- };
1998
- }
1999
- async deriveLimitsFromContract(tenantId, now) {
2000
- if (!this.subscriptionContracts) return null;
2001
- const contract = await this.subscriptionContracts.findActiveByTenantId(tenantId, now);
2002
- if (!contract) return null;
2003
- if (contract.entitlementSnapshot) {
2004
- return {
2005
- plan: contract.entitlementSnapshot.plan,
2006
- quotas: {
2007
- ...contract.entitlementSnapshot.quotas
2008
- },
2009
- features: new Set(contract.entitlementSnapshot.features)
2010
- };
2011
- }
2012
- return aggregateContractLineItemEntitlements(contract.lineItems);
2013
- }
2014
- /**
2015
- * Loads a subscription's active bundle bookings and resolves the
2016
- * `BundleVersion` features/quotas per entry. Without a registered
2017
- * `SubscriptionBundleRepository` or `BundleRepository` the method
2018
- * returns an empty list — apps without a bundle schema stay unchanged
2019
- * (plan-only aggregation).
2020
- */
2021
- async loadSubscriptionBundleSnapshots(subscriptionId, now) {
2022
- if (!this.subscriptionBundles || !this.bundles) return [];
2023
- const active = await this.subscriptionBundles.listActiveBySubscription(subscriptionId, now);
2024
- if (active.length === 0) return [];
2025
- return Promise.all(active.map(async (booking) => {
2026
- const bv = await this.bundles.findVersionById(booking.bundleVersionId);
2027
- if (!bv) {
2028
- throw new Error(`BundleVersion '${booking.bundleVersionId}' aus aktiver SubscriptionBundle nicht gefunden`);
2029
- }
2030
- return {
2031
- bundleKey: bv.bundleKey,
2032
- features: bv.features,
2033
- quotas: bv.quotas,
2034
- canceledEffectiveAt: booking.canceledEffectiveAt
2035
- };
2036
- }));
2037
- }
2038
- // ---------------------------------------------------------------------
2039
- // Write/consumption path — row lock + count + insert in the same
2040
- // transaction. Protects against race conditions on concurrent creations
2041
- // for the same tenant.
2042
- // ---------------------------------------------------------------------
2043
- async enforceLimit(input) {
2044
- const now = input.now ?? /* @__PURE__ */ new Date();
2045
- const delta = input.delta ?? 1;
2046
- return this.tx.run(async (tx) => {
2047
- const sub = await this.subscriptions.findByTenantIdLocked(input.tenantId, tx);
2048
- if (!sub) {
2049
- throw new import_common14.NotFoundException(`Keine Subscription f\xFCr Tenant ${input.tenantId}`);
2050
- }
2051
- const limits = await this.deriveLimits(sub, now, tx);
2052
- const max = limits.quotas[input.dimension];
2053
- if (max === void 0) {
2054
- throw new Error(`Quota-Dimension "${input.dimension}" nicht im PlanCatalog (${this.catalog.projectKey}).`);
2055
- }
2056
- const used = await input.currentUsage(tx, input.tenantId);
2057
- if (max !== -1 && used + delta > max) {
2058
- throw new LimitExceededError(input.dimension, max, used);
2059
- }
2060
- return input.insert(tx);
2061
- });
2062
- }
2063
- // ---------------------------------------------------------------------
2064
- // Helpers
2065
- // ---------------------------------------------------------------------
2066
- /**
2067
- * Plan fallback for TRIAL/PENDING_SALES: returns the PlanVersion active
2068
- * as of `asOf` (SPEC_V2 §4.2). If the repo does not implement
2069
- * `findActive`, we fall back to `findLatestLive` (backward compat for
2070
- * adapters without `validFrom` columns).
2071
- */
2072
- async findActivePlanVersionOrFallback(planId, asOf, tx) {
2073
- const v = this.planVersions.findActive ? await this.planVersions.findActive(planId, asOf, tx) : await this.planVersions.findLatestLive(planId, tx);
2074
- if (!v) {
2075
- throw new import_common14.NotFoundException(`Keine zu ${asOf.toISOString().slice(0, 10)} aktive PlanVersion f\xFCr ${planId} \u2014 weder validFrom-window erf\xFCllt noch latest-live verf\xFCgbar.`);
2076
- }
2077
- return v;
2078
- }
2079
- readCache(tenantId, nowMs) {
2080
- const entry = this.cache.get(tenantId);
2081
- if (!entry) return null;
2082
- if (entry.expiresAt < nowMs) {
2083
- this.cache.delete(tenantId);
2084
- return null;
2085
- }
2086
- this.cache.delete(tenantId);
2087
- this.cache.set(tenantId, entry);
2088
- return entry.value;
2089
- }
2090
- writeCache(tenantId, value, nowMs) {
2091
- if (this.cache.has(tenantId)) this.cache.delete(tenantId);
2092
- this.cache.set(tenantId, {
2093
- value,
2094
- expiresAt: nowMs + CACHE_TTL_MS
2095
- });
2096
- while (this.cache.size > CACHE_MAX_ENTRIES) {
2097
- const oldest = this.cache.keys().next().value;
2098
- if (oldest === void 0) break;
2099
- this.cache.delete(oldest);
2100
- }
2101
- }
2102
- };
2103
- EntitlementService = _ts_decorate14([
2104
- (0, import_common14.Injectable)(),
2105
- _ts_param10(0, (0, import_common14.Inject)(PLAN_CATALOG_TOKEN)),
2106
- _ts_param10(1, (0, import_common14.Inject)(SUBSCRIPTION_REPOSITORY_TOKEN)),
2107
- _ts_param10(2, (0, import_common14.Inject)(PLAN_VERSION_REPOSITORY_TOKEN)),
2108
- _ts_param10(3, (0, import_common14.Inject)(TRANSACTION_RUNNER_TOKEN)),
2109
- _ts_param10(4, (0, import_common14.Optional)()),
2110
- _ts_param10(4, (0, import_common14.Inject)(ENTITLEMENT_RESOLUTION_CONFIG_TOKEN)),
2111
- _ts_param10(5, (0, import_common14.Optional)()),
2112
- _ts_param10(5, (0, import_common14.Inject)(SUBSCRIPTION_BUNDLE_REPOSITORY_TOKEN)),
2113
- _ts_param10(6, (0, import_common14.Optional)()),
2114
- _ts_param10(6, (0, import_common14.Inject)(BUNDLE_REPOSITORY_TOKEN)),
2115
- _ts_param10(7, (0, import_common14.Optional)()),
2116
- _ts_param10(7, (0, import_common14.Inject)(SUBSCRIPTION_CONTRACT_REPOSITORY_TOKEN)),
2117
- _ts_param10(8, (0, import_common14.Optional)()),
2118
- _ts_param10(8, (0, import_common14.Inject)(DISCOVERY_SNAPSHOT_TOKEN)),
2119
- _ts_metadata10("design:type", Function),
2120
- _ts_metadata10("design:paramtypes", [
2121
- typeof PlanCatalog === "undefined" ? Object : PlanCatalog,
2122
- typeof SubscriptionRepository === "undefined" ? Object : SubscriptionRepository,
2123
- typeof PlanVersionRepository === "undefined" ? Object : PlanVersionRepository,
2124
- typeof TransactionRunner === "undefined" ? Object : TransactionRunner,
2125
- Object,
2126
- Object,
2127
- Object,
2128
- Object,
2129
- Object
2130
- ])
2131
- ], EntitlementService);
2132
-
2133
- // src/entitlement/module.ts
2134
- function _ts_decorate15(decorators, target, key, desc) {
2135
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2136
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2137
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2138
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2139
- }
2140
- __name(_ts_decorate15, "_ts_decorate");
2141
- var EntitlementModule = class _EntitlementModule {
2142
- static {
2143
- __name(this, "EntitlementModule");
2144
- }
2145
- static forRoot(options) {
2146
- const providers = [
2147
- asProvider(SUBSCRIPTION_REPOSITORY_TOKEN, options.subscriptionRepository),
2148
- asProvider(PLAN_VERSION_REPOSITORY_TOKEN, options.planVersionRepository),
2149
- asProvider(TRANSACTION_RUNNER_TOKEN, options.transactionRunner),
2150
- EntitlementService,
2151
- // Cross-bundle-stable alias so consumers in other subpath bundles
2152
- // (billing/TenantBillingModule) can inject the same instance.
2153
- {
2154
- provide: ENTITLEMENT_SERVICE_TOKEN,
2155
- useExisting: EntitlementService
2156
- }
2157
- ];
2158
- if (options.resolutionConfig) {
2159
- providers.push({
2160
- provide: ENTITLEMENT_RESOLUTION_CONFIG_TOKEN,
2161
- useValue: options.resolutionConfig
2162
- });
2163
- }
2164
- if (options.subscriptionContractRepository) {
2165
- providers.push(asProvider(SUBSCRIPTION_CONTRACT_REPOSITORY_TOKEN, options.subscriptionContractRepository));
2166
- }
2167
- if (options.subscriptionBundleRepository) {
2168
- providers.push(asProvider(SUBSCRIPTION_BUNDLE_REPOSITORY_TOKEN, options.subscriptionBundleRepository));
2169
- }
2170
- if (options.bundleRepository) {
2171
- providers.push(asProvider(BUNDLE_REPOSITORY_TOKEN, options.bundleRepository));
2172
- }
2173
- return {
2174
- module: _EntitlementModule,
2175
- global: options.global ?? false,
2176
- providers,
2177
- exports: [
2178
- EntitlementService,
2179
- ENTITLEMENT_SERVICE_TOKEN,
2180
- SUBSCRIPTION_REPOSITORY_TOKEN
2181
- ]
2182
- };
2183
- }
2184
- };
2185
- EntitlementModule = _ts_decorate15([
2186
- (0, import_common15.Module)({})
2187
- ], EntitlementModule);
2188
-
2189
- // src/platform/plan-resolver.port.ts
2190
- var PLAN_RESOLVER_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform-nest/PlanResolverPort");
2191
- var StaticPlanResolver = class {
2192
- static {
2193
- __name(this, "StaticPlanResolver");
2194
- }
2195
- planId;
2196
- constructor(planId) {
2197
- this.planId = planId;
2198
- }
2199
- async getPlanIdForTenant() {
2200
- return this.planId;
2201
- }
2202
- };
2203
-
2204
- // src/platform/static-entitlement.service.ts
2205
- var import_common16 = require("@nestjs/common");
2206
- function _ts_decorate16(decorators, target, key, desc) {
2207
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2208
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2209
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2210
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2211
- }
2212
- __name(_ts_decorate16, "_ts_decorate");
2213
- function _ts_metadata11(k, v) {
2214
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2215
- }
2216
- __name(_ts_metadata11, "_ts_metadata");
2217
- function _ts_param11(paramIndex, decorator) {
2218
- return function(target, key) {
2219
- decorator(target, key, paramIndex);
2220
- };
2221
- }
2222
- __name(_ts_param11, "_ts_param");
2223
- var StaticEntitlementService = class {
2224
- static {
2225
- __name(this, "StaticEntitlementService");
2226
- }
2227
- catalog;
2228
- resolver;
2229
- constructor(catalog, resolver) {
2230
- this.catalog = catalog;
2231
- this.resolver = resolver;
2232
- }
2233
- /**
2234
- * Returns the effective features + quotas of a tenant from the
2235
- * plan catalog. When the plan is missing: an empty snapshot (no feature,
2236
- * all quotas at 0).
2237
- */
2238
- async snapshot(tenantId) {
2239
- const planId = await this.resolver.getPlanIdForTenant(tenantId);
2240
- if (!planId) {
2241
- return {
2242
- planId: null,
2243
- features: [],
2244
- quotas: {}
2245
- };
2246
- }
2247
- const plan = this.findPlan(planId);
2248
- if (!plan) {
2249
- return {
2250
- planId,
2251
- features: [],
2252
- quotas: {}
2253
- };
2254
- }
2255
- return {
2256
- planId,
2257
- features: plan.features ?? [],
2258
- quotas: plan.quotas ?? {}
2259
- };
2260
- }
2261
- /** Convenience helper for tests / synchronous checks in the UI manifest. */
2262
- async hasFeature(tenantId, featureKey) {
2263
- const snap = await this.snapshot(tenantId);
2264
- return snap.features.includes(featureKey);
2265
- }
2266
- async quotaLimit(tenantId, quotaKey) {
2267
- const snap = await this.snapshot(tenantId);
2268
- const value = snap.quotas[quotaKey];
2269
- return value === void 0 ? null : value;
2270
- }
2271
- findPlan(planId) {
2272
- return (this.catalog.plans ?? []).find((p) => p.id === planId);
2273
- }
2274
- };
2275
- StaticEntitlementService = _ts_decorate16([
2276
- (0, import_common16.Injectable)(),
2277
- _ts_param11(0, (0, import_common16.Inject)(PLAN_CATALOG_TOKEN)),
2278
- _ts_param11(1, (0, import_common16.Inject)(PLAN_RESOLVER_PORT_TOKEN)),
2279
- _ts_metadata11("design:type", Function),
2280
- _ts_metadata11("design:paramtypes", [
2281
- typeof PlanCatalog === "undefined" ? Object : PlanCatalog,
2282
- typeof PlanResolverPort === "undefined" ? Object : PlanResolverPort
2283
- ])
2284
- ], StaticEntitlementService);
2285
-
2286
- // src/platform/static-feature.guard.ts
2287
- var import_common18 = require("@nestjs/common");
2288
- var import_core4 = require("@nestjs/core");
2289
-
2290
- // src/billing/require-feature.decorator.ts
2291
- var import_common17 = require("@nestjs/common");
2292
- var REQUIRE_FEATURE_KEY = "require-feature";
2293
-
2294
- // src/platform/static-feature.guard.ts
2295
- function _ts_decorate17(decorators, target, key, desc) {
2296
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2297
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2298
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2299
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2300
- }
2301
- __name(_ts_decorate17, "_ts_decorate");
2302
- function _ts_metadata12(k, v) {
2303
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2304
- }
2305
- __name(_ts_metadata12, "_ts_metadata");
2306
- function _ts_param12(paramIndex, decorator) {
2307
- return function(target, key) {
2308
- decorator(target, key, paramIndex);
2309
- };
2310
- }
2311
- __name(_ts_param12, "_ts_param");
2312
- var STATIC_FEATURE_GUARD_CONFIG_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform-nest/StaticFeatureGuardConfig");
2313
- var StaticFeatureGuard = class {
2314
- static {
2315
- __name(this, "StaticFeatureGuard");
2316
- }
2317
- reflector;
2318
- entitlements;
2319
- config;
2320
- constructor(reflector, entitlements, config = null) {
2321
- this.reflector = reflector;
2322
- this.entitlements = entitlements;
2323
- this.config = config;
2324
- }
2325
- async canActivate(context) {
2326
- const required = this.reflector.getAllAndOverride(REQUIRE_FEATURE_KEY, [
2327
- context.getHandler(),
2328
- context.getClass()
2329
- ]);
2330
- if (!required || required.length === 0) return true;
2331
- const request = context.switchToHttp().getRequest();
2332
- const user = request.user;
2333
- if (!user) throw new import_common18.ForbiddenException("Nicht authentifiziert");
2334
- const role = this.config?.userRoleResolver ? this.config.userRoleResolver(user) : user.role ?? user.platformRole;
2335
- if (role === "SUPER_ADMIN") return true;
2336
- const tenantId = this.config?.tenantIdResolver ? this.config.tenantIdResolver(request) : request.tenantId ?? user.tenantId;
2337
- if (!tenantId) throw new import_common18.ForbiddenException("Kein Mandant zugeordnet");
2338
- const snap = await this.entitlements.snapshot(tenantId);
2339
- const allowed = required.some((f) => snap.features.includes(f));
2340
- if (!allowed) {
2341
- throw new import_common18.ForbiddenException(`Feature ${required.join(" / ")} nicht im aktuellen Plan enthalten.`);
2342
- }
2343
- return true;
2344
- }
2345
- };
2346
- StaticFeatureGuard = _ts_decorate17([
2347
- (0, import_common18.Injectable)(),
2348
- _ts_param12(0, (0, import_common18.Inject)(import_core4.Reflector)),
2349
- _ts_param12(1, (0, import_common18.Inject)(StaticEntitlementService)),
2350
- _ts_param12(2, (0, import_common18.Optional)()),
2351
- _ts_param12(2, (0, import_common18.Inject)(STATIC_FEATURE_GUARD_CONFIG_TOKEN)),
2352
- _ts_metadata12("design:type", Function),
2353
- _ts_metadata12("design:paramtypes", [
2354
- typeof import_core4.Reflector === "undefined" ? Object : import_core4.Reflector,
2355
- typeof StaticEntitlementService === "undefined" ? Object : StaticEntitlementService,
2356
- Object
2357
- ])
2358
- ], StaticFeatureGuard);
2359
-
2360
- // src/platform/enforce-quota.interceptor.ts
2361
- var import_common19 = require("@nestjs/common");
2362
- var import_core5 = require("@nestjs/core");
2363
- var import_rxjs2 = require("rxjs");
2364
- var import_operators2 = require("rxjs/operators");
2365
- function _ts_decorate18(decorators, target, key, desc) {
2366
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2367
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2368
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2369
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2370
- }
2371
- __name(_ts_decorate18, "_ts_decorate");
2372
- function _ts_metadata13(k, v) {
2373
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2374
- }
2375
- __name(_ts_metadata13, "_ts_metadata");
2376
- function _ts_param13(paramIndex, decorator) {
2377
- return function(target, key) {
2378
- decorator(target, key, paramIndex);
2379
- };
2380
- }
2381
- __name(_ts_param13, "_ts_param");
2382
- var QUOTA_PROVIDERS_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform-nest/QuotaProviders");
2383
- var EnforceQuotaInterceptor = class {
2384
- static {
2385
- __name(this, "EnforceQuotaInterceptor");
2386
- }
2387
- reflector;
2388
- entitlements;
2389
- providers;
2390
- constructor(reflector, entitlements, providers = null) {
2391
- this.reflector = reflector;
2392
- this.entitlements = entitlements;
2393
- this.providers = providers;
2394
- }
2395
- intercept(context, next) {
2396
- const meta = this.reflector.getAllAndOverride(ENFORCE_QUOTA_KEY, [
2397
- context.getHandler(),
2398
- context.getClass()
2399
- ]);
2400
- if (!meta?.quotaKey) return next.handle();
2401
- return (0, import_rxjs2.from)(this.check(context, meta)).pipe((0, import_operators2.switchMap)(() => next.handle()));
2402
- }
2403
- async check(context, meta) {
2404
- const request = context.switchToHttp().getRequest();
2405
- const user = request.user;
2406
- if (!user) return;
2407
- const role = user.role ?? user.platformRole;
2408
- if (role === "SUPER_ADMIN") return;
2409
- const tenantId = request.tenantId ?? user.tenantId;
2410
- if (!tenantId) return;
2411
- const provider = (this.providers ?? []).find((p) => p.key === meta.quotaKey);
2412
- if (!provider) {
2413
- return;
2414
- }
2415
- const limit = await this.entitlements.quotaLimit(tenantId, meta.quotaKey);
2416
- if (limit === null || limit === -1) return;
2417
- const current = await provider.count(tenantId);
2418
- const delta = meta.incrementBy ?? 1;
2419
- if (current + delta > limit) {
2420
- throw new LimitExceededError(meta.quotaKey, limit, current);
2421
- }
2422
- }
2423
- };
2424
- EnforceQuotaInterceptor = _ts_decorate18([
2425
- (0, import_common19.Injectable)(),
2426
- _ts_param13(0, (0, import_common19.Inject)(import_core5.Reflector)),
2427
- _ts_param13(1, (0, import_common19.Inject)(StaticEntitlementService)),
2428
- _ts_param13(2, (0, import_common19.Optional)()),
2429
- _ts_param13(2, (0, import_common19.Inject)(QUOTA_PROVIDERS_TOKEN)),
2430
- _ts_metadata13("design:type", Function),
2431
- _ts_metadata13("design:paramtypes", [
2432
- typeof import_core5.Reflector === "undefined" ? Object : import_core5.Reflector,
2433
- typeof StaticEntitlementService === "undefined" ? Object : StaticEntitlementService,
2434
- Object
2435
- ])
2436
- ], EnforceQuotaInterceptor);
2437
-
2438
- // src/platform/tenant-manifest.service.ts
2439
- var import_common20 = require("@nestjs/common");
2440
- function _ts_decorate19(decorators, target, key, desc) {
2441
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2442
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2443
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2444
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2445
- }
2446
- __name(_ts_decorate19, "_ts_decorate");
2447
- function _ts_metadata14(k, v) {
2448
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2449
- }
2450
- __name(_ts_metadata14, "_ts_metadata");
2451
- var TenantManifestService = class {
2452
- static {
2453
- __name(this, "TenantManifestService");
2454
- }
2455
- entitlements;
2456
- navItems = [];
2457
- constructor(entitlements) {
2458
- this.entitlements = entitlements;
2459
- }
2460
- /**
2461
- * Call in the app's `OnModuleInit`: registers app navigation for the
2462
- * Tenant UI. Idempotent: the same `id` overwrites.
2463
- */
2464
- registerNavItem(item) {
2465
- const existing = this.navItems.findIndex((i) => i.id === item.id);
2466
- if (existing >= 0) this.navItems[existing] = item;
2467
- else this.navItems.push(item);
2468
- }
2469
- /** Bulk variant. */
2470
- registerNavItems(items) {
2471
- for (const item of items) this.registerNavItem(item);
2472
- }
2473
- /**
2474
- * Returns the filtered manifest for a Tenant. NavItems with
2475
- * `requiresFeature` are filtered out server-side as soon as none of the
2476
- * required features are present in the entitlement snapshot.
2477
- */
2478
- async getManifest(tenantId) {
2479
- const snap = await this.entitlements.snapshot(tenantId);
2480
- const featureSet = new Set(snap.features);
2481
- const visible = this.navItems.filter((item) => isVisible(item, featureSet)).map((item) => ({
2482
- ...item
2483
- })).sort((a, b) => (a.order ?? 100) - (b.order ?? 100));
2484
- return {
2485
- schemaVersion: 1,
2486
- tenant: {
2487
- id: tenantId
2488
- },
2489
- planId: snap.planId,
2490
- features: snap.features,
2491
- quotas: snap.quotas,
2492
- navigation: visible
2493
- };
2494
- }
2495
- };
2496
- TenantManifestService = _ts_decorate19([
2497
- (0, import_common20.Injectable)(),
2498
- _ts_metadata14("design:type", Function),
2499
- _ts_metadata14("design:paramtypes", [
2500
- typeof StaticEntitlementService === "undefined" ? Object : StaticEntitlementService
2501
- ])
2502
- ], TenantManifestService);
2503
- function isVisible(item, features) {
2504
- if (!item.requiresFeature) return true;
2505
- const required = Array.isArray(item.requiresFeature) ? item.requiresFeature : [
2506
- item.requiresFeature
2507
- ];
2508
- return required.some((f) => features.has(f));
2509
- }
2510
- __name(isVisible, "isVisible");
2511
-
2512
- // src/platform/tenant-manifest.controller.ts
2513
- var import_common21 = require("@nestjs/common");
2514
- function _ts_decorate20(decorators, target, key, desc) {
2515
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2516
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2517
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2518
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2519
- }
2520
- __name(_ts_decorate20, "_ts_decorate");
2521
- function _ts_metadata15(k, v) {
2522
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2523
- }
2524
- __name(_ts_metadata15, "_ts_metadata");
2525
- function _ts_param14(paramIndex, decorator) {
2526
- return function(target, key) {
2527
- decorator(target, key, paramIndex);
2528
- };
2529
- }
2530
- __name(_ts_param14, "_ts_param");
2531
- function buildTenantManifestController(options) {
2532
- const path = options.path ?? "tenant/manifest";
2533
- let GeneratedTenantManifestController = class GeneratedTenantManifestController {
2534
- static {
2535
- __name(this, "GeneratedTenantManifestController");
2536
- }
2537
- svc;
2538
- constructor(svc) {
2539
- this.svc = svc;
2540
- }
2541
- async get(req) {
2542
- const tenantId = req.user?.tenantId ?? req.tenantId;
2543
- if (!tenantId) {
2544
- throw new Error("Kein Mandant im Request \u2014 TenantManifest braucht user.tenantId.");
2545
- }
2546
- return this.svc.getManifest(tenantId);
2547
- }
2548
- };
2549
- _ts_decorate20([
2550
- (0, import_common21.Get)(),
2551
- _ts_param14(0, (0, import_common21.Req)()),
2552
- _ts_metadata15("design:type", Function),
2553
- _ts_metadata15("design:paramtypes", [
2554
- typeof RequestWithUser === "undefined" ? Object : RequestWithUser
2555
- ]),
2556
- _ts_metadata15("design:returntype", Promise)
2557
- ], GeneratedTenantManifestController.prototype, "get", null);
2558
- GeneratedTenantManifestController = _ts_decorate20([
2559
- (0, import_common21.Controller)(path),
2560
- (0, import_common21.UseGuards)(...options.guards),
2561
- _ts_param14(0, (0, import_common21.Inject)(TenantManifestService)),
2562
- _ts_metadata15("design:type", Function),
2563
- _ts_metadata15("design:paramtypes", [
2564
- typeof TenantManifestService === "undefined" ? Object : TenantManifestService
2565
- ])
2566
- ], GeneratedTenantManifestController);
2567
- return GeneratedTenantManifestController;
2568
- }
2569
- __name(buildTenantManifestController, "buildTenantManifestController");
2570
-
2571
- // src/platform/saas-platform.module.ts
2572
- function _ts_decorate21(decorators, target, key, desc) {
2573
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2574
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2575
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2576
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2577
- }
2578
- __name(_ts_decorate21, "_ts_decorate");
2579
- function buildMinimalManifestConfig() {
2580
- return {
2581
- useFactory: /* @__PURE__ */ __name((catalog) => ({
2582
- project: {
2583
- key: catalog.projectKey,
2584
- displayName: catalog.app?.name ?? catalog.projectKey,
2585
- label: catalog.app?.label,
2586
- icon: catalog.app?.icon,
2587
- logoUrl: catalog.app?.logoUrl,
2588
- environment: process.env.NODE_ENV === "production" ? "production" : "development",
2589
- availableLocales: catalog.marketing?.availableLocales,
2590
- defaultLocale: catalog.marketing?.availableLocales?.[0]
2591
- },
2592
- build: {
2593
- platformPackageVersion: "0.0.0",
2594
- appVersion: "0.0.0"
2595
- },
2596
- planCatalogSnapshot: {
2597
- source: "saas-platform-module",
2598
- hash: "sha256-quickstart",
2599
- currency: catalog.currency,
2600
- vatRate: catalog.vatRate,
2601
- plans: catalog.plans ?? [],
2602
- features: catalog.features ?? []
2603
- }
2604
- }), "useFactory"),
2605
- inject: [
2606
- PLAN_CATALOG_TOKEN
2607
- ]
2608
- };
2609
- }
2610
- __name(buildMinimalManifestConfig, "buildMinimalManifestConfig");
2611
- var SaasPlatformModule = class _SaasPlatformModule {
2612
- static {
2613
- __name(this, "SaasPlatformModule");
2614
- }
2615
- static forRoot(options) {
2616
- const explicit = options.adapters ?? {};
2617
- const persistence = options.persistence;
2618
- const adapters = {
2619
- mfa: explicit.mfa ?? persistence?.core.mfa,
2620
- audit: explicit.audit ?? persistence?.core.audit,
2621
- rlsBypass: explicit.rlsBypass ?? persistence?.core.rlsBypass,
2622
- planCatalogReadSink: explicit.planCatalogReadSink ?? persistence?.planCatalogReadSink,
2623
- planResolver: explicit.planResolver,
2624
- subscriptionRepository: explicit.subscriptionRepository ?? persistence?.entitlement?.subscriptionRepository,
2625
- planVersionRepository: explicit.planVersionRepository ?? persistence?.entitlement?.planVersionRepository,
2626
- transactionRunner: explicit.transactionRunner ?? persistence?.core.transactionRunner
2627
- };
2628
- const missingCore = [
2629
- "mfa",
2630
- "audit",
2631
- "rlsBypass"
2632
- ].filter((key) => adapters[key] === void 0);
2633
- if (missingCore.length) {
2634
- throw new Error(`SaasPlatformModule.forRoot: adapters missing (provide them via \`adapters\` or a \`persistence\` bundle): ${missingCore.join(", ")}`);
2635
- }
2636
- const mfaPort = adapters.mfa;
2637
- const auditPort = adapters.audit;
2638
- const rlsBypassPort = adapters.rlsBypass;
2639
- if (!options.planCatalog && (!adapters.planCatalogReadSink || !options.dbCatalog)) {
2640
- throw new Error("SaasPlatformModule.forRoot: either set `planCatalog` (quickstart YAML path) or, for DB hydration, BOTH a planCatalogReadSink (`adapters`/`persistence`) AND `dbCatalog` ({ projectKey, currency, vatRate }).");
2641
- }
2642
- if (options.entitlement) {
2643
- const missing = [];
2644
- if (!adapters.subscriptionRepository) missing.push("subscriptionRepository");
2645
- if (!adapters.planVersionRepository) missing.push("planVersionRepository");
2646
- if (!adapters.transactionRunner) missing.push("transactionRunner");
2647
- if (missing.length) {
2648
- throw new Error(`SaasPlatformModule.forRoot: entitlement active, but adapters are missing: ${missing.join(", ")}`);
2649
- }
2650
- if (persistence) {
2651
- (0, import_types.assertPersistenceCapabilities)(persistence.capabilities, {
2652
- transactions: true,
2653
- pessimisticLocking: true
2654
- }, "SaasPlatformModule entitlement (transactional enforceLimit)");
2655
- }
2656
- }
2657
- const dbCatalog = options.dbCatalog;
2658
- const planCatalogModule = options.planCatalog ? PlanCatalogModule.forRootWithCatalog(options.planCatalog, {
2659
- global: true
2660
- }) : PlanCatalogModule.forRoot({
2661
- projectKey: dbCatalog.projectKey,
2662
- app: dbCatalog.app,
2663
- currency: dbCatalog.currency,
2664
- vatRate: dbCatalog.vatRate,
2665
- marketing: dbCatalog.marketing,
2666
- sink: adapters.planCatalogReadSink,
2667
- imports: options.imports
2668
- });
2669
- const appInfo = options.app ?? {
2670
- key: options.planCatalog?.projectKey ?? options.dbCatalog?.projectKey ?? "app",
2671
- version: options.planCatalog?.app?.version ?? options.dbCatalog?.app?.version ?? "0.0.0"
2672
- };
2673
- const imports = [
2674
- planCatalogModule,
2675
- DiscoveryModule.forRoot({
2676
- app: appInfo,
2677
- controller: {
2678
- guards: options.controller.guards
2679
- },
2680
- imports: options.imports,
2681
- snapshotPath: options.discoverySnapshotPath === void 0 ? "var/discovery-snapshot.json" : options.discoverySnapshotPath
2682
- }),
2683
- AdminModule.forRoot({
2684
- mfaPort,
2685
- auditPort,
2686
- rlsBypassPort,
2687
- global: true
2688
- }),
2689
- AdminManifestModule.forRoot({
2690
- config: options.adminManifestConfig ?? buildMinimalManifestConfig(),
2691
- guards: options.controller.guards,
2692
- reloadGuards: options.reloadGuards,
2693
- // Global like AdminModule above: apps register their manifest
2694
- // contribution by injecting AdminManifestService into one of
2695
- // their own modules (handbook §6.6). Re-exporting the module
2696
- // from here does not make that injection resolvable.
2697
- global: true
2698
- })
2699
- ];
2700
- if (options.entitlement) {
2701
- imports.push(EntitlementModule.forRoot({
2702
- subscriptionRepository: adapters.subscriptionRepository,
2703
- planVersionRepository: adapters.planVersionRepository,
2704
- transactionRunner: adapters.transactionRunner,
2705
- resolutionConfig: options.entitlement.resolutionConfig,
2706
- subscriptionContractRepository: persistence?.entitlement?.subscriptionContractRepository,
2707
- subscriptionBundleRepository: persistence?.entitlement?.subscriptionBundleRepository,
2708
- bundleRepository: persistence?.entitlement?.bundleRepository
2709
- }));
2710
- }
2711
- const lightweightProviders = [];
2712
- const lightweightExports = [];
2713
- const hasResolver = !!adapters.planResolver;
2714
- const hasFallback = !!options.defaultPlanId;
2715
- if (hasResolver || hasFallback) {
2716
- lightweightProviders.push(hasResolver ? asProvider(PLAN_RESOLVER_PORT_TOKEN, adapters.planResolver) : {
2717
- provide: PLAN_RESOLVER_PORT_TOKEN,
2718
- useValue: new StaticPlanResolver(options.defaultPlanId)
2719
- }, StaticEntitlementService, StaticFeatureGuard, EnforceQuotaInterceptor, ...options.quotaProviders ?? [], {
2720
- provide: QUOTA_PROVIDERS_TOKEN,
2721
- useFactory: /* @__PURE__ */ __name((...providers) => providers, "useFactory"),
2722
- inject: options.quotaProviders ?? []
2723
- }, {
2724
- provide: import_core6.APP_GUARD,
2725
- useExisting: StaticFeatureGuard
2726
- }, {
2727
- provide: import_core6.APP_INTERCEPTOR,
2728
- useExisting: EnforceQuotaInterceptor
2729
- });
2730
- lightweightExports.push(PLAN_RESOLVER_PORT_TOKEN, StaticEntitlementService, StaticFeatureGuard, EnforceQuotaInterceptor, QUOTA_PROVIDERS_TOKEN);
2731
- }
2732
- const tenantControllers = [];
2733
- if (options.tenantManifest) {
2734
- if (!hasResolver && !hasFallback) {
2735
- throw new Error("SaasPlatformModule.forRoot: tenantManifest erfordert defaultPlanId oder adapters.planResolver, sonst kann das Manifest keine Features aufl\xF6sen.");
2736
- }
2737
- lightweightProviders.push(TenantManifestService);
2738
- lightweightExports.push(TenantManifestService);
2739
- tenantControllers.push(buildTenantManifestController(options.tenantManifest));
2740
- }
2741
- return {
2742
- module: _SaasPlatformModule,
2743
- imports,
2744
- providers: lightweightProviders,
2745
- controllers: tenantControllers,
2746
- exports: [
2747
- PlanCatalogModule,
2748
- DiscoveryModule,
2749
- AdminModule,
2750
- // ADMIN_MANIFEST_CONFIG travels transitively: re-exporting an
2751
- // imported module's token directly is an UnknownExportException
2752
- // at boot — the module export below already carries it.
2753
- AdminManifestModule,
2754
- ...options.entitlement ? [
2755
- EntitlementModule
2756
- ] : [],
2757
- ...lightweightExports
2758
- ],
2759
- global: true
2760
- };
2761
- }
2762
- };
2763
- SaasPlatformModule = _ts_decorate21([
2764
- (0, import_common22.Module)({})
2765
- ], SaasPlatformModule);
2766
- // Annotate the CommonJS export names for ESM import in node:
2767
- 0 && (module.exports = {
2768
- AdminManifestService,
2769
- EnforceQuotaInterceptor,
2770
- PLAN_RESOLVER_PORT_TOKEN,
2771
- QUOTA_PROVIDERS_TOKEN,
2772
- STATIC_FEATURE_GUARD_CONFIG_TOKEN,
2773
- SaasPlatformModule,
2774
- StaticEntitlementService,
2775
- StaticFeatureGuard,
2776
- StaticPlanResolver,
2777
- TenantManifestService,
2778
- buildTenantManifestController
2779
- });
1
+ 'use strict';
2
+ // Generated by scripts/build-cjs-stubs.mjs — do not edit.
3
+ // Thin re-export so every entry shares one set of class objects.
4
+ module.exports = require('../_entries.cjs').platformNs;