@saasicat/nest 0.26.1 → 1.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +90 -201
- package/dist/.build-stamp +1 -0
- package/dist/_entries.cjs +3175 -2756
- package/dist/admin/index.d.cts +2 -2
- package/dist/admin/index.d.ts +2 -2
- package/dist/admin/index.js +2 -2
- package/dist/{admin-stats.service-HUZIU448.d.cts → admin-stats.service-BroyovpL.d.cts} +1 -1
- package/dist/{admin-stats.service-Db2exT_d.d.ts → admin-stats.service-G53IEkkt.d.ts} +1 -1
- package/dist/billing/index.d.cts +7 -7
- package/dist/billing/index.d.ts +7 -7
- package/dist/billing/index.js +38 -36
- package/dist/catalog/index.d.cts +13 -1
- package/dist/catalog/index.d.ts +13 -1
- package/dist/catalog/index.js +10 -8
- package/dist/checkout-offer/index.js +2 -2
- package/dist/chunk-2FR6ZL7R.js +0 -0
- package/dist/chunk-2SLTRKXC.js +0 -0
- package/dist/{chunk-7ZPFQXHM.js → chunk-3UZW7TU4.js} +1 -1
- package/dist/{chunk-XTVAFJ3P.js → chunk-AEEK64XD.js} +3 -3
- package/dist/chunk-AOQJEYOL.js +20 -0
- package/dist/chunk-H3W6QDQH.js +85 -0
- package/dist/{chunk-YYUXKLXK.js → chunk-I7GTA3RX.js} +2 -2
- package/dist/{chunk-SN5UOC43.js → chunk-IPOLCDKN.js} +11 -11
- package/dist/{chunk-B7NRY2LV.js → chunk-IVEA6M3B.js} +2 -40
- package/dist/chunk-JCAZT727.js +1883 -0
- package/dist/chunk-JTLKWP3Q.js +792 -0
- package/dist/{chunk-E56W4U2P.js → chunk-O2J2HDXA.js} +4 -4
- package/dist/{chunk-WKVBNTYC.js → chunk-OUDSQ3PU.js} +6 -6
- package/dist/{chunk-MJ32VNZD.js → chunk-P5D5CYO2.js} +18 -18
- package/dist/{chunk-L4SUWH5B.js → chunk-QUZU2M33.js} +2 -2
- package/dist/{chunk-WPUVJRIN.js → chunk-S6BAD5XM.js} +10 -10
- package/dist/{chunk-7X5MSAIL.js → chunk-SH7J5IU7.js} +9 -9
- package/dist/{chunk-37HEKVDH.js → chunk-V4OJ43K2.js} +10 -10
- package/dist/{chunk-NYLON2VC.js → chunk-VBRYPU7V.js} +2 -2
- package/dist/{chunk-B6IWLV7R.js → chunk-VTMCMVYQ.js} +6 -6
- package/dist/{chunk-YWDLYOFN.js → chunk-WSPHIIGS.js} +429 -1205
- package/dist/{chunk-5ZXBXYXU.js → chunk-WXYJHZCN.js} +3 -3
- package/dist/{chunk-6WL6VQ7Z.js → chunk-XBYAFEOR.js} +2 -2
- package/dist/{chunk-NNNXXUPT.js → chunk-XEW6YXL5.js} +28 -93
- package/dist/chunk-Y43SGD5J.js +50 -0
- package/dist/{define-saasicat-BK6RRWWG.d.ts → define-saasicat-BFgeoVG9.d.ts} +28 -5
- package/dist/{define-saasicat-D30I-WET.d.cts → define-saasicat-CNUWjfNK.d.cts} +28 -5
- package/dist/discovery/index.js +4 -4
- package/dist/{enforce-quota.interceptor-B9QueAFf.d.ts → enforce-quota.interceptor-BYqmdFB5.d.ts} +3 -3
- package/dist/{enforce-quota.interceptor-DY6CLjoP.d.cts → enforce-quota.interceptor-BxIR63Wn.d.cts} +3 -3
- package/dist/entitlement/index.js +9 -6
- package/dist/index.d.cts +10 -10
- package/dist/index.d.ts +10 -10
- package/dist/index.js +61 -52
- package/dist/{saas-platform.module-D1mUZPrz.d.ts → module-options-BymIkAsh.d.ts} +68 -55
- package/dist/{saas-platform.module-CgalxzcT.d.cts → module-options-DgwBKVpC.d.cts} +68 -55
- package/dist/platform/index.d.cts +204 -22
- package/dist/platform/index.d.ts +204 -22
- package/dist/platform/index.js +49 -25
- package/dist/promo/index.d.cts +3 -3
- package/dist/promo/index.d.ts +3 -3
- package/dist/promo/index.js +3 -3
- package/dist/{module-BE17XFLg.d.ts → promo.module-CnzddtJ0.d.ts} +1 -1
- package/dist/{module-CHMIyWlf.d.cts → promo.module-DokmUGmm.d.cts} +1 -1
- package/dist/registration/index.d.cts +1 -1
- package/dist/registration/index.d.ts +1 -1
- package/dist/registration/index.js +1 -1
- package/dist/subscription-contract/index.js +6 -3
- package/dist/{tenant-billing.controller-BSlpFqVA.d.ts → tenant-billing.controller-BQPy5yXh.d.ts} +2 -2
- package/dist/{tenant-billing.controller-DHSUrT_N.d.cts → tenant-billing.controller-CODIYzlI.d.cts} +2 -2
- package/dist/{subscription-bundles.module-BDbODWvA.d.ts → tenant-billing.module-B0stLoJn.d.ts} +51 -51
- package/dist/{subscription-bundles.module-Cfi-lFWF.d.cts → tenant-billing.module-BxJS6fX4.d.cts} +51 -51
- package/dist/testing/index.d.cts +14 -14
- package/dist/testing/index.d.ts +14 -14
- package/dist/testing/index.js +31 -29
- package/package.json +6 -6
- package/dist/chunk-L4KTQ6CL.js +0 -20
- package/dist/chunk-TCBYOFAC.js +0 -1464
- /package/dist/{chunk-FY4EJTV4.js → chunk-OYNHOY45.js} +0 -0
- /package/dist/{service-D48clObR.d.cts → promo.service-D48clObR.d.cts} +0 -0
- /package/dist/{service-D48clObR.d.ts → promo.service-D48clObR.d.ts} +0 -0
|
@@ -0,0 +1,1883 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PromoCodesModule
|
|
3
|
+
} from "./chunk-VTMCMVYQ.js";
|
|
4
|
+
import {
|
|
5
|
+
SubscriptionContractModule
|
|
6
|
+
} from "./chunk-Y43SGD5J.js";
|
|
7
|
+
import {
|
|
8
|
+
AdminManifestModule,
|
|
9
|
+
AdminManifestService,
|
|
10
|
+
AdminModule,
|
|
11
|
+
AdminResourcesModule,
|
|
12
|
+
AdminStatsModule,
|
|
13
|
+
MfaService,
|
|
14
|
+
SuperAdminGuard
|
|
15
|
+
} from "./chunk-IPOLCDKN.js";
|
|
16
|
+
import {
|
|
17
|
+
FEATURE_GUARD_MARKER,
|
|
18
|
+
PublicCatalogModule,
|
|
19
|
+
REQUIRE_FEATURE_KEY,
|
|
20
|
+
SubscriptionBundleModule,
|
|
21
|
+
TenantBillingModule,
|
|
22
|
+
isPlatformFeatureGuard
|
|
23
|
+
} from "./chunk-WSPHIIGS.js";
|
|
24
|
+
import {
|
|
25
|
+
CatalogModule
|
|
26
|
+
} from "./chunk-SH7J5IU7.js";
|
|
27
|
+
import {
|
|
28
|
+
CheckoutOfferModule
|
|
29
|
+
} from "./chunk-AEEK64XD.js";
|
|
30
|
+
import {
|
|
31
|
+
SaaSiCatPublicRoute
|
|
32
|
+
} from "./chunk-YSC46BGM.js";
|
|
33
|
+
import {
|
|
34
|
+
DiscoveryModule
|
|
35
|
+
} from "./chunk-XBYAFEOR.js";
|
|
36
|
+
import {
|
|
37
|
+
EntitlementModule
|
|
38
|
+
} from "./chunk-H3W6QDQH.js";
|
|
39
|
+
import {
|
|
40
|
+
LimitExceededError
|
|
41
|
+
} from "./chunk-XEW6YXL5.js";
|
|
42
|
+
import {
|
|
43
|
+
PLAN_CATALOG_TOKEN,
|
|
44
|
+
PlanCatalogModule
|
|
45
|
+
} from "./chunk-DQKCK7DX.js";
|
|
46
|
+
import {
|
|
47
|
+
codedError
|
|
48
|
+
} from "./chunk-EBJJNTW6.js";
|
|
49
|
+
import {
|
|
50
|
+
asProvider
|
|
51
|
+
} from "./chunk-SABTXESR.js";
|
|
52
|
+
import {
|
|
53
|
+
ENFORCE_QUOTA_KEY
|
|
54
|
+
} from "./chunk-QUZU2M33.js";
|
|
55
|
+
import {
|
|
56
|
+
__name
|
|
57
|
+
} from "./chunk-SHUYVCID.js";
|
|
58
|
+
|
|
59
|
+
// src/setup/setup.tokens.ts
|
|
60
|
+
var SETUP_PROVISIONING_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/nest/SetupProvisioningPort");
|
|
61
|
+
var SETUP_CONFIG_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/nest/SetupConfig");
|
|
62
|
+
|
|
63
|
+
// src/setup/setup.service.ts
|
|
64
|
+
import { BadRequestException, ConflictException, ForbiddenException, Inject, Injectable, UnauthorizedException } from "@nestjs/common";
|
|
65
|
+
import { isPlatformUserExistsError, SETUP_ERROR_CODES } from "@saasicat/types";
|
|
66
|
+
import { randomBytes, timingSafeEqual } from "crypto";
|
|
67
|
+
import QRCode from "qrcode";
|
|
68
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
69
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
70
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
71
|
+
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;
|
|
72
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
73
|
+
}
|
|
74
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
75
|
+
function _ts_metadata(k, v) {
|
|
76
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
77
|
+
}
|
|
78
|
+
__name(_ts_metadata, "_ts_metadata");
|
|
79
|
+
function _ts_param(paramIndex, decorator) {
|
|
80
|
+
return function(target, key) {
|
|
81
|
+
decorator(target, key, paramIndex);
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
__name(_ts_param, "_ts_param");
|
|
85
|
+
var GENERATED_PASSWORD_BYTES = 12;
|
|
86
|
+
function timingSafeEqualStr(a, b) {
|
|
87
|
+
const ab = Buffer.from(a, "utf8");
|
|
88
|
+
const bb = Buffer.from(b, "utf8");
|
|
89
|
+
if (ab.length !== bb.length) return false;
|
|
90
|
+
return timingSafeEqual(ab, bb);
|
|
91
|
+
}
|
|
92
|
+
__name(timingSafeEqualStr, "timingSafeEqualStr");
|
|
93
|
+
var SetupService = class {
|
|
94
|
+
static {
|
|
95
|
+
__name(this, "SetupService");
|
|
96
|
+
}
|
|
97
|
+
users;
|
|
98
|
+
config;
|
|
99
|
+
mfa;
|
|
100
|
+
constructor(users, config, mfa) {
|
|
101
|
+
this.users = users;
|
|
102
|
+
this.config = config;
|
|
103
|
+
this.mfa = mfa;
|
|
104
|
+
}
|
|
105
|
+
async status() {
|
|
106
|
+
return {
|
|
107
|
+
needsSetup: await this.users.countSuperAdmins() === 0
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
async setup(req) {
|
|
111
|
+
this.assertToken(req.token);
|
|
112
|
+
await this.assertNoSuperAdmin();
|
|
113
|
+
const email = req.email.trim().toLowerCase();
|
|
114
|
+
if (!email.includes("@")) {
|
|
115
|
+
throw new BadRequestException(codedError(SETUP_ERROR_CODES.INVALID_EMAIL));
|
|
116
|
+
}
|
|
117
|
+
const generated = !req.password;
|
|
118
|
+
const password = req.password ?? randomBytes(GENERATED_PASSWORD_BYTES).toString("base64url");
|
|
119
|
+
let user;
|
|
120
|
+
try {
|
|
121
|
+
user = await this.users.createSuperAdmin({
|
|
122
|
+
email,
|
|
123
|
+
password
|
|
124
|
+
});
|
|
125
|
+
} catch (err) {
|
|
126
|
+
if (isPlatformUserExistsError(err)) {
|
|
127
|
+
throw new ConflictException({
|
|
128
|
+
code: SETUP_ERROR_CODES.EMAIL_EXISTS,
|
|
129
|
+
message: err.message
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
throw err;
|
|
133
|
+
}
|
|
134
|
+
const totp = await this.mfa.setup(user.id, user.email, this.config.mfaIssuer);
|
|
135
|
+
const qrDataUrl = await QRCode.toDataURL(totp.otpauthUri, {
|
|
136
|
+
width: 220,
|
|
137
|
+
margin: 1
|
|
138
|
+
});
|
|
139
|
+
return {
|
|
140
|
+
userId: user.id,
|
|
141
|
+
email: user.email,
|
|
142
|
+
otpauthUri: totp.otpauthUri,
|
|
143
|
+
qrDataUrl,
|
|
144
|
+
secret: totp.secret,
|
|
145
|
+
...generated ? {
|
|
146
|
+
generatedPassword: password
|
|
147
|
+
} : {}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
async confirmMfa(req) {
|
|
151
|
+
this.assertToken(req.token);
|
|
152
|
+
const ok = await this.mfa.verify({
|
|
153
|
+
userId: req.userId,
|
|
154
|
+
code: req.code
|
|
155
|
+
});
|
|
156
|
+
return {
|
|
157
|
+
ok
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
assertToken(token) {
|
|
161
|
+
const expected = process.env[this.config.setupTokenEnvVar]?.trim();
|
|
162
|
+
if (!expected) {
|
|
163
|
+
throw new ForbiddenException({
|
|
164
|
+
code: SETUP_ERROR_CODES.SETUP_DISABLED,
|
|
165
|
+
message: `${this.config.setupTokenEnvVar} is not set \u2014 setup is disabled.`,
|
|
166
|
+
params: {
|
|
167
|
+
envVar: this.config.setupTokenEnvVar
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
if (!token || !timingSafeEqualStr(token, expected)) {
|
|
172
|
+
throw new UnauthorizedException(codedError(SETUP_ERROR_CODES.INVALID_SETUP_TOKEN));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
async assertNoSuperAdmin() {
|
|
176
|
+
if (await this.users.countSuperAdmins() > 0) {
|
|
177
|
+
throw new ConflictException(codedError(SETUP_ERROR_CODES.SETUP_ALREADY_DONE));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
SetupService = _ts_decorate([
|
|
182
|
+
Injectable(),
|
|
183
|
+
_ts_param(0, Inject(SETUP_PROVISIONING_PORT_TOKEN)),
|
|
184
|
+
_ts_param(1, Inject(SETUP_CONFIG_TOKEN)),
|
|
185
|
+
_ts_metadata("design:type", Function),
|
|
186
|
+
_ts_metadata("design:paramtypes", [
|
|
187
|
+
typeof SuperAdminProvisioningPort === "undefined" ? Object : SuperAdminProvisioningPort,
|
|
188
|
+
typeof SetupConfig === "undefined" ? Object : SetupConfig,
|
|
189
|
+
typeof MfaService === "undefined" ? Object : MfaService
|
|
190
|
+
])
|
|
191
|
+
], SetupService);
|
|
192
|
+
|
|
193
|
+
// src/setup/setup.controller.ts
|
|
194
|
+
import { Body, Controller, Get, Post } from "@nestjs/common";
|
|
195
|
+
import { IsEmail, IsNotEmpty, IsOptional, IsString, MinLength } from "class-validator";
|
|
196
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
197
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
198
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
199
|
+
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;
|
|
200
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
201
|
+
}
|
|
202
|
+
__name(_ts_decorate2, "_ts_decorate");
|
|
203
|
+
function _ts_metadata2(k, v) {
|
|
204
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
205
|
+
}
|
|
206
|
+
__name(_ts_metadata2, "_ts_metadata");
|
|
207
|
+
function _ts_param2(paramIndex, decorator) {
|
|
208
|
+
return function(target, key) {
|
|
209
|
+
decorator(target, key, paramIndex);
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
__name(_ts_param2, "_ts_param");
|
|
213
|
+
var SetupDto = class SetupDto2 {
|
|
214
|
+
static {
|
|
215
|
+
__name(this, "SetupDto");
|
|
216
|
+
}
|
|
217
|
+
token;
|
|
218
|
+
email;
|
|
219
|
+
password;
|
|
220
|
+
};
|
|
221
|
+
_ts_decorate2([
|
|
222
|
+
IsString(),
|
|
223
|
+
IsNotEmpty(),
|
|
224
|
+
_ts_metadata2("design:type", String)
|
|
225
|
+
], SetupDto.prototype, "token", void 0);
|
|
226
|
+
_ts_decorate2([
|
|
227
|
+
IsEmail(),
|
|
228
|
+
_ts_metadata2("design:type", String)
|
|
229
|
+
], SetupDto.prototype, "email", void 0);
|
|
230
|
+
_ts_decorate2([
|
|
231
|
+
IsOptional(),
|
|
232
|
+
IsString(),
|
|
233
|
+
MinLength(8),
|
|
234
|
+
_ts_metadata2("design:type", String)
|
|
235
|
+
], SetupDto.prototype, "password", void 0);
|
|
236
|
+
var SetupConfirmMfaDto = class SetupConfirmMfaDto2 {
|
|
237
|
+
static {
|
|
238
|
+
__name(this, "SetupConfirmMfaDto");
|
|
239
|
+
}
|
|
240
|
+
token;
|
|
241
|
+
userId;
|
|
242
|
+
code;
|
|
243
|
+
};
|
|
244
|
+
_ts_decorate2([
|
|
245
|
+
IsString(),
|
|
246
|
+
IsNotEmpty(),
|
|
247
|
+
_ts_metadata2("design:type", String)
|
|
248
|
+
], SetupConfirmMfaDto.prototype, "token", void 0);
|
|
249
|
+
_ts_decorate2([
|
|
250
|
+
IsString(),
|
|
251
|
+
IsNotEmpty(),
|
|
252
|
+
_ts_metadata2("design:type", String)
|
|
253
|
+
], SetupConfirmMfaDto.prototype, "userId", void 0);
|
|
254
|
+
_ts_decorate2([
|
|
255
|
+
IsString(),
|
|
256
|
+
IsNotEmpty(),
|
|
257
|
+
_ts_metadata2("design:type", String)
|
|
258
|
+
], SetupConfirmMfaDto.prototype, "code", void 0);
|
|
259
|
+
var SetupController = class {
|
|
260
|
+
static {
|
|
261
|
+
__name(this, "SetupController");
|
|
262
|
+
}
|
|
263
|
+
setup;
|
|
264
|
+
constructor(setup) {
|
|
265
|
+
this.setup = setup;
|
|
266
|
+
}
|
|
267
|
+
status() {
|
|
268
|
+
return this.setup.status();
|
|
269
|
+
}
|
|
270
|
+
run(dto) {
|
|
271
|
+
return this.setup.setup(dto);
|
|
272
|
+
}
|
|
273
|
+
confirm(dto) {
|
|
274
|
+
return this.setup.confirmMfa(dto);
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
_ts_decorate2([
|
|
278
|
+
Get("status"),
|
|
279
|
+
_ts_metadata2("design:type", Function),
|
|
280
|
+
_ts_metadata2("design:paramtypes", []),
|
|
281
|
+
_ts_metadata2("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
282
|
+
], SetupController.prototype, "status", null);
|
|
283
|
+
_ts_decorate2([
|
|
284
|
+
Post(),
|
|
285
|
+
_ts_param2(0, Body()),
|
|
286
|
+
_ts_metadata2("design:type", Function),
|
|
287
|
+
_ts_metadata2("design:paramtypes", [
|
|
288
|
+
typeof SetupDto === "undefined" ? Object : SetupDto
|
|
289
|
+
]),
|
|
290
|
+
_ts_metadata2("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
291
|
+
], SetupController.prototype, "run", null);
|
|
292
|
+
_ts_decorate2([
|
|
293
|
+
Post("confirm-mfa"),
|
|
294
|
+
_ts_param2(0, Body()),
|
|
295
|
+
_ts_metadata2("design:type", Function),
|
|
296
|
+
_ts_metadata2("design:paramtypes", [
|
|
297
|
+
typeof SetupConfirmMfaDto === "undefined" ? Object : SetupConfirmMfaDto
|
|
298
|
+
]),
|
|
299
|
+
_ts_metadata2("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
300
|
+
], SetupController.prototype, "confirm", null);
|
|
301
|
+
SetupController = _ts_decorate2([
|
|
302
|
+
Controller("admin/setup"),
|
|
303
|
+
SaaSiCatPublicRoute(),
|
|
304
|
+
_ts_metadata2("design:type", Function),
|
|
305
|
+
_ts_metadata2("design:paramtypes", [
|
|
306
|
+
typeof SetupService === "undefined" ? Object : SetupService
|
|
307
|
+
])
|
|
308
|
+
], SetupController);
|
|
309
|
+
|
|
310
|
+
// src/setup/setup.module.ts
|
|
311
|
+
import { Module } from "@nestjs/common";
|
|
312
|
+
function _ts_decorate3(decorators, target, key, desc) {
|
|
313
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
314
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
315
|
+
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;
|
|
316
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
317
|
+
}
|
|
318
|
+
__name(_ts_decorate3, "_ts_decorate");
|
|
319
|
+
var DEFAULT_SETUP_TOKEN_ENV_VAR = "SETUP_TOKEN";
|
|
320
|
+
var DEFAULT_MFA_ISSUER = "SuperAdmin";
|
|
321
|
+
var SetupModule = class _SetupModule {
|
|
322
|
+
static {
|
|
323
|
+
__name(this, "SetupModule");
|
|
324
|
+
}
|
|
325
|
+
static forRoot(options) {
|
|
326
|
+
return {
|
|
327
|
+
module: _SetupModule,
|
|
328
|
+
global: options.global ?? false,
|
|
329
|
+
imports: options.imports ?? [],
|
|
330
|
+
controllers: [
|
|
331
|
+
SetupController
|
|
332
|
+
],
|
|
333
|
+
providers: [
|
|
334
|
+
asProvider(SETUP_PROVISIONING_PORT_TOKEN, options.provisioningPort),
|
|
335
|
+
{
|
|
336
|
+
provide: SETUP_CONFIG_TOKEN,
|
|
337
|
+
useValue: {
|
|
338
|
+
setupTokenEnvVar: options.setupTokenEnvVar ?? DEFAULT_SETUP_TOKEN_ENV_VAR,
|
|
339
|
+
mfaIssuer: options.mfaIssuer ?? DEFAULT_MFA_ISSUER
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
SetupService,
|
|
343
|
+
...options.extraProviders ?? []
|
|
344
|
+
],
|
|
345
|
+
exports: [
|
|
346
|
+
SetupService
|
|
347
|
+
]
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
SetupModule = _ts_decorate3([
|
|
352
|
+
Module({})
|
|
353
|
+
], SetupModule);
|
|
354
|
+
|
|
355
|
+
// src/platform/enforcement-chain.check.ts
|
|
356
|
+
import { Inject as Inject2, Injectable as Injectable2, Optional } from "@nestjs/common";
|
|
357
|
+
import { DiscoveryService, MetadataScanner } from "@nestjs/core";
|
|
358
|
+
function _ts_decorate4(decorators, target, key, desc) {
|
|
359
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
360
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
361
|
+
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;
|
|
362
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
363
|
+
}
|
|
364
|
+
__name(_ts_decorate4, "_ts_decorate");
|
|
365
|
+
function _ts_metadata3(k, v) {
|
|
366
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
367
|
+
}
|
|
368
|
+
__name(_ts_metadata3, "_ts_metadata");
|
|
369
|
+
function _ts_param3(paramIndex, decorator) {
|
|
370
|
+
return function(target, key) {
|
|
371
|
+
decorator(target, key, paramIndex);
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
__name(_ts_param3, "_ts_param");
|
|
375
|
+
var GUARDS_METADATA = "__guards__";
|
|
376
|
+
var METHOD_METADATA = "method";
|
|
377
|
+
var ENFORCEMENT_CHAIN_STATE_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/nest/EnforcementChainState");
|
|
378
|
+
var EnforcementChainCheck = class {
|
|
379
|
+
static {
|
|
380
|
+
__name(this, "EnforcementChainCheck");
|
|
381
|
+
}
|
|
382
|
+
discoveryService;
|
|
383
|
+
metadataScanner;
|
|
384
|
+
state;
|
|
385
|
+
constructor(discoveryService, metadataScanner, state = {
|
|
386
|
+
featureEnforcementActive: true,
|
|
387
|
+
quotaEnforcementActive: true,
|
|
388
|
+
globalGuardBound: true,
|
|
389
|
+
globalGuardOptedOut: false
|
|
390
|
+
}) {
|
|
391
|
+
this.discoveryService = discoveryService;
|
|
392
|
+
this.metadataScanner = metadataScanner;
|
|
393
|
+
this.state = state;
|
|
394
|
+
}
|
|
395
|
+
onApplicationBootstrap() {
|
|
396
|
+
const annotated = this.findAnnotatedRoutes();
|
|
397
|
+
if (!this.state.featureEnforcementActive) {
|
|
398
|
+
if (annotated.length === 0) return;
|
|
399
|
+
throw new Error(`${annotated.length} route(s) are annotated for licence enforcement, but nothing can resolve a tenant to a plan, so @RequireFeature and @EnforceQuota do nothing at all: every annotated route serves unlicensed traffic and every quota reads as unlimited.
|
|
400
|
+
${describe(annotated)}
|
|
401
|
+
Set one of: adapters.planResolver, defaultPlanId, or tenantBilling. If this application deliberately publishes a catalogue without enforcing it, remove the annotations \u2014 a decorator that cannot act is not documentation, it is a claim the code does not keep.`);
|
|
402
|
+
}
|
|
403
|
+
if (!this.state.quotaEnforcementActive) {
|
|
404
|
+
const unenforced = annotated.filter((r) => r.quota !== null);
|
|
405
|
+
if (unenforced.length > 0) {
|
|
406
|
+
throw new Error(`${unenforced.length} route(s) enforce a quota, but nothing registers EnforceQuotaInterceptor, so every @EnforceQuota reads as unlimited and the limit a customer paid for is not applied:
|
|
407
|
+
${describe(unenforced)}
|
|
408
|
+
The V3 entitlement stack resolves features, not quotas: the interceptor is registered only where a plan can be resolved. Set one of adapters.planResolver, defaultPlanId, or tenantBilling with a subscription repository \u2014 or remove the annotations, because a decorator that cannot act is not documentation.
|
|
409
|
+
If this application enforces these quotas itself, set \`enforcementChainCheck: false\` \u2014 it turns this check off and nothing else.`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
if (this.state.globalGuardBound) return;
|
|
413
|
+
const uncovered = annotated.filter((r) => r.features.length > 0 && !r.guarded);
|
|
414
|
+
if (uncovered.length === 0) return;
|
|
415
|
+
const why = this.state.globalGuardOptedOut ? "globalFeatureGuard is off" : "the platform binds no global feature guard on this entitlement path";
|
|
416
|
+
throw new Error(`${why} and ${uncovered.length} annotated route(s) have no feature guard in front of them, so they serve unlicensed traffic:
|
|
417
|
+
${describe(uncovered)}
|
|
418
|
+
Bind one behind your auth guard \u2014 @UseGuards(JwtAuthGuard, StaticFeatureGuard), or FeatureGuard from @saasicat/nest/billing on the V3 entitlement path.
|
|
419
|
+
Two things this cannot see, and the way out of each: a guard of your own that wraps ours (put FEATURE_GUARD_MARKER on the wrapper class \u2014 the symbol StaticFeatureGuard and FeatureGuard both carry), and a feature guard you bind globally as an APP_GUARD rather than per controller. If either is what these routes use, set \`enforcementChainCheck: false\` \u2014 it turns this check off and nothing else.`);
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Every route carrying an enforcement annotation, with whether a
|
|
423
|
+
* recognisable feature guard covers it.
|
|
424
|
+
*
|
|
425
|
+
* Deliberately conservative in one direction only: an unrecognised wrapper
|
|
426
|
+
* is reported (and the message says so) rather than assumed safe, because
|
|
427
|
+
* the named route is cheap to check. The reverse — staying silent about a
|
|
428
|
+
* route with no guard at all — is the failure this exists to prevent.
|
|
429
|
+
*/
|
|
430
|
+
findAnnotatedRoutes() {
|
|
431
|
+
const annotated = [];
|
|
432
|
+
for (const wrapper of this.discoveryService.getControllers()) {
|
|
433
|
+
const instance = wrapper.instance;
|
|
434
|
+
if (!instance || typeof instance !== "object") continue;
|
|
435
|
+
const ctor = instance.constructor;
|
|
436
|
+
if (!ctor) continue;
|
|
437
|
+
const classGuards = readGuards(ctor);
|
|
438
|
+
const prototype = Object.getPrototypeOf(instance);
|
|
439
|
+
for (const method of this.metadataScanner.getAllMethodNames(prototype)) {
|
|
440
|
+
const handler = instance[method];
|
|
441
|
+
if (typeof handler !== "function") continue;
|
|
442
|
+
if (Reflect.getMetadata(METHOD_METADATA, handler) === void 0) continue;
|
|
443
|
+
const features = readFeatures(handler) ?? readFeatures(ctor) ?? /* none */
|
|
444
|
+
[];
|
|
445
|
+
const quota = readQuota(handler) ?? readQuota(ctor);
|
|
446
|
+
if (features.length === 0 && quota === null) continue;
|
|
447
|
+
const guards = [
|
|
448
|
+
...classGuards,
|
|
449
|
+
...readGuards(handler)
|
|
450
|
+
];
|
|
451
|
+
annotated.push({
|
|
452
|
+
controller: ctor.name,
|
|
453
|
+
handler: method,
|
|
454
|
+
features,
|
|
455
|
+
quota,
|
|
456
|
+
guarded: guards.some(isPlatformFeatureGuard)
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
return annotated;
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
EnforcementChainCheck = _ts_decorate4([
|
|
464
|
+
Injectable2(),
|
|
465
|
+
_ts_param3(0, Inject2(DiscoveryService)),
|
|
466
|
+
_ts_param3(1, Inject2(MetadataScanner)),
|
|
467
|
+
_ts_param3(2, Optional()),
|
|
468
|
+
_ts_param3(2, Inject2(ENFORCEMENT_CHAIN_STATE_TOKEN)),
|
|
469
|
+
_ts_metadata3("design:type", Function),
|
|
470
|
+
_ts_metadata3("design:paramtypes", [
|
|
471
|
+
typeof DiscoveryService === "undefined" ? Object : DiscoveryService,
|
|
472
|
+
typeof MetadataScanner === "undefined" ? Object : MetadataScanner,
|
|
473
|
+
typeof EnforcementChainState === "undefined" ? Object : EnforcementChainState
|
|
474
|
+
])
|
|
475
|
+
], EnforcementChainCheck);
|
|
476
|
+
function describe(routes) {
|
|
477
|
+
return routes.map((r) => {
|
|
478
|
+
const requirement = r.features.length > 0 ? `requires ${r.features.join(" or ")}` : `enforces quota ${r.quota}`;
|
|
479
|
+
return ` ${r.controller}.${r.handler} \u2014 ${requirement}`;
|
|
480
|
+
}).join("\n");
|
|
481
|
+
}
|
|
482
|
+
__name(describe, "describe");
|
|
483
|
+
function readFeatures(target) {
|
|
484
|
+
const value = Reflect.getMetadata(REQUIRE_FEATURE_KEY, target);
|
|
485
|
+
return Array.isArray(value) ? value : null;
|
|
486
|
+
}
|
|
487
|
+
__name(readFeatures, "readFeatures");
|
|
488
|
+
function readQuota(target) {
|
|
489
|
+
const value = Reflect.getMetadata(ENFORCE_QUOTA_KEY, target);
|
|
490
|
+
if (typeof value !== "object" || value === null) return null;
|
|
491
|
+
const quotaKey = value.quotaKey;
|
|
492
|
+
return typeof quotaKey === "string" ? quotaKey : null;
|
|
493
|
+
}
|
|
494
|
+
__name(readQuota, "readQuota");
|
|
495
|
+
function readGuards(target) {
|
|
496
|
+
const value = Reflect.getMetadata(GUARDS_METADATA, target);
|
|
497
|
+
if (!Array.isArray(value)) return [];
|
|
498
|
+
return value.flatMap((guard) => typeof guard === "function" ? [
|
|
499
|
+
guard
|
|
500
|
+
] : [
|
|
501
|
+
guard,
|
|
502
|
+
guard?.constructor
|
|
503
|
+
]);
|
|
504
|
+
}
|
|
505
|
+
__name(readGuards, "readGuards");
|
|
506
|
+
|
|
507
|
+
// src/platform/validation/rules.ts
|
|
508
|
+
import { assertPersistenceCapabilities } from "@saasicat/types";
|
|
509
|
+
var DOCS_BASE = "https://github.com/uelker70/saasicat/blob/main/docs/reference/options.md";
|
|
510
|
+
var headingSlug = /* @__PURE__ */ __name((text) => text.trim().toLowerCase().replace(/[^a-z0-9 -]/g, "").replace(/ /g, "-"), "headingSlug");
|
|
511
|
+
var docsUrlFor = /* @__PURE__ */ __name((id) => `${DOCS_BASE}#${headingSlug(id)}`, "docsUrlFor");
|
|
512
|
+
function optionsOf(slot) {
|
|
513
|
+
return typeof slot === "object" ? slot : void 0;
|
|
514
|
+
}
|
|
515
|
+
__name(optionsOf, "optionsOf");
|
|
516
|
+
var bundle = /* @__PURE__ */ __name((c) => c.options.persistence, "bundle");
|
|
517
|
+
var requiresFullEntitlement = /* @__PURE__ */ __name((c) => Boolean(c.options.entitlement || c.options.tenantBilling || c.options.subscriptionBundles), "requiresFullEntitlement");
|
|
518
|
+
var resolvesPlans = /* @__PURE__ */ __name((c) => Boolean(c.adapters.planResolver || c.options.defaultPlanId || c.options.tenantBilling && c.adapters.subscriptionRepository), "resolvesPlans");
|
|
519
|
+
function absent(required) {
|
|
520
|
+
return Object.keys(required).filter((key) => !required[key]);
|
|
521
|
+
}
|
|
522
|
+
__name(absent, "absent");
|
|
523
|
+
var list = /* @__PURE__ */ __name((names) => names.join(", "), "list");
|
|
524
|
+
var RULE_SPECS = [
|
|
525
|
+
{
|
|
526
|
+
id: "core.adapters-bound",
|
|
527
|
+
when: /* @__PURE__ */ __name(() => true, "when"),
|
|
528
|
+
assert: /* @__PURE__ */ __name((c) => absent(coreAdapters(c)).length === 0, "assert"),
|
|
529
|
+
message: /* @__PURE__ */ __name((c) => `the core adapters are missing: ${list(absent(coreAdapters(c)))}. Bind them through \`persistence: prismaPersistence({ client })\` or one by one through \`adapters\`.`, "message")
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
id: "catalog.identity-or-sink",
|
|
533
|
+
when: /* @__PURE__ */ __name((c) => !c.options.planCatalog, "when"),
|
|
534
|
+
assert: /* @__PURE__ */ __name((c) => Boolean(c.adapters.planCatalogReadSink && c.options.dbCatalog), "assert"),
|
|
535
|
+
message: "no plan catalogue is reachable. Either set `planCatalog` (the quickstart YAML path) or, for DB hydration, BOTH a `planCatalogReadSink` (via `adapters` or `persistence`) AND `dbCatalog` ({ projectKey, currency, vatRate }). Without the identity the sink loads with projectKey '' and the app boots with a silently empty catalogue."
|
|
536
|
+
},
|
|
537
|
+
{
|
|
538
|
+
id: "catalog.requires-persistence",
|
|
539
|
+
when: /* @__PURE__ */ __name((c) => Boolean(c.options.catalog), "when"),
|
|
540
|
+
assert: /* @__PURE__ */ __name((c) => Boolean(bundle(c)?.catalog), "assert"),
|
|
541
|
+
message: "catalog is enabled, but `persistence.catalog` is missing. Use a complete persistence bundle, or wire `CatalogModule` directly."
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
id: "admin-resources.requires-persistence",
|
|
545
|
+
when: /* @__PURE__ */ __name((c) => {
|
|
546
|
+
if (!c.options.adminResources) return false;
|
|
547
|
+
return !optionsOf(c.options.adminResources)?.resources;
|
|
548
|
+
}, "when"),
|
|
549
|
+
assert: /* @__PURE__ */ __name((c) => Boolean(bundle(c)?.adminResources), "assert"),
|
|
550
|
+
message: "adminResources is enabled, but `persistence.adminResources` is missing. Use a complete persistence bundle, or pass your own `adminResources.resources`."
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
id: "promo-codes.requires-persistence-and-transactions",
|
|
554
|
+
when: /* @__PURE__ */ __name((c) => Boolean(c.options.promoCodes), "when"),
|
|
555
|
+
assert: /* @__PURE__ */ __name((c) => Boolean(bundle(c)?.promo && c.adapters.transactionRunner), "assert"),
|
|
556
|
+
message: /* @__PURE__ */ __name((c) => `promoCodes requires \`persistence.promo\` and a transactionRunner; missing: ${list(absent({
|
|
557
|
+
"persistence.promo": bundle(c)?.promo,
|
|
558
|
+
transactionRunner: c.adapters.transactionRunner
|
|
559
|
+
}))}. Redeeming a code writes two rows and must not half-apply.`, "message")
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
id: "setup.requires-provisioning-port",
|
|
563
|
+
when: /* @__PURE__ */ __name((c) => Boolean(c.options.setup), "when"),
|
|
564
|
+
assert: /* @__PURE__ */ __name((c) => Boolean(optionsOf(c.options.setup)?.provisioningPort ?? bundle(c)?.core.superAdminProvisioning), "assert"),
|
|
565
|
+
message: "setup is enabled, but no provisioningPort is available. Set `setup.provisioningPort` or use a bundle providing `persistence.core.superAdminProvisioning`."
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
id: "admin-stats.requires-audit-stats-port",
|
|
569
|
+
when: /* @__PURE__ */ __name((c) => Boolean(c.options.adminStats), "when"),
|
|
570
|
+
assert: /* @__PURE__ */ __name((c) => Boolean(optionsOf(c.options.adminStats)?.auditStatsPort ?? bundle(c)?.core.auditStats), "assert"),
|
|
571
|
+
message: "adminStats is enabled, but no auditStatsPort is available. Set `adminStats.auditStatsPort` or use a bundle providing `persistence.core.auditStats`."
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
id: "subscription-contract.requires-repository",
|
|
575
|
+
when: /* @__PURE__ */ __name((c) => Boolean(c.options.subscriptionContract), "when"),
|
|
576
|
+
assert: /* @__PURE__ */ __name((c) => Boolean(optionsOf(c.options.subscriptionContract)?.subscriptionContractRepository ?? bundle(c)?.entitlement?.subscriptionContractRepository), "assert"),
|
|
577
|
+
message: "subscriptionContract is enabled, but no repository is available. Set `subscriptionContract.subscriptionContractRepository` or use a compatible persistence bundle."
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
id: "promo-codes.public-preview-requires-first-time-check",
|
|
581
|
+
when: /* @__PURE__ */ __name((c) => {
|
|
582
|
+
const config = optionsOf(c.options.promoCodes);
|
|
583
|
+
return config !== void 0 && config.includePublicController !== false;
|
|
584
|
+
}, "when"),
|
|
585
|
+
assert: /* @__PURE__ */ __name((c) => Boolean(optionsOf(c.options.promoCodes)?.firstTimeCustomerCheck), "assert"),
|
|
586
|
+
message: "the public promo preview is mounted but has no `firstTimeCustomerCheck`, so every first-time-only code would preview as unavailable. Pass one, or set `promoCodes.includePublicController: false`."
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
id: "tenant-billing.requires-ports",
|
|
590
|
+
when: /* @__PURE__ */ __name((c) => Boolean(c.options.tenantBilling) && !bundle(c)?.tenantBilling, "when"),
|
|
591
|
+
assert: /* @__PURE__ */ __name((c) => absent(tenantBillingPorts(c)).length === 0, "assert"),
|
|
592
|
+
message: /* @__PURE__ */ __name((c) => `tenantBilling is enabled without a \`persistence.tenantBilling\` slice, and these adapters are missing: ${list(absent(tenantBillingPorts(c)))}.`, "message")
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
id: "subscription-bundles.requires-tenant-billing",
|
|
596
|
+
when: /* @__PURE__ */ __name((c) => Boolean(c.options.subscriptionBundles), "when"),
|
|
597
|
+
assert: /* @__PURE__ */ __name((c) => Boolean(c.options.tenantBilling), "assert"),
|
|
598
|
+
message: "subscriptionBundles requires tenantBilling, so the two share authentication and subscription usage rather than resolving them twice."
|
|
599
|
+
},
|
|
600
|
+
{
|
|
601
|
+
id: "subscription-bundles.requires-repositories",
|
|
602
|
+
when: /* @__PURE__ */ __name((c) => Boolean(c.options.subscriptionBundles), "when"),
|
|
603
|
+
assert: /* @__PURE__ */ __name((c) => absent(bundleRepositories(c)).length === 0, "assert"),
|
|
604
|
+
message: /* @__PURE__ */ __name((c) => `subscriptionBundles is enabled, but these persistence adapters are missing: ${list(absent(bundleRepositories(c)))}.`, "message")
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
id: "entitlement.requires-adapters",
|
|
608
|
+
when: requiresFullEntitlement,
|
|
609
|
+
assert: /* @__PURE__ */ __name((c) => absent(entitlementAdapters(c)).length === 0, "assert"),
|
|
610
|
+
message: /* @__PURE__ */ __name((c) => `entitlement active or required by the standard stack, but adapters are missing: ${list(absent(entitlementAdapters(c)))}.`, "message")
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
id: "entitlement.requires-transactional-persistence",
|
|
614
|
+
when: /* @__PURE__ */ __name((c) => requiresFullEntitlement(c) && Boolean(bundle(c)), "when"),
|
|
615
|
+
assert: /* @__PURE__ */ __name((c) => absent(entitlementCapabilities(c)).length === 0, "assert"),
|
|
616
|
+
message: /* @__PURE__ */ __name((c) => `the entitlement stack needs a store that can do ${list(absent(entitlementCapabilities(c)))}, and the persistence bundle declares it cannot. \`enforceLimit()\` reads a counter and writes it back; without a transaction and a row lock two concurrent requests both pass the limit.`, "message"),
|
|
617
|
+
// `PersistenceCapabilityError` carries `code:
|
|
618
|
+
// 'PERSISTENCE_CAPABILITY_MISSING'`, which is a machine-readable part
|
|
619
|
+
// of the published contract rather than prose. Raising it through the
|
|
620
|
+
// shared helper keeps the class, the code and the wording identical to
|
|
621
|
+
// what a consumer catches today.
|
|
622
|
+
error: /* @__PURE__ */ __name((c) => {
|
|
623
|
+
assertPersistenceCapabilities(bundle(c).capabilities, {
|
|
624
|
+
transactions: true,
|
|
625
|
+
pessimisticLocking: true
|
|
626
|
+
}, "SaaSiCatModule entitlement (transactional enforceLimit)");
|
|
627
|
+
throw new Error("persistence capabilities check did not fail as expected");
|
|
628
|
+
}, "error")
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
id: "tenant-manifest.requires-plan-resolution",
|
|
632
|
+
when: /* @__PURE__ */ __name((c) => Boolean(c.options.tenantManifest), "when"),
|
|
633
|
+
assert: resolvesPlans,
|
|
634
|
+
message: "tenantManifest is enabled but nothing can resolve a tenant to a plan, so it could not filter features. Set `defaultPlanId`, `adapters.planResolver`, or enable `tenantBilling`."
|
|
635
|
+
}
|
|
636
|
+
];
|
|
637
|
+
var PLATFORM_RULES = RULE_SPECS.map((spec) => ({
|
|
638
|
+
...spec,
|
|
639
|
+
docs: docsUrlFor(spec.id)
|
|
640
|
+
}));
|
|
641
|
+
var coreAdapters = /* @__PURE__ */ __name((c) => ({
|
|
642
|
+
mfa: c.adapters.mfa,
|
|
643
|
+
audit: c.adapters.audit,
|
|
644
|
+
rlsBypass: c.adapters.rlsBypass
|
|
645
|
+
}), "coreAdapters");
|
|
646
|
+
var tenantBillingPorts = /* @__PURE__ */ __name((c) => {
|
|
647
|
+
const config = optionsOf(c.options.tenantBilling);
|
|
648
|
+
return {
|
|
649
|
+
subscriptionUsagePort: config?.subscriptionUsagePort,
|
|
650
|
+
subscriptionWritePort: config?.subscriptionWritePort
|
|
651
|
+
};
|
|
652
|
+
}, "tenantBillingPorts");
|
|
653
|
+
var bundleRepositories = /* @__PURE__ */ __name((c) => ({
|
|
654
|
+
subscriptionBundleRepository: bundle(c)?.entitlement?.subscriptionBundleRepository,
|
|
655
|
+
bundleRepository: bundle(c)?.catalog?.bundleRepository
|
|
656
|
+
}), "bundleRepositories");
|
|
657
|
+
var entitlementAdapters = /* @__PURE__ */ __name((c) => ({
|
|
658
|
+
subscriptionRepository: c.adapters.subscriptionRepository,
|
|
659
|
+
planVersionRepository: c.adapters.planVersionRepository,
|
|
660
|
+
transactionRunner: c.adapters.transactionRunner
|
|
661
|
+
}), "entitlementAdapters");
|
|
662
|
+
var entitlementCapabilities = /* @__PURE__ */ __name((c) => ({
|
|
663
|
+
transactions: bundle(c)?.capabilities.transactions,
|
|
664
|
+
"pessimistic locking": bundle(c)?.capabilities.pessimisticLocking
|
|
665
|
+
}), "entitlementCapabilities");
|
|
666
|
+
|
|
667
|
+
// src/platform/validation/validate.ts
|
|
668
|
+
function findViolations(configuration, rules = PLATFORM_RULES) {
|
|
669
|
+
const violations = [];
|
|
670
|
+
for (const rule of rules) {
|
|
671
|
+
if (!rule.when(configuration)) continue;
|
|
672
|
+
if (rule.assert(configuration)) continue;
|
|
673
|
+
violations.push({
|
|
674
|
+
id: rule.id,
|
|
675
|
+
message: typeof rule.message === "string" ? rule.message : rule.message(configuration),
|
|
676
|
+
docs: rule.docs
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
return violations;
|
|
680
|
+
}
|
|
681
|
+
__name(findViolations, "findViolations");
|
|
682
|
+
var SaaSiCatConfigurationError = class extends Error {
|
|
683
|
+
static {
|
|
684
|
+
__name(this, "SaaSiCatConfigurationError");
|
|
685
|
+
}
|
|
686
|
+
violations;
|
|
687
|
+
constructor(violations) {
|
|
688
|
+
super(formatViolations(violations)), this.violations = violations;
|
|
689
|
+
this.name = "SaaSiCatConfigurationError";
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
function formatViolations(violations) {
|
|
693
|
+
const heading = violations.length === 1 ? "SaaSiCatModule.forRoot: 1 configuration problem:" : `SaaSiCatModule.forRoot: ${violations.length} configuration problems:`;
|
|
694
|
+
const body = violations.map((v, index) => ` ${index + 1}. [${v.id}] ${v.message}
|
|
695
|
+
\u2192 ${v.docs}`).join("\n");
|
|
696
|
+
return `${heading}
|
|
697
|
+
${body}`;
|
|
698
|
+
}
|
|
699
|
+
__name(formatViolations, "formatViolations");
|
|
700
|
+
function assertConfiguration(configuration, rules = PLATFORM_RULES) {
|
|
701
|
+
const violations = findViolations(configuration, rules);
|
|
702
|
+
if (violations.length === 0) return;
|
|
703
|
+
if (violations.length === 1) {
|
|
704
|
+
const rule = rules.find((r) => r.id === violations[0].id);
|
|
705
|
+
if (rule?.error) throw rule.error(configuration);
|
|
706
|
+
}
|
|
707
|
+
throw new SaaSiCatConfigurationError(violations);
|
|
708
|
+
}
|
|
709
|
+
__name(assertConfiguration, "assertConfiguration");
|
|
710
|
+
|
|
711
|
+
// src/platform/quota-providers-usage-snapshot.ts
|
|
712
|
+
var QuotaProvidersUsageSnapshot = class {
|
|
713
|
+
static {
|
|
714
|
+
__name(this, "QuotaProvidersUsageSnapshot");
|
|
715
|
+
}
|
|
716
|
+
providers;
|
|
717
|
+
constructor(providers) {
|
|
718
|
+
this.providers = providers;
|
|
719
|
+
}
|
|
720
|
+
async snapshot(tenantId) {
|
|
721
|
+
const entries = await Promise.all(this.providers.map(async (provider) => [
|
|
722
|
+
provider.key,
|
|
723
|
+
await provider.count(tenantId)
|
|
724
|
+
]));
|
|
725
|
+
return Object.fromEntries(entries);
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
// src/platform/compose/context.ts
|
|
730
|
+
function optionsOf2(slot) {
|
|
731
|
+
return typeof slot === "object" ? slot : {};
|
|
732
|
+
}
|
|
733
|
+
__name(optionsOf2, "optionsOf");
|
|
734
|
+
|
|
735
|
+
// src/platform/compose/admin-resources.ts
|
|
736
|
+
function composeAdminResources({ options, persistence }) {
|
|
737
|
+
if (!options.adminResources) return [];
|
|
738
|
+
const config = optionsOf2(options.adminResources);
|
|
739
|
+
return [
|
|
740
|
+
AdminResourcesModule.forRoot({
|
|
741
|
+
resources: config.resources ?? persistence?.adminResources?.resources,
|
|
742
|
+
guards: config.guards ?? [
|
|
743
|
+
...options.controller.guards,
|
|
744
|
+
SuperAdminGuard
|
|
745
|
+
],
|
|
746
|
+
imports: config.imports ?? options.imports
|
|
747
|
+
})
|
|
748
|
+
];
|
|
749
|
+
}
|
|
750
|
+
__name(composeAdminResources, "composeAdminResources");
|
|
751
|
+
|
|
752
|
+
// src/platform/compose/catalog.ts
|
|
753
|
+
function composeCatalog({ options, persistence, appInfo }) {
|
|
754
|
+
const config = options.catalog;
|
|
755
|
+
if (!config) return [];
|
|
756
|
+
const catalog = persistence?.catalog;
|
|
757
|
+
const modules = [
|
|
758
|
+
CatalogModule.forRoot({
|
|
759
|
+
planRepository: catalog.planRepository,
|
|
760
|
+
bundleRepository: catalog.bundleRepository,
|
|
761
|
+
catalogEntryRepository: catalog.catalogEntryRepository,
|
|
762
|
+
marketingProjectionRepository: catalog.marketingProjectionRepository,
|
|
763
|
+
promotionRepository: catalog.promotionRepository,
|
|
764
|
+
marketingSettingsRepository: catalog.marketingSettingsRepository,
|
|
765
|
+
controller: config.adminControllers === false ? void 0 : {
|
|
766
|
+
guards: options.controller.guards
|
|
767
|
+
},
|
|
768
|
+
imports: config.imports ?? options.imports,
|
|
769
|
+
extraProviders: config.extraProviders,
|
|
770
|
+
strictModeCheckMode: config.strictModeCheckMode,
|
|
771
|
+
autoSyncDiscoveryAtBoot: config.autoSyncDiscoveryAtBoot,
|
|
772
|
+
marketedOnlyFeatures: config.marketedOnlyFeatures,
|
|
773
|
+
featureUiRegistry: config.featureUiRegistry,
|
|
774
|
+
publicMarketingCatalog: config.publicMarketingCatalog
|
|
775
|
+
})
|
|
776
|
+
];
|
|
777
|
+
if (config.publicCatalog !== false) {
|
|
778
|
+
modules.push(PublicCatalogModule.forRoot({
|
|
779
|
+
featureUiRegistry: config.featureUiRegistry,
|
|
780
|
+
projectKey: appInfo.key,
|
|
781
|
+
bundleRepository: catalog.bundleRepository,
|
|
782
|
+
marketingRepository: catalog.marketingProjectionRepository,
|
|
783
|
+
catalogEntryRepository: catalog.catalogEntryRepository,
|
|
784
|
+
imports: config.imports ?? options.imports
|
|
785
|
+
}));
|
|
786
|
+
}
|
|
787
|
+
return modules;
|
|
788
|
+
}
|
|
789
|
+
__name(composeCatalog, "composeCatalog");
|
|
790
|
+
|
|
791
|
+
// src/platform/compose/entitlement.ts
|
|
792
|
+
function composeEntitlement({ options, adapters, persistence, requiresFullEntitlement: requiresFullEntitlement2 }) {
|
|
793
|
+
if (!requiresFullEntitlement2) return [];
|
|
794
|
+
return [
|
|
795
|
+
EntitlementModule.forRoot({
|
|
796
|
+
subscriptionRepository: adapters.subscriptionRepository,
|
|
797
|
+
planVersionRepository: adapters.planVersionRepository,
|
|
798
|
+
transactionRunner: adapters.transactionRunner,
|
|
799
|
+
resolutionConfig: options.entitlement ? options.entitlement.resolutionConfig : void 0,
|
|
800
|
+
subscriptionContractRepository: persistence?.entitlement?.subscriptionContractRepository,
|
|
801
|
+
subscriptionBundleRepository: persistence?.entitlement?.subscriptionBundleRepository,
|
|
802
|
+
bundleRepository: persistence?.entitlement?.bundleRepository,
|
|
803
|
+
imports: options.imports,
|
|
804
|
+
global: Boolean(options.tenantBilling || options.subscriptionBundles)
|
|
805
|
+
})
|
|
806
|
+
];
|
|
807
|
+
}
|
|
808
|
+
__name(composeEntitlement, "composeEntitlement");
|
|
809
|
+
|
|
810
|
+
// src/platform/compose/optional-modules.ts
|
|
811
|
+
function composeSetup({ options, persistence }) {
|
|
812
|
+
if (!options.setup) return [];
|
|
813
|
+
const config = optionsOf2(options.setup);
|
|
814
|
+
return [
|
|
815
|
+
SetupModule.forRoot({
|
|
816
|
+
...config,
|
|
817
|
+
provisioningPort: config.provisioningPort ?? persistence?.core.superAdminProvisioning,
|
|
818
|
+
imports: config.imports ?? options.imports
|
|
819
|
+
})
|
|
820
|
+
];
|
|
821
|
+
}
|
|
822
|
+
__name(composeSetup, "composeSetup");
|
|
823
|
+
function composeAdminStats({ options, persistence }) {
|
|
824
|
+
const config = options.adminStats;
|
|
825
|
+
if (!config) return [];
|
|
826
|
+
return [
|
|
827
|
+
AdminStatsModule.forRoot({
|
|
828
|
+
...config,
|
|
829
|
+
guards: config.guards ?? [
|
|
830
|
+
...options.controller.guards,
|
|
831
|
+
SuperAdminGuard
|
|
832
|
+
],
|
|
833
|
+
auditStatsPort: config.auditStatsPort ?? persistence?.core.auditStats,
|
|
834
|
+
imports: config.imports ?? options.imports
|
|
835
|
+
})
|
|
836
|
+
];
|
|
837
|
+
}
|
|
838
|
+
__name(composeAdminStats, "composeAdminStats");
|
|
839
|
+
function composeCheckoutOffer({ options, persistence }) {
|
|
840
|
+
const config = options.checkoutOffer;
|
|
841
|
+
if (!config) return [];
|
|
842
|
+
return [
|
|
843
|
+
CheckoutOfferModule.forRoot({
|
|
844
|
+
...config,
|
|
845
|
+
bundleRepository: config.bundleRepository ?? persistence?.catalog?.bundleRepository,
|
|
846
|
+
planRepository: config.planRepository ?? persistence?.catalog?.planRepository,
|
|
847
|
+
catalogEntryRepository: config.catalogEntryRepository ?? persistence?.catalog?.catalogEntryRepository,
|
|
848
|
+
imports: config.imports ?? options.imports
|
|
849
|
+
})
|
|
850
|
+
];
|
|
851
|
+
}
|
|
852
|
+
__name(composeCheckoutOffer, "composeCheckoutOffer");
|
|
853
|
+
function composeSubscriptionContract({ options, persistence }) {
|
|
854
|
+
if (!options.subscriptionContract) return [];
|
|
855
|
+
const config = optionsOf2(options.subscriptionContract);
|
|
856
|
+
return [
|
|
857
|
+
SubscriptionContractModule.forRoot({
|
|
858
|
+
...config,
|
|
859
|
+
subscriptionContractRepository: config.subscriptionContractRepository ?? persistence?.entitlement?.subscriptionContractRepository,
|
|
860
|
+
imports: config.imports ?? options.imports
|
|
861
|
+
})
|
|
862
|
+
];
|
|
863
|
+
}
|
|
864
|
+
__name(composeSubscriptionContract, "composeSubscriptionContract");
|
|
865
|
+
|
|
866
|
+
// src/platform/compose/promo-codes.ts
|
|
867
|
+
function composePromoCodes({ options, adapters, persistence }) {
|
|
868
|
+
if (!options.promoCodes) return [];
|
|
869
|
+
const config = optionsOf2(options.promoCodes);
|
|
870
|
+
const promo = persistence?.promo;
|
|
871
|
+
const { firstTimeCustomerCheck, adminGuards, imports: promoImports, includePublicController, adminController, ...promoOptions } = config;
|
|
872
|
+
return [
|
|
873
|
+
PromoCodesModule.forRoot({
|
|
874
|
+
...promoOptions,
|
|
875
|
+
promoCodeRepository: promo.promoCodeRepository,
|
|
876
|
+
redemptionRepository: promo.redemptionRepository,
|
|
877
|
+
validationLogRepository: promo.validationLogRepository,
|
|
878
|
+
subscriptionLookup: promo.subscriptionLookup,
|
|
879
|
+
revenueAggregator: promo.revenueAggregator,
|
|
880
|
+
transactionRunner: adapters.transactionRunner,
|
|
881
|
+
firstTimeCustomerCheck: firstTimeCustomerCheck ?? {
|
|
882
|
+
hasExistingCustomerForEmail: /* @__PURE__ */ __name(async () => false, "hasExistingCustomerForEmail")
|
|
883
|
+
},
|
|
884
|
+
includePublicController: includePublicController ?? false,
|
|
885
|
+
adminController: adminController === false ? false : {
|
|
886
|
+
guards: adminGuards ?? [
|
|
887
|
+
...options.controller.guards,
|
|
888
|
+
SuperAdminGuard
|
|
889
|
+
]
|
|
890
|
+
},
|
|
891
|
+
imports: promoImports ?? options.imports
|
|
892
|
+
})
|
|
893
|
+
];
|
|
894
|
+
}
|
|
895
|
+
__name(composePromoCodes, "composePromoCodes");
|
|
896
|
+
|
|
897
|
+
// src/platform/compose/tenant-bindings.ts
|
|
898
|
+
function normalizeTenantAuthGuards(guards) {
|
|
899
|
+
if (!Array.isArray(guards)) return guards;
|
|
900
|
+
return {
|
|
901
|
+
useFactory: /* @__PURE__ */ __name((...instances) => instances, "useFactory"),
|
|
902
|
+
inject: guards
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
__name(normalizeTenantAuthGuards, "normalizeTenantAuthGuards");
|
|
906
|
+
function quotaUsageSnapshotProvider(quotaProviders) {
|
|
907
|
+
return {
|
|
908
|
+
useFactory: /* @__PURE__ */ __name((...providers) => new QuotaProvidersUsageSnapshot(providers), "useFactory"),
|
|
909
|
+
inject: [
|
|
910
|
+
...quotaProviders
|
|
911
|
+
]
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
__name(quotaUsageSnapshotProvider, "quotaUsageSnapshotProvider");
|
|
915
|
+
|
|
916
|
+
// src/platform/compose/tenant-billing.ts
|
|
917
|
+
function composeTenantBilling(ctx) {
|
|
918
|
+
const config = ctx.options.tenantBilling;
|
|
919
|
+
if (!config) return [];
|
|
920
|
+
const tenantSlice = ctx.persistence?.tenantBilling;
|
|
921
|
+
const { authGuards, subscriptionUsagePort, usageSnapshotPort, subscriptionWritePort, imports: tenantImports, extraProviders, extraExports, ...tenantOptions } = config;
|
|
922
|
+
const quotaProviders = ctx.options.quotaProviders ?? [];
|
|
923
|
+
ctx.shared.quotaProvidersHostedByTenantBilling = quotaProviders.length > 0;
|
|
924
|
+
ctx.shared.authGuards = normalizeTenantAuthGuards(authGuards);
|
|
925
|
+
ctx.shared.subscriptionUsagePort = subscriptionUsagePort ?? tenantSlice?.subscriptionUsagePort;
|
|
926
|
+
return [
|
|
927
|
+
TenantBillingModule.forRoot({
|
|
928
|
+
...tenantOptions,
|
|
929
|
+
authGuards: ctx.shared.authGuards,
|
|
930
|
+
subscriptionUsagePort: ctx.shared.subscriptionUsagePort,
|
|
931
|
+
usageSnapshotPort: usageSnapshotPort ?? tenantSlice?.usageSnapshotPort ?? quotaUsageSnapshotProvider(quotaProviders),
|
|
932
|
+
subscriptionWritePort: subscriptionWritePort ?? tenantSlice?.subscriptionWritePort,
|
|
933
|
+
imports: tenantImports ?? ctx.options.imports,
|
|
934
|
+
extraProviders: [
|
|
935
|
+
...quotaProviders,
|
|
936
|
+
...extraProviders ?? []
|
|
937
|
+
],
|
|
938
|
+
extraExports: [
|
|
939
|
+
...quotaProviders,
|
|
940
|
+
...extraExports ?? []
|
|
941
|
+
]
|
|
942
|
+
})
|
|
943
|
+
];
|
|
944
|
+
}
|
|
945
|
+
__name(composeTenantBilling, "composeTenantBilling");
|
|
946
|
+
function composeSubscriptionBundles(ctx) {
|
|
947
|
+
if (!ctx.options.subscriptionBundles) return [];
|
|
948
|
+
const config = optionsOf2(ctx.options.subscriptionBundles);
|
|
949
|
+
const { controller, imports: bundleImports, extraProviders, ...bundleOptions } = config;
|
|
950
|
+
return [
|
|
951
|
+
SubscriptionBundleModule.forRoot({
|
|
952
|
+
...bundleOptions,
|
|
953
|
+
subscriptionBundleRepository: ctx.persistence?.entitlement?.subscriptionBundleRepository,
|
|
954
|
+
bundleRepository: ctx.persistence?.catalog?.bundleRepository,
|
|
955
|
+
controller: controller === false ? void 0 : {
|
|
956
|
+
...controller ?? {},
|
|
957
|
+
authGuards: ctx.shared.authGuards,
|
|
958
|
+
subscriptionUsagePort: ctx.shared.subscriptionUsagePort
|
|
959
|
+
},
|
|
960
|
+
// Falls back to the tenant-billing imports before the shared ones,
|
|
961
|
+
// because the two controllers usually resolve the same app
|
|
962
|
+
// providers and an app that scoped them for one meant both.
|
|
963
|
+
imports: bundleImports ?? optionsOf2(ctx.options.tenantBilling).imports ?? ctx.options.imports,
|
|
964
|
+
extraProviders
|
|
965
|
+
})
|
|
966
|
+
];
|
|
967
|
+
}
|
|
968
|
+
__name(composeSubscriptionBundles, "composeSubscriptionBundles");
|
|
969
|
+
|
|
970
|
+
// src/platform/compose/index.ts
|
|
971
|
+
var FEATURE_COMPOSERS = [
|
|
972
|
+
composeEntitlement,
|
|
973
|
+
composeCatalog,
|
|
974
|
+
composeAdminResources,
|
|
975
|
+
composePromoCodes,
|
|
976
|
+
// Before the bundles — see the note above.
|
|
977
|
+
composeTenantBilling,
|
|
978
|
+
composeSubscriptionBundles,
|
|
979
|
+
composeSetup,
|
|
980
|
+
composeAdminStats,
|
|
981
|
+
composeCheckoutOffer,
|
|
982
|
+
composeSubscriptionContract
|
|
983
|
+
];
|
|
984
|
+
function composeFeatures(ctx) {
|
|
985
|
+
return FEATURE_COMPOSERS.flatMap((compose) => compose(ctx));
|
|
986
|
+
}
|
|
987
|
+
__name(composeFeatures, "composeFeatures");
|
|
988
|
+
|
|
989
|
+
// src/platform/compose/module-exports.ts
|
|
990
|
+
var ALWAYS = [
|
|
991
|
+
PlanCatalogModule,
|
|
992
|
+
DiscoveryModule,
|
|
993
|
+
AdminModule,
|
|
994
|
+
// ADMIN_MANIFEST_CONFIG travels transitively: re-exporting an imported
|
|
995
|
+
// module's token directly is an UnknownExportException at boot — the module
|
|
996
|
+
// export here already carries it.
|
|
997
|
+
AdminManifestModule
|
|
998
|
+
];
|
|
999
|
+
function optionalExports({ options, requiresFullEntitlement: requiresFullEntitlement2 }) {
|
|
1000
|
+
return [
|
|
1001
|
+
[
|
|
1002
|
+
requiresFullEntitlement2,
|
|
1003
|
+
EntitlementModule
|
|
1004
|
+
],
|
|
1005
|
+
[
|
|
1006
|
+
options.catalog,
|
|
1007
|
+
CatalogModule
|
|
1008
|
+
],
|
|
1009
|
+
[
|
|
1010
|
+
options.tenantBilling,
|
|
1011
|
+
TenantBillingModule
|
|
1012
|
+
],
|
|
1013
|
+
[
|
|
1014
|
+
options.subscriptionBundles,
|
|
1015
|
+
SubscriptionBundleModule
|
|
1016
|
+
],
|
|
1017
|
+
[
|
|
1018
|
+
options.adminResources,
|
|
1019
|
+
AdminResourcesModule
|
|
1020
|
+
],
|
|
1021
|
+
[
|
|
1022
|
+
options.promoCodes,
|
|
1023
|
+
PromoCodesModule
|
|
1024
|
+
],
|
|
1025
|
+
[
|
|
1026
|
+
options.setup,
|
|
1027
|
+
SetupModule
|
|
1028
|
+
],
|
|
1029
|
+
[
|
|
1030
|
+
options.adminStats,
|
|
1031
|
+
AdminStatsModule
|
|
1032
|
+
],
|
|
1033
|
+
[
|
|
1034
|
+
options.checkoutOffer,
|
|
1035
|
+
CheckoutOfferModule
|
|
1036
|
+
],
|
|
1037
|
+
[
|
|
1038
|
+
options.subscriptionContract,
|
|
1039
|
+
SubscriptionContractModule
|
|
1040
|
+
]
|
|
1041
|
+
];
|
|
1042
|
+
}
|
|
1043
|
+
__name(optionalExports, "optionalExports");
|
|
1044
|
+
var MOUNTED_BUT_NOT_EXPORTED = [
|
|
1045
|
+
"PublicCatalogModule"
|
|
1046
|
+
];
|
|
1047
|
+
function composeModuleExports(ctx, ownExports) {
|
|
1048
|
+
return [
|
|
1049
|
+
...ALWAYS,
|
|
1050
|
+
...optionalExports(ctx).filter(([enabled]) => Boolean(enabled)).map(([, module]) => module),
|
|
1051
|
+
...ownExports
|
|
1052
|
+
];
|
|
1053
|
+
}
|
|
1054
|
+
__name(composeModuleExports, "composeModuleExports");
|
|
1055
|
+
|
|
1056
|
+
// src/platform/plan-resolver.port.ts
|
|
1057
|
+
var PLAN_RESOLVER_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/nest/PlanResolverPort");
|
|
1058
|
+
var StaticPlanResolver = class {
|
|
1059
|
+
static {
|
|
1060
|
+
__name(this, "StaticPlanResolver");
|
|
1061
|
+
}
|
|
1062
|
+
planId;
|
|
1063
|
+
constructor(planId) {
|
|
1064
|
+
this.planId = planId;
|
|
1065
|
+
}
|
|
1066
|
+
async getPlanIdForTenant() {
|
|
1067
|
+
return this.planId;
|
|
1068
|
+
}
|
|
1069
|
+
};
|
|
1070
|
+
|
|
1071
|
+
// src/platform/static-entitlement.service.ts
|
|
1072
|
+
import { Inject as Inject3, Injectable as Injectable3 } from "@nestjs/common";
|
|
1073
|
+
function _ts_decorate5(decorators, target, key, desc) {
|
|
1074
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1075
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1076
|
+
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;
|
|
1077
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1078
|
+
}
|
|
1079
|
+
__name(_ts_decorate5, "_ts_decorate");
|
|
1080
|
+
function _ts_metadata4(k, v) {
|
|
1081
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1082
|
+
}
|
|
1083
|
+
__name(_ts_metadata4, "_ts_metadata");
|
|
1084
|
+
function _ts_param4(paramIndex, decorator) {
|
|
1085
|
+
return function(target, key) {
|
|
1086
|
+
decorator(target, key, paramIndex);
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
__name(_ts_param4, "_ts_param");
|
|
1090
|
+
var StaticEntitlementService = class {
|
|
1091
|
+
static {
|
|
1092
|
+
__name(this, "StaticEntitlementService");
|
|
1093
|
+
}
|
|
1094
|
+
catalog;
|
|
1095
|
+
resolver;
|
|
1096
|
+
constructor(catalog, resolver) {
|
|
1097
|
+
this.catalog = catalog;
|
|
1098
|
+
this.resolver = resolver;
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* Returns the effective features + quotas of a tenant from the
|
|
1102
|
+
* plan catalog. When the plan is missing: an empty snapshot (no feature,
|
|
1103
|
+
* all quotas at 0).
|
|
1104
|
+
*/
|
|
1105
|
+
async snapshot(tenantId) {
|
|
1106
|
+
const planId = await this.resolver.getPlanIdForTenant(tenantId);
|
|
1107
|
+
if (!planId) {
|
|
1108
|
+
return {
|
|
1109
|
+
planId: null,
|
|
1110
|
+
features: [],
|
|
1111
|
+
quotas: {}
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
const plan = this.findPlan(planId);
|
|
1115
|
+
if (!plan) {
|
|
1116
|
+
return {
|
|
1117
|
+
planId,
|
|
1118
|
+
features: [],
|
|
1119
|
+
quotas: {}
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
return {
|
|
1123
|
+
planId,
|
|
1124
|
+
features: plan.features ?? [],
|
|
1125
|
+
quotas: plan.quotas ?? {}
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
/** Convenience helper for tests / synchronous checks in the UI manifest. */
|
|
1129
|
+
async hasFeature(tenantId, featureKey) {
|
|
1130
|
+
const snap = await this.snapshot(tenantId);
|
|
1131
|
+
return snap.features.includes(featureKey);
|
|
1132
|
+
}
|
|
1133
|
+
async quotaLimit(tenantId, quotaKey) {
|
|
1134
|
+
const snap = await this.snapshot(tenantId);
|
|
1135
|
+
const value = snap.quotas[quotaKey];
|
|
1136
|
+
return value === void 0 ? null : value;
|
|
1137
|
+
}
|
|
1138
|
+
findPlan(planId) {
|
|
1139
|
+
return (this.catalog.plans ?? []).find((p) => p.id === planId);
|
|
1140
|
+
}
|
|
1141
|
+
};
|
|
1142
|
+
StaticEntitlementService = _ts_decorate5([
|
|
1143
|
+
Injectable3(),
|
|
1144
|
+
_ts_param4(0, Inject3(PLAN_CATALOG_TOKEN)),
|
|
1145
|
+
_ts_param4(1, Inject3(PLAN_RESOLVER_PORT_TOKEN)),
|
|
1146
|
+
_ts_metadata4("design:type", Function),
|
|
1147
|
+
_ts_metadata4("design:paramtypes", [
|
|
1148
|
+
typeof PlanCatalog === "undefined" ? Object : PlanCatalog,
|
|
1149
|
+
typeof PlanResolverPort === "undefined" ? Object : PlanResolverPort
|
|
1150
|
+
])
|
|
1151
|
+
], StaticEntitlementService);
|
|
1152
|
+
|
|
1153
|
+
// src/platform/tenant-manifest.service.ts
|
|
1154
|
+
import { Injectable as Injectable4 } from "@nestjs/common";
|
|
1155
|
+
function _ts_decorate6(decorators, target, key, desc) {
|
|
1156
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1157
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1158
|
+
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;
|
|
1159
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1160
|
+
}
|
|
1161
|
+
__name(_ts_decorate6, "_ts_decorate");
|
|
1162
|
+
function _ts_metadata5(k, v) {
|
|
1163
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1164
|
+
}
|
|
1165
|
+
__name(_ts_metadata5, "_ts_metadata");
|
|
1166
|
+
var TenantManifestService = class {
|
|
1167
|
+
static {
|
|
1168
|
+
__name(this, "TenantManifestService");
|
|
1169
|
+
}
|
|
1170
|
+
entitlements;
|
|
1171
|
+
navItems = [];
|
|
1172
|
+
constructor(entitlements) {
|
|
1173
|
+
this.entitlements = entitlements;
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Call in the app's `OnModuleInit`: registers app navigation for the
|
|
1177
|
+
* Tenant UI. Idempotent: the same `id` overwrites.
|
|
1178
|
+
*/
|
|
1179
|
+
registerNavItem(item) {
|
|
1180
|
+
const existing = this.navItems.findIndex((i) => i.id === item.id);
|
|
1181
|
+
if (existing >= 0) this.navItems[existing] = item;
|
|
1182
|
+
else this.navItems.push(item);
|
|
1183
|
+
}
|
|
1184
|
+
/** Bulk variant. */
|
|
1185
|
+
registerNavItems(items) {
|
|
1186
|
+
for (const item of items) this.registerNavItem(item);
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Returns the filtered manifest for a Tenant. NavItems with
|
|
1190
|
+
* `requiresFeature` are filtered out server-side as soon as none of the
|
|
1191
|
+
* required features are present in the entitlement snapshot.
|
|
1192
|
+
*/
|
|
1193
|
+
async getManifest(tenantId) {
|
|
1194
|
+
const snap = await this.entitlements.snapshot(tenantId);
|
|
1195
|
+
const featureSet = new Set(snap.features);
|
|
1196
|
+
const visible = this.navItems.filter((item) => isVisible(item, featureSet)).map((item) => ({
|
|
1197
|
+
...item
|
|
1198
|
+
})).sort((a, b) => (a.order ?? 100) - (b.order ?? 100));
|
|
1199
|
+
return {
|
|
1200
|
+
schemaVersion: 1,
|
|
1201
|
+
tenant: {
|
|
1202
|
+
id: tenantId
|
|
1203
|
+
},
|
|
1204
|
+
planId: snap.planId,
|
|
1205
|
+
features: snap.features,
|
|
1206
|
+
quotas: snap.quotas,
|
|
1207
|
+
navigation: visible
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
};
|
|
1211
|
+
TenantManifestService = _ts_decorate6([
|
|
1212
|
+
Injectable4(),
|
|
1213
|
+
_ts_metadata5("design:type", Function),
|
|
1214
|
+
_ts_metadata5("design:paramtypes", [
|
|
1215
|
+
typeof StaticEntitlementService === "undefined" ? Object : StaticEntitlementService
|
|
1216
|
+
])
|
|
1217
|
+
], TenantManifestService);
|
|
1218
|
+
function isVisible(item, features) {
|
|
1219
|
+
if (!item.requiresFeature) return true;
|
|
1220
|
+
const required = Array.isArray(item.requiresFeature) ? item.requiresFeature : [
|
|
1221
|
+
item.requiresFeature
|
|
1222
|
+
];
|
|
1223
|
+
return required.some((f) => features.has(f));
|
|
1224
|
+
}
|
|
1225
|
+
__name(isVisible, "isVisible");
|
|
1226
|
+
|
|
1227
|
+
// src/platform/tenant-manifest.controller.ts
|
|
1228
|
+
import { Controller as Controller2, Get as Get2, Inject as Inject4, Req, UseGuards } from "@nestjs/common";
|
|
1229
|
+
function _ts_decorate7(decorators, target, key, desc) {
|
|
1230
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1231
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1232
|
+
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;
|
|
1233
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1234
|
+
}
|
|
1235
|
+
__name(_ts_decorate7, "_ts_decorate");
|
|
1236
|
+
function _ts_metadata6(k, v) {
|
|
1237
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1238
|
+
}
|
|
1239
|
+
__name(_ts_metadata6, "_ts_metadata");
|
|
1240
|
+
function _ts_param5(paramIndex, decorator) {
|
|
1241
|
+
return function(target, key) {
|
|
1242
|
+
decorator(target, key, paramIndex);
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
__name(_ts_param5, "_ts_param");
|
|
1246
|
+
function buildTenantManifestController(options) {
|
|
1247
|
+
const path = options.path ?? "tenant/manifest";
|
|
1248
|
+
let GeneratedTenantManifestController = class GeneratedTenantManifestController {
|
|
1249
|
+
static {
|
|
1250
|
+
__name(this, "GeneratedTenantManifestController");
|
|
1251
|
+
}
|
|
1252
|
+
svc;
|
|
1253
|
+
constructor(svc) {
|
|
1254
|
+
this.svc = svc;
|
|
1255
|
+
}
|
|
1256
|
+
async get(req) {
|
|
1257
|
+
const tenantId = req.user?.tenantId ?? req.tenantId;
|
|
1258
|
+
if (!tenantId) {
|
|
1259
|
+
throw new Error("No tenant on the request \u2014 TenantManifest requires user.tenantId.");
|
|
1260
|
+
}
|
|
1261
|
+
return this.svc.getManifest(tenantId);
|
|
1262
|
+
}
|
|
1263
|
+
};
|
|
1264
|
+
_ts_decorate7([
|
|
1265
|
+
Get2(),
|
|
1266
|
+
_ts_param5(0, Req()),
|
|
1267
|
+
_ts_metadata6("design:type", Function),
|
|
1268
|
+
_ts_metadata6("design:paramtypes", [
|
|
1269
|
+
typeof RequestWithUser === "undefined" ? Object : RequestWithUser
|
|
1270
|
+
]),
|
|
1271
|
+
_ts_metadata6("design:returntype", Promise)
|
|
1272
|
+
], GeneratedTenantManifestController.prototype, "get", null);
|
|
1273
|
+
GeneratedTenantManifestController = _ts_decorate7([
|
|
1274
|
+
Controller2(path),
|
|
1275
|
+
UseGuards(...options.guards),
|
|
1276
|
+
_ts_param5(0, Inject4(TenantManifestService)),
|
|
1277
|
+
_ts_metadata6("design:type", Function),
|
|
1278
|
+
_ts_metadata6("design:paramtypes", [
|
|
1279
|
+
typeof TenantManifestService === "undefined" ? Object : TenantManifestService
|
|
1280
|
+
])
|
|
1281
|
+
], GeneratedTenantManifestController);
|
|
1282
|
+
return GeneratedTenantManifestController;
|
|
1283
|
+
}
|
|
1284
|
+
__name(buildTenantManifestController, "buildTenantManifestController");
|
|
1285
|
+
|
|
1286
|
+
// src/platform/enforce-quota.interceptor.ts
|
|
1287
|
+
import { Inject as Inject5, Injectable as Injectable5, Optional as Optional2 } from "@nestjs/common";
|
|
1288
|
+
import { Reflector } from "@nestjs/core";
|
|
1289
|
+
import { from } from "rxjs";
|
|
1290
|
+
import { switchMap } from "rxjs/operators";
|
|
1291
|
+
function _ts_decorate8(decorators, target, key, desc) {
|
|
1292
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1293
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1294
|
+
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;
|
|
1295
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1296
|
+
}
|
|
1297
|
+
__name(_ts_decorate8, "_ts_decorate");
|
|
1298
|
+
function _ts_metadata7(k, v) {
|
|
1299
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1300
|
+
}
|
|
1301
|
+
__name(_ts_metadata7, "_ts_metadata");
|
|
1302
|
+
function _ts_param6(paramIndex, decorator) {
|
|
1303
|
+
return function(target, key) {
|
|
1304
|
+
decorator(target, key, paramIndex);
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
__name(_ts_param6, "_ts_param");
|
|
1308
|
+
var QUOTA_PROVIDERS_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/nest/QuotaProviders");
|
|
1309
|
+
var EnforceQuotaInterceptor = class {
|
|
1310
|
+
static {
|
|
1311
|
+
__name(this, "EnforceQuotaInterceptor");
|
|
1312
|
+
}
|
|
1313
|
+
reflector;
|
|
1314
|
+
entitlements;
|
|
1315
|
+
providers;
|
|
1316
|
+
constructor(reflector, entitlements, providers = null) {
|
|
1317
|
+
this.reflector = reflector;
|
|
1318
|
+
this.entitlements = entitlements;
|
|
1319
|
+
this.providers = providers;
|
|
1320
|
+
}
|
|
1321
|
+
intercept(context, next) {
|
|
1322
|
+
const meta = this.reflector.getAllAndOverride(ENFORCE_QUOTA_KEY, [
|
|
1323
|
+
context.getHandler(),
|
|
1324
|
+
context.getClass()
|
|
1325
|
+
]);
|
|
1326
|
+
if (!meta?.quotaKey) return next.handle();
|
|
1327
|
+
return from(this.check(context, meta)).pipe(switchMap(() => next.handle()));
|
|
1328
|
+
}
|
|
1329
|
+
async check(context, meta) {
|
|
1330
|
+
const request = context.switchToHttp().getRequest();
|
|
1331
|
+
const user = request.user;
|
|
1332
|
+
if (!user) return;
|
|
1333
|
+
const role = user.role ?? user.platformRole;
|
|
1334
|
+
if (role === "SUPER_ADMIN") return;
|
|
1335
|
+
const tenantId = request.tenantId ?? user.tenantId;
|
|
1336
|
+
if (!tenantId) return;
|
|
1337
|
+
const provider = (this.providers ?? []).find((p) => p.key === meta.quotaKey);
|
|
1338
|
+
if (!provider) {
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
const limit = await this.entitlements.quotaLimit(tenantId, meta.quotaKey);
|
|
1342
|
+
if (limit === null || limit === -1) return;
|
|
1343
|
+
const current = await provider.count(tenantId);
|
|
1344
|
+
const delta = meta.incrementBy ?? 1;
|
|
1345
|
+
if (current + delta > limit) {
|
|
1346
|
+
throw new LimitExceededError(meta.quotaKey, limit, current);
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
};
|
|
1350
|
+
EnforceQuotaInterceptor = _ts_decorate8([
|
|
1351
|
+
Injectable5(),
|
|
1352
|
+
_ts_param6(0, Inject5(Reflector)),
|
|
1353
|
+
_ts_param6(1, Inject5(StaticEntitlementService)),
|
|
1354
|
+
_ts_param6(2, Optional2()),
|
|
1355
|
+
_ts_param6(2, Inject5(QUOTA_PROVIDERS_TOKEN)),
|
|
1356
|
+
_ts_metadata7("design:type", Function),
|
|
1357
|
+
_ts_metadata7("design:paramtypes", [
|
|
1358
|
+
typeof Reflector === "undefined" ? Object : Reflector,
|
|
1359
|
+
typeof StaticEntitlementService === "undefined" ? Object : StaticEntitlementService,
|
|
1360
|
+
Object
|
|
1361
|
+
])
|
|
1362
|
+
], EnforceQuotaInterceptor);
|
|
1363
|
+
|
|
1364
|
+
// src/platform/static-feature.guard.ts
|
|
1365
|
+
import { ForbiddenException as ForbiddenException2, Inject as Inject6, Injectable as Injectable6, Optional as Optional3 } from "@nestjs/common";
|
|
1366
|
+
import { Reflector as Reflector2 } from "@nestjs/core";
|
|
1367
|
+
import { AUTH_ERROR_CODES, FEATURE_NOT_LICENSED } from "@saasicat/types";
|
|
1368
|
+
function _ts_decorate9(decorators, target, key, desc) {
|
|
1369
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1370
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1371
|
+
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;
|
|
1372
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1373
|
+
}
|
|
1374
|
+
__name(_ts_decorate9, "_ts_decorate");
|
|
1375
|
+
function _ts_metadata8(k, v) {
|
|
1376
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1377
|
+
}
|
|
1378
|
+
__name(_ts_metadata8, "_ts_metadata");
|
|
1379
|
+
function _ts_param7(paramIndex, decorator) {
|
|
1380
|
+
return function(target, key) {
|
|
1381
|
+
decorator(target, key, paramIndex);
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
__name(_ts_param7, "_ts_param");
|
|
1385
|
+
var STATIC_FEATURE_GUARD_CONFIG_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/nest/StaticFeatureGuardConfig");
|
|
1386
|
+
var StaticFeatureGuard = class {
|
|
1387
|
+
static {
|
|
1388
|
+
__name(this, "StaticFeatureGuard");
|
|
1389
|
+
}
|
|
1390
|
+
reflector;
|
|
1391
|
+
entitlements;
|
|
1392
|
+
config;
|
|
1393
|
+
/** Lets `EnforcementChainCheck` recognise this guard across CJS entries. */
|
|
1394
|
+
static [FEATURE_GUARD_MARKER] = true;
|
|
1395
|
+
constructor(reflector, entitlements, config = null) {
|
|
1396
|
+
this.reflector = reflector;
|
|
1397
|
+
this.entitlements = entitlements;
|
|
1398
|
+
this.config = config;
|
|
1399
|
+
}
|
|
1400
|
+
async canActivate(context) {
|
|
1401
|
+
const required = this.reflector.getAllAndOverride(REQUIRE_FEATURE_KEY, [
|
|
1402
|
+
context.getHandler(),
|
|
1403
|
+
context.getClass()
|
|
1404
|
+
]);
|
|
1405
|
+
if (!required || required.length === 0) return true;
|
|
1406
|
+
const request = context.switchToHttp().getRequest();
|
|
1407
|
+
const user = request.user;
|
|
1408
|
+
if (!user) {
|
|
1409
|
+
throw new ForbiddenException2({
|
|
1410
|
+
code: AUTH_ERROR_CODES.NOT_AUTHENTICATED,
|
|
1411
|
+
message: "Not authenticated"
|
|
1412
|
+
});
|
|
1413
|
+
}
|
|
1414
|
+
const role = this.config?.userRoleResolver ? this.config.userRoleResolver(user) : user.role ?? user.platformRole;
|
|
1415
|
+
if (role === "SUPER_ADMIN") return true;
|
|
1416
|
+
const tenantId = this.config?.tenantIdResolver ? this.config.tenantIdResolver(request) : request.tenantId ?? user.tenantId;
|
|
1417
|
+
if (!tenantId) {
|
|
1418
|
+
throw new ForbiddenException2({
|
|
1419
|
+
code: AUTH_ERROR_CODES.NO_TENANT_ASSIGNED,
|
|
1420
|
+
message: "No tenant assigned"
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
const snap = await this.entitlements.snapshot(tenantId);
|
|
1424
|
+
const allowed = required.some((f) => snap.features.includes(f));
|
|
1425
|
+
if (!allowed) {
|
|
1426
|
+
const body = {
|
|
1427
|
+
code: FEATURE_NOT_LICENSED,
|
|
1428
|
+
featureKey: required[0],
|
|
1429
|
+
featureKeys: required,
|
|
1430
|
+
offers: [],
|
|
1431
|
+
message: `Feature ${required.join(" / ")} is not included in the current plan.`
|
|
1432
|
+
};
|
|
1433
|
+
throw new ForbiddenException2(body);
|
|
1434
|
+
}
|
|
1435
|
+
return true;
|
|
1436
|
+
}
|
|
1437
|
+
};
|
|
1438
|
+
StaticFeatureGuard = _ts_decorate9([
|
|
1439
|
+
Injectable6(),
|
|
1440
|
+
_ts_param7(0, Inject6(Reflector2)),
|
|
1441
|
+
_ts_param7(1, Inject6(StaticEntitlementService)),
|
|
1442
|
+
_ts_param7(2, Optional3()),
|
|
1443
|
+
_ts_param7(2, Inject6(STATIC_FEATURE_GUARD_CONFIG_TOKEN)),
|
|
1444
|
+
_ts_metadata8("design:type", Function),
|
|
1445
|
+
_ts_metadata8("design:paramtypes", [
|
|
1446
|
+
typeof Reflector2 === "undefined" ? Object : Reflector2,
|
|
1447
|
+
typeof StaticEntitlementService === "undefined" ? Object : StaticEntitlementService,
|
|
1448
|
+
Object
|
|
1449
|
+
])
|
|
1450
|
+
], StaticFeatureGuard);
|
|
1451
|
+
|
|
1452
|
+
// src/platform/subscription-plan-resolver.ts
|
|
1453
|
+
var DEFAULT_ACTIVE_STATUSES = /* @__PURE__ */ new Set([
|
|
1454
|
+
"ACTIVE",
|
|
1455
|
+
"TRIAL"
|
|
1456
|
+
]);
|
|
1457
|
+
var SubscriptionPlanResolver = class {
|
|
1458
|
+
static {
|
|
1459
|
+
__name(this, "SubscriptionPlanResolver");
|
|
1460
|
+
}
|
|
1461
|
+
subscriptions;
|
|
1462
|
+
activeStatuses;
|
|
1463
|
+
constructor(subscriptions, activeStatuses = DEFAULT_ACTIVE_STATUSES) {
|
|
1464
|
+
this.subscriptions = subscriptions;
|
|
1465
|
+
this.activeStatuses = activeStatuses;
|
|
1466
|
+
}
|
|
1467
|
+
async getPlanIdForTenant(tenantId) {
|
|
1468
|
+
const subscription = await this.subscriptions.findByTenantId(tenantId);
|
|
1469
|
+
if (!subscription || !this.activeStatuses.has(subscription.status)) return null;
|
|
1470
|
+
return subscription.plan;
|
|
1471
|
+
}
|
|
1472
|
+
};
|
|
1473
|
+
|
|
1474
|
+
// src/platform/saasicat.module.ts
|
|
1475
|
+
import { Logger, Module as Module2 } from "@nestjs/common";
|
|
1476
|
+
import { DiscoveryModule as NestDiscoveryModule } from "@nestjs/core";
|
|
1477
|
+
|
|
1478
|
+
// src/platform/compose/manifest.ts
|
|
1479
|
+
var PLATFORM_SUBSCRIPTION_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/nest/PlatformSubscriptionRepository");
|
|
1480
|
+
var STANDARD_MANIFEST_REGISTRATION_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/nest/StandardManifestRegistration");
|
|
1481
|
+
function buildStandardManifestContribution(catalog, adminResources, promoCodes) {
|
|
1482
|
+
const capabilities = {
|
|
1483
|
+
"discovery.read": true
|
|
1484
|
+
};
|
|
1485
|
+
if (catalog && catalog.adminControllers !== false) {
|
|
1486
|
+
Object.assign(capabilities, {
|
|
1487
|
+
"plans.read": true,
|
|
1488
|
+
"plans.publish": true,
|
|
1489
|
+
"bundles.read": true,
|
|
1490
|
+
"bundles.write": true,
|
|
1491
|
+
"bundles.publish": true,
|
|
1492
|
+
"marketingProjections.read": true,
|
|
1493
|
+
"marketingProjections.write": true
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
const navigation = {};
|
|
1497
|
+
if (adminResources) {
|
|
1498
|
+
Object.assign(capabilities, {
|
|
1499
|
+
"tenants.read": true,
|
|
1500
|
+
"tenants.suspend": true,
|
|
1501
|
+
"tenants.reactivate": true,
|
|
1502
|
+
"users.read": true,
|
|
1503
|
+
"audit.read": true,
|
|
1504
|
+
"subscriptions.read": true
|
|
1505
|
+
});
|
|
1506
|
+
navigation.standardPages = {
|
|
1507
|
+
subscriptions: {
|
|
1508
|
+
enabled: true,
|
|
1509
|
+
requiredCapability: "subscriptions.read"
|
|
1510
|
+
}
|
|
1511
|
+
};
|
|
1512
|
+
}
|
|
1513
|
+
if (promoCodes) {
|
|
1514
|
+
Object.assign(capabilities, {
|
|
1515
|
+
"promoCodes.read": true,
|
|
1516
|
+
"promoCodes.write": true,
|
|
1517
|
+
"promoCodes.delete": true
|
|
1518
|
+
});
|
|
1519
|
+
navigation.standardPages = {
|
|
1520
|
+
...navigation.standardPages ?? {},
|
|
1521
|
+
promoCodes: {
|
|
1522
|
+
enabled: true,
|
|
1523
|
+
requiredCapability: "promoCodes.read"
|
|
1524
|
+
}
|
|
1525
|
+
};
|
|
1526
|
+
}
|
|
1527
|
+
return {
|
|
1528
|
+
capabilities,
|
|
1529
|
+
navigation: navigation.standardPages ? navigation : void 0
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
__name(buildStandardManifestContribution, "buildStandardManifestContribution");
|
|
1533
|
+
function buildMinimalManifestConfig() {
|
|
1534
|
+
return {
|
|
1535
|
+
useFactory: /* @__PURE__ */ __name((catalog) => ({
|
|
1536
|
+
project: {
|
|
1537
|
+
key: catalog.projectKey,
|
|
1538
|
+
displayName: catalog.app?.name ?? catalog.projectKey,
|
|
1539
|
+
label: catalog.app?.label,
|
|
1540
|
+
icon: catalog.app?.icon,
|
|
1541
|
+
logoUrl: catalog.app?.logoUrl,
|
|
1542
|
+
environment: process.env.NODE_ENV === "production" ? "production" : "development",
|
|
1543
|
+
availableLocales: catalog.marketing?.availableLocales,
|
|
1544
|
+
defaultLocale: catalog.marketing?.availableLocales?.[0]
|
|
1545
|
+
},
|
|
1546
|
+
build: {
|
|
1547
|
+
platformPackageVersion: "0.0.0",
|
|
1548
|
+
appVersion: "0.0.0"
|
|
1549
|
+
},
|
|
1550
|
+
planCatalogSnapshot: {
|
|
1551
|
+
source: "saasicat-module",
|
|
1552
|
+
hash: "sha256-quickstart",
|
|
1553
|
+
currency: catalog.currency,
|
|
1554
|
+
vatRate: catalog.vatRate,
|
|
1555
|
+
plans: catalog.plans ?? [],
|
|
1556
|
+
features: catalog.features ?? []
|
|
1557
|
+
}
|
|
1558
|
+
}), "useFactory"),
|
|
1559
|
+
inject: [
|
|
1560
|
+
PLAN_CATALOG_TOKEN
|
|
1561
|
+
]
|
|
1562
|
+
};
|
|
1563
|
+
}
|
|
1564
|
+
__name(buildMinimalManifestConfig, "buildMinimalManifestConfig");
|
|
1565
|
+
|
|
1566
|
+
// src/platform/compose/base.ts
|
|
1567
|
+
var DEFAULT_SNAPSHOT_PATH = "var/discovery-snapshot.json";
|
|
1568
|
+
function resolveAppInfo(options) {
|
|
1569
|
+
return options.app ?? {
|
|
1570
|
+
key: options.planCatalog?.projectKey ?? options.dbCatalog?.projectKey ?? "app",
|
|
1571
|
+
version: options.planCatalog?.app?.version ?? options.dbCatalog?.app?.version ?? "0.0.0"
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
__name(resolveAppInfo, "resolveAppInfo");
|
|
1575
|
+
function composePlanCatalog(options, sink) {
|
|
1576
|
+
if (options.planCatalog) {
|
|
1577
|
+
return PlanCatalogModule.forRootWithCatalog(options.planCatalog, {
|
|
1578
|
+
global: true
|
|
1579
|
+
});
|
|
1580
|
+
}
|
|
1581
|
+
const dbCatalog = options.dbCatalog;
|
|
1582
|
+
return PlanCatalogModule.forRoot({
|
|
1583
|
+
projectKey: dbCatalog.projectKey,
|
|
1584
|
+
app: dbCatalog.app,
|
|
1585
|
+
currency: dbCatalog.currency,
|
|
1586
|
+
vatRate: dbCatalog.vatRate,
|
|
1587
|
+
marketing: dbCatalog.marketing,
|
|
1588
|
+
sink,
|
|
1589
|
+
imports: options.imports
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
__name(composePlanCatalog, "composePlanCatalog");
|
|
1593
|
+
function composeBaseModules(options, appInfo, ports) {
|
|
1594
|
+
return [
|
|
1595
|
+
DiscoveryModule.forRoot({
|
|
1596
|
+
app: appInfo,
|
|
1597
|
+
controller: {
|
|
1598
|
+
guards: options.controller.guards
|
|
1599
|
+
},
|
|
1600
|
+
imports: options.imports,
|
|
1601
|
+
snapshotPath: options.discoverySnapshotPath === void 0 ? DEFAULT_SNAPSHOT_PATH : options.discoverySnapshotPath
|
|
1602
|
+
}),
|
|
1603
|
+
AdminModule.forRoot({
|
|
1604
|
+
...ports,
|
|
1605
|
+
imports: options.imports,
|
|
1606
|
+
global: true
|
|
1607
|
+
}),
|
|
1608
|
+
AdminManifestModule.forRoot({
|
|
1609
|
+
config: options.adminManifestConfig ?? buildMinimalManifestConfig(),
|
|
1610
|
+
extraProviders: options.adminManifestExtraProviders,
|
|
1611
|
+
includeManifestController: options.includeManifestController,
|
|
1612
|
+
guards: options.controller.guards,
|
|
1613
|
+
reloadGuards: options.reloadGuards,
|
|
1614
|
+
imports: options.imports,
|
|
1615
|
+
// Global like AdminModule above: apps register their manifest
|
|
1616
|
+
// contribution by injecting AdminManifestService into one of their
|
|
1617
|
+
// own modules (handbook §6.6). Re-exporting the module from here
|
|
1618
|
+
// does not make that injection resolvable.
|
|
1619
|
+
global: true
|
|
1620
|
+
})
|
|
1621
|
+
];
|
|
1622
|
+
}
|
|
1623
|
+
__name(composeBaseModules, "composeBaseModules");
|
|
1624
|
+
|
|
1625
|
+
// src/platform/compose/tenant-manifest.ts
|
|
1626
|
+
var NONE = {
|
|
1627
|
+
providers: [],
|
|
1628
|
+
exports: [],
|
|
1629
|
+
controllers: []
|
|
1630
|
+
};
|
|
1631
|
+
function composeTenantManifest(ctx) {
|
|
1632
|
+
const { options } = ctx;
|
|
1633
|
+
if (!options.tenantManifest) return NONE;
|
|
1634
|
+
const tenantAuthGuards = optionsOf2(options.tenantBilling).authGuards;
|
|
1635
|
+
return {
|
|
1636
|
+
providers: [
|
|
1637
|
+
TenantManifestService
|
|
1638
|
+
],
|
|
1639
|
+
exports: [
|
|
1640
|
+
TenantManifestService
|
|
1641
|
+
],
|
|
1642
|
+
controllers: [
|
|
1643
|
+
buildTenantManifestController(options.tenantManifest === true ? {
|
|
1644
|
+
guards: Array.isArray(tenantAuthGuards) ? tenantAuthGuards : options.controller.guards
|
|
1645
|
+
} : options.tenantManifest)
|
|
1646
|
+
]
|
|
1647
|
+
};
|
|
1648
|
+
}
|
|
1649
|
+
__name(composeTenantManifest, "composeTenantManifest");
|
|
1650
|
+
|
|
1651
|
+
// src/platform/compose/enforcement-runtime.ts
|
|
1652
|
+
import { APP_GUARD, APP_INTERCEPTOR } from "@nestjs/core";
|
|
1653
|
+
function resolvePlanResolution(ctx) {
|
|
1654
|
+
const explicit = !!ctx.adapters.planResolver;
|
|
1655
|
+
const viaSubscriptions = Boolean(ctx.options.tenantBilling && ctx.adapters.subscriptionRepository);
|
|
1656
|
+
const fallback = !!ctx.options.defaultPlanId;
|
|
1657
|
+
return {
|
|
1658
|
+
explicit,
|
|
1659
|
+
viaSubscriptions,
|
|
1660
|
+
fallback,
|
|
1661
|
+
available: explicit || viaSubscriptions || fallback
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
__name(resolvePlanResolution, "resolvePlanResolution");
|
|
1665
|
+
function composeEnforcementRuntime(ctx, resolution) {
|
|
1666
|
+
if (!resolution.available) return {
|
|
1667
|
+
providers: [],
|
|
1668
|
+
exports: []
|
|
1669
|
+
};
|
|
1670
|
+
const { options, adapters, shared } = ctx;
|
|
1671
|
+
const providers = [];
|
|
1672
|
+
if (resolution.viaSubscriptions && !resolution.explicit) {
|
|
1673
|
+
providers.push(asProvider(PLATFORM_SUBSCRIPTION_REPOSITORY_TOKEN, adapters.subscriptionRepository));
|
|
1674
|
+
}
|
|
1675
|
+
providers.push(
|
|
1676
|
+
planResolverProvider(ctx, resolution),
|
|
1677
|
+
StaticEntitlementService,
|
|
1678
|
+
StaticFeatureGuard,
|
|
1679
|
+
EnforceQuotaInterceptor,
|
|
1680
|
+
...shared.quotaProvidersHostedByTenantBilling ? [] : options.quotaProviders ?? [],
|
|
1681
|
+
{
|
|
1682
|
+
provide: QUOTA_PROVIDERS_TOKEN,
|
|
1683
|
+
useFactory: /* @__PURE__ */ __name((...quotas) => quotas, "useFactory"),
|
|
1684
|
+
inject: options.quotaProviders ?? []
|
|
1685
|
+
},
|
|
1686
|
+
...options.globalFeatureGuard === false ? [] : [
|
|
1687
|
+
{
|
|
1688
|
+
provide: APP_GUARD,
|
|
1689
|
+
useExisting: StaticFeatureGuard
|
|
1690
|
+
}
|
|
1691
|
+
],
|
|
1692
|
+
// The interceptor stays global either way: interceptors run after all
|
|
1693
|
+
// guards, so it sees the authenticated request even when the app binds
|
|
1694
|
+
// its own guard chain.
|
|
1695
|
+
{
|
|
1696
|
+
provide: APP_INTERCEPTOR,
|
|
1697
|
+
useExisting: EnforceQuotaInterceptor
|
|
1698
|
+
}
|
|
1699
|
+
);
|
|
1700
|
+
return {
|
|
1701
|
+
providers,
|
|
1702
|
+
exports: [
|
|
1703
|
+
PLAN_RESOLVER_PORT_TOKEN,
|
|
1704
|
+
StaticEntitlementService,
|
|
1705
|
+
StaticFeatureGuard,
|
|
1706
|
+
EnforceQuotaInterceptor,
|
|
1707
|
+
QUOTA_PROVIDERS_TOKEN
|
|
1708
|
+
]
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
__name(composeEnforcementRuntime, "composeEnforcementRuntime");
|
|
1712
|
+
function planResolverProvider(ctx, resolution) {
|
|
1713
|
+
if (resolution.explicit) {
|
|
1714
|
+
return asProvider(PLAN_RESOLVER_PORT_TOKEN, ctx.adapters.planResolver);
|
|
1715
|
+
}
|
|
1716
|
+
if (resolution.viaSubscriptions) {
|
|
1717
|
+
return {
|
|
1718
|
+
provide: PLAN_RESOLVER_PORT_TOKEN,
|
|
1719
|
+
useFactory: /* @__PURE__ */ __name((subscriptions) => new SubscriptionPlanResolver(subscriptions), "useFactory"),
|
|
1720
|
+
inject: [
|
|
1721
|
+
PLATFORM_SUBSCRIPTION_REPOSITORY_TOKEN
|
|
1722
|
+
]
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
return {
|
|
1726
|
+
provide: PLAN_RESOLVER_PORT_TOKEN,
|
|
1727
|
+
useValue: new StaticPlanResolver(ctx.options.defaultPlanId)
|
|
1728
|
+
};
|
|
1729
|
+
}
|
|
1730
|
+
__name(planResolverProvider, "planResolverProvider");
|
|
1731
|
+
|
|
1732
|
+
// src/platform/saasicat.module.ts
|
|
1733
|
+
function _ts_decorate10(decorators, target, key, desc) {
|
|
1734
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1735
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1736
|
+
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;
|
|
1737
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1738
|
+
}
|
|
1739
|
+
__name(_ts_decorate10, "_ts_decorate");
|
|
1740
|
+
var PLATFORM_LOGGER = new Logger("SaaSiCat");
|
|
1741
|
+
function enforcementChainState(state) {
|
|
1742
|
+
return {
|
|
1743
|
+
provide: ENFORCEMENT_CHAIN_STATE_TOKEN,
|
|
1744
|
+
useValue: state
|
|
1745
|
+
};
|
|
1746
|
+
}
|
|
1747
|
+
__name(enforcementChainState, "enforcementChainState");
|
|
1748
|
+
var SaaSiCatModule = class _SaaSiCatModule {
|
|
1749
|
+
static {
|
|
1750
|
+
__name(this, "SaaSiCatModule");
|
|
1751
|
+
}
|
|
1752
|
+
static forRoot(options) {
|
|
1753
|
+
const explicit = options.adapters ?? {};
|
|
1754
|
+
const persistence = options.persistence;
|
|
1755
|
+
const catalogConfig = options.catalog || null;
|
|
1756
|
+
const tenantBillingConfig = options.tenantBilling || null;
|
|
1757
|
+
const subscriptionBundlesConfig = options.subscriptionBundles || null;
|
|
1758
|
+
const adminResourcesConfig = options.adminResources || null;
|
|
1759
|
+
const promoCodesConfig = options.promoCodes || null;
|
|
1760
|
+
const requiresFullEntitlement2 = Boolean(options.entitlement || tenantBillingConfig || subscriptionBundlesConfig);
|
|
1761
|
+
const adapters = {
|
|
1762
|
+
mfa: explicit.mfa ?? persistence?.core.mfa,
|
|
1763
|
+
audit: explicit.audit ?? persistence?.core.audit,
|
|
1764
|
+
rlsBypass: explicit.rlsBypass ?? persistence?.core.rlsBypass,
|
|
1765
|
+
planCatalogReadSink: explicit.planCatalogReadSink ?? persistence?.planCatalogReadSink,
|
|
1766
|
+
planResolver: explicit.planResolver,
|
|
1767
|
+
subscriptionRepository: explicit.subscriptionRepository ?? persistence?.entitlement?.subscriptionRepository,
|
|
1768
|
+
planVersionRepository: explicit.planVersionRepository ?? persistence?.entitlement?.planVersionRepository,
|
|
1769
|
+
transactionRunner: explicit.transactionRunner ?? persistence?.core.transactionRunner
|
|
1770
|
+
};
|
|
1771
|
+
assertConfiguration({
|
|
1772
|
+
options,
|
|
1773
|
+
adapters
|
|
1774
|
+
});
|
|
1775
|
+
const mfaPort = adapters.mfa;
|
|
1776
|
+
const auditPort = adapters.audit;
|
|
1777
|
+
const rlsBypassPort = adapters.rlsBypass;
|
|
1778
|
+
const appInfo = resolveAppInfo(options);
|
|
1779
|
+
const imports = [
|
|
1780
|
+
// Make injected client/auth providers visible to the high-level
|
|
1781
|
+
// module's own quota providers and automatic plan resolver too.
|
|
1782
|
+
...options.imports ?? [],
|
|
1783
|
+
composePlanCatalog(options, adapters.planCatalogReadSink),
|
|
1784
|
+
...composeBaseModules(options, appInfo, {
|
|
1785
|
+
mfaPort,
|
|
1786
|
+
auditPort,
|
|
1787
|
+
rlsBypassPort
|
|
1788
|
+
})
|
|
1789
|
+
];
|
|
1790
|
+
const composition = {
|
|
1791
|
+
options,
|
|
1792
|
+
adapters,
|
|
1793
|
+
persistence,
|
|
1794
|
+
appInfo,
|
|
1795
|
+
requiresFullEntitlement: requiresFullEntitlement2,
|
|
1796
|
+
shared: {
|
|
1797
|
+
quotaProvidersHostedByTenantBilling: false
|
|
1798
|
+
}
|
|
1799
|
+
};
|
|
1800
|
+
imports.push(...composeFeatures(composition));
|
|
1801
|
+
const lightweightProviders = [];
|
|
1802
|
+
const lightweightExports = [];
|
|
1803
|
+
if (options.autoManifest !== false) {
|
|
1804
|
+
const contribution = buildStandardManifestContribution(catalogConfig, adminResourcesConfig, promoCodesConfig);
|
|
1805
|
+
lightweightProviders.push({
|
|
1806
|
+
provide: STANDARD_MANIFEST_REGISTRATION_TOKEN,
|
|
1807
|
+
useFactory: /* @__PURE__ */ __name((manifest) => {
|
|
1808
|
+
manifest.register(contribution);
|
|
1809
|
+
return contribution;
|
|
1810
|
+
}, "useFactory"),
|
|
1811
|
+
inject: [
|
|
1812
|
+
AdminManifestService
|
|
1813
|
+
]
|
|
1814
|
+
});
|
|
1815
|
+
}
|
|
1816
|
+
const resolution = resolvePlanResolution(composition);
|
|
1817
|
+
const runtime = composeEnforcementRuntime(composition, resolution);
|
|
1818
|
+
lightweightProviders.push(...runtime.providers);
|
|
1819
|
+
lightweightExports.push(...runtime.exports);
|
|
1820
|
+
if (!resolution.available && !requiresFullEntitlement2) {
|
|
1821
|
+
PLATFORM_LOGGER.warn("No plan resolver configured \u2014 @RequireFeature and @EnforceQuota are INERT. The platform registered neither StaticFeatureGuard nor EnforceQuotaInterceptor, so every annotated route serves unlicensed traffic and every quota is unlimited. Set one of: adapters.planResolver, defaultPlanId, or tenantBilling.");
|
|
1822
|
+
}
|
|
1823
|
+
if (options.enforcementChainCheck !== false) {
|
|
1824
|
+
lightweightProviders.push(EnforcementChainCheck, enforcementChainState({
|
|
1825
|
+
featureEnforcementActive: resolution.available || requiresFullEntitlement2,
|
|
1826
|
+
quotaEnforcementActive: resolution.available,
|
|
1827
|
+
globalGuardBound: resolution.available && options.globalFeatureGuard !== false,
|
|
1828
|
+
// Whether the OPTION unbound it, as opposed to this simply
|
|
1829
|
+
// being a path where the platform binds none. The message
|
|
1830
|
+
// names the true cause, and an application that never set
|
|
1831
|
+
// the option is not told to look for it.
|
|
1832
|
+
globalGuardOptedOut: options.globalFeatureGuard === false
|
|
1833
|
+
}));
|
|
1834
|
+
}
|
|
1835
|
+
if (options.enforcementChainCheck !== false) imports.push(NestDiscoveryModule);
|
|
1836
|
+
const tenantManifest = composeTenantManifest(composition);
|
|
1837
|
+
lightweightProviders.push(...tenantManifest.providers);
|
|
1838
|
+
lightweightExports.push(...tenantManifest.exports);
|
|
1839
|
+
return {
|
|
1840
|
+
module: _SaaSiCatModule,
|
|
1841
|
+
imports,
|
|
1842
|
+
providers: lightweightProviders,
|
|
1843
|
+
controllers: tenantManifest.controllers,
|
|
1844
|
+
exports: composeModuleExports(composition, lightweightExports),
|
|
1845
|
+
global: true
|
|
1846
|
+
};
|
|
1847
|
+
}
|
|
1848
|
+
};
|
|
1849
|
+
SaaSiCatModule = _ts_decorate10([
|
|
1850
|
+
Module2({})
|
|
1851
|
+
], SaaSiCatModule);
|
|
1852
|
+
|
|
1853
|
+
export {
|
|
1854
|
+
SETUP_PROVISIONING_PORT_TOKEN,
|
|
1855
|
+
SETUP_CONFIG_TOKEN,
|
|
1856
|
+
SetupService,
|
|
1857
|
+
SetupController,
|
|
1858
|
+
SetupModule,
|
|
1859
|
+
ENFORCEMENT_CHAIN_STATE_TOKEN,
|
|
1860
|
+
EnforcementChainCheck,
|
|
1861
|
+
headingSlug,
|
|
1862
|
+
docsUrlFor,
|
|
1863
|
+
PLATFORM_RULES,
|
|
1864
|
+
findViolations,
|
|
1865
|
+
SaaSiCatConfigurationError,
|
|
1866
|
+
assertConfiguration,
|
|
1867
|
+
QuotaProvidersUsageSnapshot,
|
|
1868
|
+
FEATURE_COMPOSERS,
|
|
1869
|
+
composeFeatures,
|
|
1870
|
+
MOUNTED_BUT_NOT_EXPORTED,
|
|
1871
|
+
composeModuleExports,
|
|
1872
|
+
PLAN_RESOLVER_PORT_TOKEN,
|
|
1873
|
+
StaticPlanResolver,
|
|
1874
|
+
StaticEntitlementService,
|
|
1875
|
+
TenantManifestService,
|
|
1876
|
+
buildTenantManifestController,
|
|
1877
|
+
QUOTA_PROVIDERS_TOKEN,
|
|
1878
|
+
EnforceQuotaInterceptor,
|
|
1879
|
+
STATIC_FEATURE_GUARD_CONFIG_TOKEN,
|
|
1880
|
+
StaticFeatureGuard,
|
|
1881
|
+
SubscriptionPlanResolver,
|
|
1882
|
+
SaaSiCatModule
|
|
1883
|
+
};
|