@saasicat/nest 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +53 -0
- package/dist/admin/index.cjs +1283 -0
- package/dist/admin/index.d.cts +225 -0
- package/dist/admin/index.d.ts +225 -0
- package/dist/admin/index.js +58 -0
- package/dist/admin-audit.service-9IqXMlZm.d.cts +30 -0
- package/dist/admin-audit.service-9IqXMlZm.d.ts +30 -0
- package/dist/admin-manifest.config-DyrQNT7M.d.cts +10 -0
- package/dist/admin-manifest.config-DyrQNT7M.d.ts +10 -0
- package/dist/aggregation-CJ3qQf92.d.cts +288 -0
- package/dist/aggregation-Dvz9e8X2.d.ts +288 -0
- package/dist/billing/index.cjs +5205 -0
- package/dist/billing/index.d.cts +1326 -0
- package/dist/billing/index.d.ts +1326 -0
- package/dist/billing/index.js +197 -0
- package/dist/catalog/index.cjs +6449 -0
- package/dist/catalog/index.d.cts +1073 -0
- package/dist/catalog/index.d.ts +1073 -0
- package/dist/catalog/index.js +158 -0
- package/dist/checkout-offer/index.cjs +807 -0
- package/dist/checkout-offer/index.d.cts +120 -0
- package/dist/checkout-offer/index.d.ts +120 -0
- package/dist/checkout-offer/index.js +20 -0
- package/dist/chunk-2FR6ZL7R.js +0 -0
- package/dist/chunk-2PNX2QL2.js +347 -0
- package/dist/chunk-2VNZYRDV.js +326 -0
- package/dist/chunk-7MLGHWKO.js +12 -0
- package/dist/chunk-ABB2CRAS.js +31 -0
- package/dist/chunk-AJ3EPELS.js +157 -0
- package/dist/chunk-E56W4U2P.js +90 -0
- package/dist/chunk-HRU3CP57.js +71 -0
- package/dist/chunk-L4SUWH5B.js +14 -0
- package/dist/chunk-M6CZQYPY.js +314 -0
- package/dist/chunk-MDIZUVIK.js +87 -0
- package/dist/chunk-MV6LW5UB.js +217 -0
- package/dist/chunk-NYLON2VC.js +6 -0
- package/dist/chunk-P6MYZMXQ.js +951 -0
- package/dist/chunk-Q53N43LQ.js +13 -0
- package/dist/chunk-QJVPRD3R.js +573 -0
- package/dist/chunk-RIGLQ4BV.js +3367 -0
- package/dist/chunk-SABTXESR.js +24 -0
- package/dist/chunk-SHUYVCID.js +6 -0
- package/dist/chunk-SYEVGPFC.js +686 -0
- package/dist/chunk-V7HUZ6UQ.js +5923 -0
- package/dist/chunk-WPUVJRIN.js +29 -0
- package/dist/chunk-XCZ3TTFK.js +22 -0
- package/dist/chunk-XSGU2Y2O.js +1611 -0
- package/dist/chunk-YNEW3L63.js +190 -0
- package/dist/chunk-YXGHPILG.js +614 -0
- package/dist/chunk-ZL4LLE2Z.js +465 -0
- package/dist/di-CcNeq9v-.d.cts +16 -0
- package/dist/di-CcNeq9v-.d.ts +16 -0
- package/dist/discovery/index.cjs +588 -0
- package/dist/discovery/index.d.cts +299 -0
- package/dist/discovery/index.d.ts +299 -0
- package/dist/discovery/index.js +49 -0
- package/dist/discovery.scanner-CUYLKlYT.d.cts +59 -0
- package/dist/discovery.scanner-CUYLKlYT.d.ts +59 -0
- package/dist/entitlement/index.cjs +805 -0
- package/dist/entitlement/index.d.cts +60 -0
- package/dist/entitlement/index.d.ts +60 -0
- package/dist/entitlement/index.js +57 -0
- package/dist/index.cjs +15918 -0
- package/dist/index.d.cts +85 -0
- package/dist/index.d.ts +85 -0
- package/dist/index.js +956 -0
- package/dist/plan-resolution-CFCoUkrE.d.cts +41 -0
- package/dist/plan-resolution-CFCoUkrE.d.ts +41 -0
- package/dist/platform/index.cjs +2772 -0
- package/dist/platform/index.d.cts +118 -0
- package/dist/platform/index.d.ts +118 -0
- package/dist/platform/index.js +37 -0
- package/dist/promo/index.cjs +995 -0
- package/dist/promo/index.d.cts +121 -0
- package/dist/promo/index.d.ts +121 -0
- package/dist/promo/index.js +58 -0
- package/dist/registration/index.cjs +1687 -0
- package/dist/registration/index.d.cts +444 -0
- package/dist/registration/index.d.ts +444 -0
- package/dist/registration/index.js +82 -0
- package/dist/saas-platform.module-CgZ2omgg.d.cts +180 -0
- package/dist/saas-platform.module-DT9TnCzk.d.ts +180 -0
- package/dist/service-DX8KbGXl.d.cts +109 -0
- package/dist/service-DX8KbGXl.d.ts +109 -0
- package/dist/subscription-contract/index.cjs +471 -0
- package/dist/subscription-contract/index.d.cts +17 -0
- package/dist/subscription-contract/index.d.ts +17 -0
- package/dist/subscription-contract/index.js +21 -0
- package/dist/subscription-contract.service--cm47ZJJ.d.cts +39 -0
- package/dist/subscription-contract.service--cm47ZJJ.d.ts +39 -0
- package/dist/testing/index.cjs +3942 -0
- package/dist/testing/index.d.cts +279 -0
- package/dist/testing/index.d.ts +279 -0
- package/dist/testing/index.js +1195 -0
- package/package.json +185 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// src/discovery/tokens.ts
|
|
2
|
+
var IMPLEMENTS_CAPABILITY_KEY = "discovery:implements-capability";
|
|
3
|
+
var REQUIRES_CAPABILITY_KEY = "discovery:requires-capability";
|
|
4
|
+
var DEFINES_QUOTA_KEY = "discovery:defines-quota";
|
|
5
|
+
var ENFORCE_QUOTA_KEY = "discovery:enforce-quota";
|
|
6
|
+
var DISCOVERY_SNAPSHOT_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/DiscoverySnapshot");
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
IMPLEMENTS_CAPABILITY_KEY,
|
|
10
|
+
REQUIRES_CAPABILITY_KEY,
|
|
11
|
+
DEFINES_QUOTA_KEY,
|
|
12
|
+
ENFORCE_QUOTA_KEY,
|
|
13
|
+
DISCOVERY_SNAPSHOT_TOKEN
|
|
14
|
+
};
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFINES_QUOTA_KEY,
|
|
3
|
+
ENFORCE_QUOTA_KEY,
|
|
4
|
+
IMPLEMENTS_CAPABILITY_KEY
|
|
5
|
+
} from "./chunk-L4SUWH5B.js";
|
|
6
|
+
import {
|
|
7
|
+
__name
|
|
8
|
+
} from "./chunk-SHUYVCID.js";
|
|
9
|
+
|
|
10
|
+
// src/discovery/discovery.scanner.ts
|
|
11
|
+
import { createHash } from "crypto";
|
|
12
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
13
|
+
import { dirname } from "path";
|
|
14
|
+
import { Inject, Injectable, Logger, Optional } from "@nestjs/common";
|
|
15
|
+
import { DiscoveryService, MetadataScanner, Reflector } from "@nestjs/core";
|
|
16
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
17
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
18
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
19
|
+
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;
|
|
20
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
21
|
+
}
|
|
22
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
23
|
+
function _ts_metadata(k, v) {
|
|
24
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
25
|
+
}
|
|
26
|
+
__name(_ts_metadata, "_ts_metadata");
|
|
27
|
+
function _ts_param(paramIndex, decorator) {
|
|
28
|
+
return function(target, key) {
|
|
29
|
+
decorator(target, key, paramIndex);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
__name(_ts_param, "_ts_param");
|
|
33
|
+
var DEFAULT_APP_INFO = {
|
|
34
|
+
key: "unknown",
|
|
35
|
+
version: "0.0.0"
|
|
36
|
+
};
|
|
37
|
+
var DISCOVERY_APP_INFO_TOKEN = /* @__PURE__ */ Symbol("DISCOVERY_APP_INFO");
|
|
38
|
+
var DISCOVERY_SNAPSHOT_PATH_TOKEN = /* @__PURE__ */ Symbol("DISCOVERY_SNAPSHOT_PATH");
|
|
39
|
+
var DiscoveryScanner = class _DiscoveryScanner {
|
|
40
|
+
static {
|
|
41
|
+
__name(this, "DiscoveryScanner");
|
|
42
|
+
}
|
|
43
|
+
discoveryService;
|
|
44
|
+
metadataScanner;
|
|
45
|
+
reflector;
|
|
46
|
+
appInfo;
|
|
47
|
+
snapshotPath;
|
|
48
|
+
logger = new Logger(_DiscoveryScanner.name);
|
|
49
|
+
snapshot = null;
|
|
50
|
+
// Explicit @Inject instead of type reflection: in the platform package
|
|
51
|
+
// build tsup/esbuild do not emit `design:paramtypes` metadata, so Nest
|
|
52
|
+
// cannot otherwise resolve the DI targets at the constructor
|
|
53
|
+
// (UndefinedDependencyException at the consumer's boot). Pattern
|
|
54
|
+
// analogous to admin/admin-manifest.module.ts.
|
|
55
|
+
constructor(discoveryService, metadataScanner, reflector, appInfo = DEFAULT_APP_INFO, snapshotPath = null) {
|
|
56
|
+
this.discoveryService = discoveryService;
|
|
57
|
+
this.metadataScanner = metadataScanner;
|
|
58
|
+
this.reflector = reflector;
|
|
59
|
+
this.appInfo = appInfo;
|
|
60
|
+
this.snapshotPath = snapshotPath;
|
|
61
|
+
}
|
|
62
|
+
onApplicationBootstrap() {
|
|
63
|
+
this.snapshot = this.buildSnapshot();
|
|
64
|
+
this.logger.log(`Discovery snapshot built: ${this.snapshot.capabilities.length} capabilities, ${this.snapshot.features.length} features, ${this.snapshot.quotas.length} quotas (hash=${this.snapshot.hash.slice(0, 12)}\u2026)`);
|
|
65
|
+
if (this.snapshotPath) {
|
|
66
|
+
this.persistSnapshot(this.snapshotPath, this.snapshot);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
persistSnapshot(path, snapshot) {
|
|
70
|
+
try {
|
|
71
|
+
mkdirSync(dirname(path), {
|
|
72
|
+
recursive: true
|
|
73
|
+
});
|
|
74
|
+
writeFileSync(path, JSON.stringify(snapshot, null, 2) + "\n", "utf-8");
|
|
75
|
+
this.logger.log(`Discovery snapshot persisted to ${path}`);
|
|
76
|
+
} catch (err) {
|
|
77
|
+
this.logger.warn(`Discovery snapshot konnte nicht nach ${path} geschrieben werden: ${err.message}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Returns the boot-time snapshot. Before `onApplicationBootstrap` (e.g. in
|
|
82
|
+
* tests) the snapshot is built lazily.
|
|
83
|
+
*/
|
|
84
|
+
getSnapshot() {
|
|
85
|
+
if (!this.snapshot) {
|
|
86
|
+
this.snapshot = this.buildSnapshot();
|
|
87
|
+
}
|
|
88
|
+
return this.snapshot;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Forces a re-scan (SuperAdmin "re-run discovery" or tests with
|
|
92
|
+
* dynamically registered providers). `scannedAt` is set anew and the
|
|
93
|
+
* snapshot — as at boot — is persisted to disk so that the last scan
|
|
94
|
+
* actually performed is preserved.
|
|
95
|
+
*/
|
|
96
|
+
rebuildSnapshot() {
|
|
97
|
+
this.snapshot = this.buildSnapshot();
|
|
98
|
+
if (this.snapshotPath) {
|
|
99
|
+
this.persistSnapshot(this.snapshotPath, this.snapshot);
|
|
100
|
+
}
|
|
101
|
+
return this.snapshot;
|
|
102
|
+
}
|
|
103
|
+
buildSnapshot() {
|
|
104
|
+
const capabilitiesByKey = /* @__PURE__ */ new Map();
|
|
105
|
+
const quotasByKey = /* @__PURE__ */ new Map();
|
|
106
|
+
const enforceRefs = [];
|
|
107
|
+
const wrappers = [
|
|
108
|
+
...this.discoveryService.getProviders(),
|
|
109
|
+
...this.discoveryService.getControllers()
|
|
110
|
+
];
|
|
111
|
+
for (const wrapper of wrappers) {
|
|
112
|
+
const instance = wrapper.instance;
|
|
113
|
+
if (!instance || typeof instance !== "object") continue;
|
|
114
|
+
const ctor = instance.constructor;
|
|
115
|
+
if (!ctor) continue;
|
|
116
|
+
const quotaOpts = this.reflector.get(DEFINES_QUOTA_KEY, ctor);
|
|
117
|
+
if (quotaOpts) {
|
|
118
|
+
this.registerQuota(quotasByKey, quotaOpts, ctor.name);
|
|
119
|
+
}
|
|
120
|
+
const prototype = Object.getPrototypeOf(instance);
|
|
121
|
+
const methodNames = this.metadataScanner.getAllMethodNames(prototype);
|
|
122
|
+
for (const methodName of methodNames) {
|
|
123
|
+
const methodRef = instance[methodName];
|
|
124
|
+
if (typeof methodRef !== "function") continue;
|
|
125
|
+
const capMeta = this.reflector.get(IMPLEMENTS_CAPABILITY_KEY, methodRef);
|
|
126
|
+
if (capMeta) {
|
|
127
|
+
this.registerCapability(capabilitiesByKey, capMeta, `${ctor.name}.${methodName}`);
|
|
128
|
+
}
|
|
129
|
+
const enforceMeta = this.reflector.get(ENFORCE_QUOTA_KEY, methodRef);
|
|
130
|
+
if (enforceMeta && capMeta) {
|
|
131
|
+
enforceRefs.push({
|
|
132
|
+
capabilityKey: capMeta.capabilityKey,
|
|
133
|
+
quotaKey: enforceMeta.quotaKey
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const capabilities = sortByKey([
|
|
139
|
+
...capabilitiesByKey.values()
|
|
140
|
+
], (c) => c.capabilityKey);
|
|
141
|
+
const features = aggregateFeatures(capabilities);
|
|
142
|
+
const quotas = sortByKey(decorateQuotasWithEnforcers([
|
|
143
|
+
...quotasByKey.values()
|
|
144
|
+
], enforceRefs), (q) => q.quotaKey);
|
|
145
|
+
const snapshot = {
|
|
146
|
+
schemaVersion: 1,
|
|
147
|
+
scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
148
|
+
app: {
|
|
149
|
+
...this.appInfo
|
|
150
|
+
},
|
|
151
|
+
capabilities,
|
|
152
|
+
features,
|
|
153
|
+
quotas,
|
|
154
|
+
hash: ""
|
|
155
|
+
};
|
|
156
|
+
snapshot.hash = computeSnapshotHash(snapshot);
|
|
157
|
+
return snapshot;
|
|
158
|
+
}
|
|
159
|
+
registerCapability(target, meta, declaredAt) {
|
|
160
|
+
const existing = target.get(meta.capabilityKey);
|
|
161
|
+
if (existing) {
|
|
162
|
+
this.logger.warn(`Capability '${meta.capabilityKey}' wird an mehreren Stellen deklariert (${existing.declaredAt} und ${declaredAt}); erste Deklaration gewinnt`);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
target.set(meta.capabilityKey, {
|
|
166
|
+
capabilityKey: meta.capabilityKey,
|
|
167
|
+
label: meta.label ?? null,
|
|
168
|
+
feature: meta.feature ?? null,
|
|
169
|
+
status: meta.status ?? "active",
|
|
170
|
+
kind: meta.kind ?? "endpoint",
|
|
171
|
+
owner: meta.owner ?? null,
|
|
172
|
+
replacementKey: meta.replacementKey ?? null,
|
|
173
|
+
removalPlannedAt: meta.removalPlannedAt ?? null,
|
|
174
|
+
reason: meta.reason ?? null,
|
|
175
|
+
requires: normalizeKeyList(meta.requires),
|
|
176
|
+
replaces: normalizeKeyList(meta.replaces),
|
|
177
|
+
declaredAt
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
registerQuota(target, opts, declaredAt) {
|
|
181
|
+
const existing = target.get(opts.key);
|
|
182
|
+
if (existing) {
|
|
183
|
+
this.logger.warn(`Quota '${opts.key}' wird an mehreren Stellen definiert (${existing.declaredAt} und ${declaredAt}); erste Definition gewinnt`);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
target.set(opts.key, {
|
|
187
|
+
quotaKey: opts.key,
|
|
188
|
+
label: opts.label,
|
|
189
|
+
unit: opts.unit,
|
|
190
|
+
policy: opts.policy,
|
|
191
|
+
feature: opts.feature ?? null,
|
|
192
|
+
replaces: normalizeKeyList(opts.replaces),
|
|
193
|
+
declaredAt,
|
|
194
|
+
enforcedBy: []
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
DiscoveryScanner = _ts_decorate([
|
|
199
|
+
Injectable(),
|
|
200
|
+
_ts_param(0, Inject(DiscoveryService)),
|
|
201
|
+
_ts_param(1, Inject(MetadataScanner)),
|
|
202
|
+
_ts_param(2, Inject(Reflector)),
|
|
203
|
+
_ts_param(3, Optional()),
|
|
204
|
+
_ts_param(3, Inject(DISCOVERY_APP_INFO_TOKEN)),
|
|
205
|
+
_ts_param(4, Optional()),
|
|
206
|
+
_ts_param(4, Inject(DISCOVERY_SNAPSHOT_PATH_TOKEN)),
|
|
207
|
+
_ts_metadata("design:type", Function),
|
|
208
|
+
_ts_metadata("design:paramtypes", [
|
|
209
|
+
typeof DiscoveryService === "undefined" ? Object : DiscoveryService,
|
|
210
|
+
typeof MetadataScanner === "undefined" ? Object : MetadataScanner,
|
|
211
|
+
typeof Reflector === "undefined" ? Object : Reflector,
|
|
212
|
+
typeof DiscoveryAppInfo === "undefined" ? Object : DiscoveryAppInfo,
|
|
213
|
+
Object
|
|
214
|
+
])
|
|
215
|
+
], DiscoveryScanner);
|
|
216
|
+
function aggregateFeatures(capabilities) {
|
|
217
|
+
const byFeature = /* @__PURE__ */ new Map();
|
|
218
|
+
for (const cap of capabilities) {
|
|
219
|
+
if (!cap.feature) continue;
|
|
220
|
+
const acc = byFeature.get(cap.feature) ?? {
|
|
221
|
+
capabilityKeys: [],
|
|
222
|
+
requires: /* @__PURE__ */ new Set(),
|
|
223
|
+
replaces: /* @__PURE__ */ new Set()
|
|
224
|
+
};
|
|
225
|
+
acc.capabilityKeys.push(cap.capabilityKey);
|
|
226
|
+
for (const key of cap.requires ?? []) acc.requires.add(key);
|
|
227
|
+
for (const key of cap.replaces ?? []) acc.replaces.add(key);
|
|
228
|
+
byFeature.set(cap.feature, acc);
|
|
229
|
+
}
|
|
230
|
+
return sortByKey([
|
|
231
|
+
...byFeature.entries()
|
|
232
|
+
].map(([featureKey, acc]) => {
|
|
233
|
+
acc.requires.delete(featureKey);
|
|
234
|
+
acc.replaces.delete(featureKey);
|
|
235
|
+
return {
|
|
236
|
+
featureKey,
|
|
237
|
+
capabilityKeys: [
|
|
238
|
+
...acc.capabilityKeys
|
|
239
|
+
].sort((a, b) => a.localeCompare(b)),
|
|
240
|
+
requires: setToSortedListOrNull(acc.requires),
|
|
241
|
+
replaces: setToSortedListOrNull(acc.replaces)
|
|
242
|
+
};
|
|
243
|
+
}), (f) => f.featureKey);
|
|
244
|
+
}
|
|
245
|
+
__name(aggregateFeatures, "aggregateFeatures");
|
|
246
|
+
function normalizeKeyList(keys) {
|
|
247
|
+
if (!keys || keys.length === 0) return null;
|
|
248
|
+
return [
|
|
249
|
+
...new Set(keys)
|
|
250
|
+
].sort((a, b) => a.localeCompare(b));
|
|
251
|
+
}
|
|
252
|
+
__name(normalizeKeyList, "normalizeKeyList");
|
|
253
|
+
function setToSortedListOrNull(keys) {
|
|
254
|
+
if (keys.size === 0) return null;
|
|
255
|
+
return [
|
|
256
|
+
...keys
|
|
257
|
+
].sort((a, b) => a.localeCompare(b));
|
|
258
|
+
}
|
|
259
|
+
__name(setToSortedListOrNull, "setToSortedListOrNull");
|
|
260
|
+
function decorateQuotasWithEnforcers(quotas, enforceRefs) {
|
|
261
|
+
const enforcedByQuota = /* @__PURE__ */ new Map();
|
|
262
|
+
for (const ref of enforceRefs) {
|
|
263
|
+
const set = enforcedByQuota.get(ref.quotaKey) ?? /* @__PURE__ */ new Set();
|
|
264
|
+
set.add(ref.capabilityKey);
|
|
265
|
+
enforcedByQuota.set(ref.quotaKey, set);
|
|
266
|
+
}
|
|
267
|
+
return quotas.map((q) => ({
|
|
268
|
+
...q,
|
|
269
|
+
enforcedBy: [
|
|
270
|
+
...enforcedByQuota.get(q.quotaKey) ?? /* @__PURE__ */ new Set()
|
|
271
|
+
].sort((a, b) => a.localeCompare(b))
|
|
272
|
+
}));
|
|
273
|
+
}
|
|
274
|
+
__name(decorateQuotasWithEnforcers, "decorateQuotasWithEnforcers");
|
|
275
|
+
function sortByKey(items, keyFn) {
|
|
276
|
+
return [
|
|
277
|
+
...items
|
|
278
|
+
].sort((a, b) => keyFn(a).localeCompare(keyFn(b)));
|
|
279
|
+
}
|
|
280
|
+
__name(sortByKey, "sortByKey");
|
|
281
|
+
function computeSnapshotHash(snapshot) {
|
|
282
|
+
const stableInput = {
|
|
283
|
+
schemaVersion: snapshot.schemaVersion,
|
|
284
|
+
appKey: snapshot.app.key,
|
|
285
|
+
capabilities: snapshot.capabilities.map((c) => ({
|
|
286
|
+
...c
|
|
287
|
+
})),
|
|
288
|
+
features: snapshot.features,
|
|
289
|
+
quotas: snapshot.quotas
|
|
290
|
+
};
|
|
291
|
+
const json = canonicalStringify(stableInput);
|
|
292
|
+
return `sha256-${createHash("sha256").update(json).digest("hex")}`;
|
|
293
|
+
}
|
|
294
|
+
__name(computeSnapshotHash, "computeSnapshotHash");
|
|
295
|
+
function canonicalStringify(value) {
|
|
296
|
+
return JSON.stringify(value, (_key, val) => {
|
|
297
|
+
if (val && typeof val === "object" && !Array.isArray(val)) {
|
|
298
|
+
const sorted = {};
|
|
299
|
+
for (const k of Object.keys(val).sort()) {
|
|
300
|
+
sorted[k] = val[k];
|
|
301
|
+
}
|
|
302
|
+
return sorted;
|
|
303
|
+
}
|
|
304
|
+
return val;
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
__name(canonicalStringify, "canonicalStringify");
|
|
308
|
+
|
|
309
|
+
export {
|
|
310
|
+
DISCOVERY_APP_INFO_TOKEN,
|
|
311
|
+
DISCOVERY_SNAPSHOT_PATH_TOKEN,
|
|
312
|
+
DiscoveryScanner,
|
|
313
|
+
computeSnapshotHash
|
|
314
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__name
|
|
3
|
+
} from "./chunk-SHUYVCID.js";
|
|
4
|
+
|
|
5
|
+
// src/checkout-offer/discount-line-items.ts
|
|
6
|
+
function appendImplicitDiscountLineItem(input) {
|
|
7
|
+
const lineItems = input.lineItems.map((item) => cloneCheckoutOfferLineItem(item));
|
|
8
|
+
const discountNet = roundMoney(input.priceBreakdown.regularNet - input.priceBreakdown.effectiveNet);
|
|
9
|
+
if (lineItems.some((item) => item.kind === "discount" && !isGeneratedDiscount(item))) {
|
|
10
|
+
return lineItems;
|
|
11
|
+
}
|
|
12
|
+
const lineItemsWithoutGeneratedDiscount = lineItems.filter((item) => !isGeneratedDiscount(item));
|
|
13
|
+
if (discountNet <= 0) {
|
|
14
|
+
return lineItemsWithoutGeneratedDiscount;
|
|
15
|
+
}
|
|
16
|
+
return [
|
|
17
|
+
...lineItemsWithoutGeneratedDiscount,
|
|
18
|
+
createDiscountLineItem(input, discountNet)
|
|
19
|
+
];
|
|
20
|
+
}
|
|
21
|
+
__name(appendImplicitDiscountLineItem, "appendImplicitDiscountLineItem");
|
|
22
|
+
function cloneCheckoutOfferLineItem(item) {
|
|
23
|
+
return {
|
|
24
|
+
...item,
|
|
25
|
+
featuresSnapshot: [
|
|
26
|
+
...item.featuresSnapshot ?? []
|
|
27
|
+
],
|
|
28
|
+
quotaEffectsSnapshot: {
|
|
29
|
+
...item.quotaEffectsSnapshot ?? {}
|
|
30
|
+
},
|
|
31
|
+
metadata: cloneRecord(item.metadata)
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
__name(cloneCheckoutOfferLineItem, "cloneCheckoutOfferLineItem");
|
|
35
|
+
function createDiscountLineItem(input, discountNet) {
|
|
36
|
+
const promoCode = input.promoCodeSnapshot ?? null;
|
|
37
|
+
const firstPromotion = input.promotionSnapshots?.[0] ?? null;
|
|
38
|
+
const discountGross = roundMoney(discountNet * (1 + input.priceBreakdown.vatRate));
|
|
39
|
+
return {
|
|
40
|
+
kind: "discount",
|
|
41
|
+
sourceKey: promoCode?.code ?? firstPromotion?.id ?? "price-discount",
|
|
42
|
+
sourceVersionId: null,
|
|
43
|
+
titleSnapshot: promoCode?.label ?? firstPromotion?.label ?? "Rabatt",
|
|
44
|
+
descriptionSnapshot: promoCode ? `Promo-Code ${promoCode.code}` : null,
|
|
45
|
+
quantity: 1,
|
|
46
|
+
unit: null,
|
|
47
|
+
priceNet: -discountNet,
|
|
48
|
+
priceGross: -discountGross,
|
|
49
|
+
billingCycle: input.billingCycle,
|
|
50
|
+
featuresSnapshot: [],
|
|
51
|
+
quotaEffectsSnapshot: {},
|
|
52
|
+
metadata: {
|
|
53
|
+
generated: true,
|
|
54
|
+
source: promoCode ? "promo_code" : firstPromotion ? "promotion" : "price_breakdown",
|
|
55
|
+
discountNet,
|
|
56
|
+
discountGross,
|
|
57
|
+
promotionSnapshots: cloneArray(input.promotionSnapshots ?? []),
|
|
58
|
+
promoCodeSnapshot: cloneJsonValue(promoCode)
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
__name(createDiscountLineItem, "createDiscountLineItem");
|
|
63
|
+
function roundMoney(value) {
|
|
64
|
+
return Math.round((value + Number.EPSILON) * 100) / 100;
|
|
65
|
+
}
|
|
66
|
+
__name(roundMoney, "roundMoney");
|
|
67
|
+
function isGeneratedDiscount(item) {
|
|
68
|
+
return item.kind === "discount" && item.metadata?.generated === true;
|
|
69
|
+
}
|
|
70
|
+
__name(isGeneratedDiscount, "isGeneratedDiscount");
|
|
71
|
+
function cloneRecord(value) {
|
|
72
|
+
if (!value) return null;
|
|
73
|
+
return cloneJsonValue(value);
|
|
74
|
+
}
|
|
75
|
+
__name(cloneRecord, "cloneRecord");
|
|
76
|
+
function cloneArray(value) {
|
|
77
|
+
return cloneJsonValue(value);
|
|
78
|
+
}
|
|
79
|
+
__name(cloneArray, "cloneArray");
|
|
80
|
+
function cloneJsonValue(value) {
|
|
81
|
+
return value == null ? value : JSON.parse(JSON.stringify(value));
|
|
82
|
+
}
|
|
83
|
+
__name(cloneJsonValue, "cloneJsonValue");
|
|
84
|
+
|
|
85
|
+
export {
|
|
86
|
+
appendImplicitDiscountLineItem
|
|
87
|
+
};
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import {
|
|
2
|
+
asProvider
|
|
3
|
+
} from "./chunk-SABTXESR.js";
|
|
4
|
+
import {
|
|
5
|
+
__name
|
|
6
|
+
} from "./chunk-SHUYVCID.js";
|
|
7
|
+
|
|
8
|
+
// src/promo/math.ts
|
|
9
|
+
function round2(n) {
|
|
10
|
+
return Math.round(n * 100) / 100;
|
|
11
|
+
}
|
|
12
|
+
__name(round2, "round2");
|
|
13
|
+
function grossFromNet(net, vatRate) {
|
|
14
|
+
return round2(net * (1 + vatRate / 100));
|
|
15
|
+
}
|
|
16
|
+
__name(grossFromNet, "grossFromNet");
|
|
17
|
+
function computeIncludedVat(grossAmount, vatRate) {
|
|
18
|
+
return round2(grossAmount * vatRate / (100 + vatRate));
|
|
19
|
+
}
|
|
20
|
+
__name(computeIncludedVat, "computeIncludedVat");
|
|
21
|
+
|
|
22
|
+
// src/billing/plan-catalog-from-snapshot.ts
|
|
23
|
+
function buildPlanCatalogFromSnapshot(settings, snapshot) {
|
|
24
|
+
const liveByPlanKey = new Map(snapshot.livePlanVersions.map((v) => [
|
|
25
|
+
v.planId,
|
|
26
|
+
v
|
|
27
|
+
]));
|
|
28
|
+
const plans = snapshot.plans.filter((p) => p.deletedAt === null).sort((a, b) => a.sortOrder - b.sortOrder).map((stem) => {
|
|
29
|
+
const live = liveByPlanKey.get(stem.planKey);
|
|
30
|
+
if (!live) {
|
|
31
|
+
return {
|
|
32
|
+
id: stem.planKey,
|
|
33
|
+
name: stem.label,
|
|
34
|
+
tagline: stem.description ?? void 0,
|
|
35
|
+
marketed: false,
|
|
36
|
+
monthlyNet: null,
|
|
37
|
+
yearlyNet: null,
|
|
38
|
+
features: [],
|
|
39
|
+
quotas: {}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
id: stem.planKey,
|
|
44
|
+
name: stem.label,
|
|
45
|
+
tagline: stem.description ?? void 0,
|
|
46
|
+
marketed: live.marketed,
|
|
47
|
+
monthlyNet: parseFloat(live.monthlyNet),
|
|
48
|
+
yearlyNet: parseFloat(live.yearlyNet),
|
|
49
|
+
features: live.features,
|
|
50
|
+
quotas: live.quotas ?? {}
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
const features = snapshot.featureEntries.filter((f) => f.deletedAt === null).sort((a, b) => a.sortOrder - b.sortOrder).map((row) => ({
|
|
54
|
+
key: row.featureKey,
|
|
55
|
+
label: row.label,
|
|
56
|
+
icon: row.icon ?? void 0,
|
|
57
|
+
tier: row.tier ?? void 0,
|
|
58
|
+
plannedOnly: row.plannedOnly
|
|
59
|
+
}));
|
|
60
|
+
return {
|
|
61
|
+
schemaVersion: 1,
|
|
62
|
+
projectKey: settings.projectKey,
|
|
63
|
+
...settings.app ? {
|
|
64
|
+
app: settings.app
|
|
65
|
+
} : {},
|
|
66
|
+
currency: settings.currency,
|
|
67
|
+
vatRate: settings.vatRate,
|
|
68
|
+
...settings.marketing ? {
|
|
69
|
+
marketing: settings.marketing
|
|
70
|
+
} : {},
|
|
71
|
+
features,
|
|
72
|
+
plans
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
__name(buildPlanCatalogFromSnapshot, "buildPlanCatalogFromSnapshot");
|
|
76
|
+
|
|
77
|
+
// src/billing/plan-catalog.module.ts
|
|
78
|
+
import { Module } from "@nestjs/common";
|
|
79
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
80
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
81
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
82
|
+
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;
|
|
83
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
84
|
+
}
|
|
85
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
86
|
+
var PLAN_CATALOG_TOKEN = /* @__PURE__ */ Symbol("PLAN_CATALOG");
|
|
87
|
+
var PLAN_CATALOG_READ_SINK_TOKEN = /* @__PURE__ */ Symbol("PLAN_CATALOG_READ_SINK");
|
|
88
|
+
var PlanCatalogModule = class _PlanCatalogModule {
|
|
89
|
+
static {
|
|
90
|
+
__name(this, "PlanCatalogModule");
|
|
91
|
+
}
|
|
92
|
+
static forRoot(options) {
|
|
93
|
+
return {
|
|
94
|
+
module: _PlanCatalogModule,
|
|
95
|
+
global: options.global ?? true,
|
|
96
|
+
imports: options.imports ?? [],
|
|
97
|
+
providers: [
|
|
98
|
+
...options.extraProviders ?? [],
|
|
99
|
+
asProvider(PLAN_CATALOG_READ_SINK_TOKEN, options.sink),
|
|
100
|
+
{
|
|
101
|
+
provide: PLAN_CATALOG_TOKEN,
|
|
102
|
+
useFactory: /* @__PURE__ */ __name(async (sink) => {
|
|
103
|
+
const snapshot = await sink.loadSnapshot(options.projectKey);
|
|
104
|
+
return buildPlanCatalogFromSnapshot({
|
|
105
|
+
projectKey: options.projectKey,
|
|
106
|
+
app: options.app,
|
|
107
|
+
currency: options.currency,
|
|
108
|
+
vatRate: options.vatRate,
|
|
109
|
+
marketing: options.marketing
|
|
110
|
+
}, snapshot);
|
|
111
|
+
}, "useFactory"),
|
|
112
|
+
inject: [
|
|
113
|
+
PLAN_CATALOG_READ_SINK_TOKEN
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
],
|
|
117
|
+
exports: [
|
|
118
|
+
PLAN_CATALOG_TOKEN,
|
|
119
|
+
PLAN_CATALOG_READ_SINK_TOKEN
|
|
120
|
+
]
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Tests / in-memory setup: takes the catalog object directly, without a
|
|
125
|
+
* sink/DB lookup. For unit tests + test bootstraps.
|
|
126
|
+
*/
|
|
127
|
+
static forRootWithCatalog(catalog, opts = {}) {
|
|
128
|
+
return {
|
|
129
|
+
module: _PlanCatalogModule,
|
|
130
|
+
providers: [
|
|
131
|
+
{
|
|
132
|
+
provide: PLAN_CATALOG_TOKEN,
|
|
133
|
+
useValue: catalog
|
|
134
|
+
}
|
|
135
|
+
],
|
|
136
|
+
exports: [
|
|
137
|
+
PLAN_CATALOG_TOKEN
|
|
138
|
+
],
|
|
139
|
+
global: opts.global ?? true
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
PlanCatalogModule = _ts_decorate([
|
|
144
|
+
Module({})
|
|
145
|
+
], PlanCatalogModule);
|
|
146
|
+
|
|
147
|
+
// src/billing/plan-helpers.ts
|
|
148
|
+
function findPlan(catalog, planId) {
|
|
149
|
+
return (catalog.plans ?? []).find((p) => p.id === planId);
|
|
150
|
+
}
|
|
151
|
+
__name(findPlan, "findPlan");
|
|
152
|
+
function getPlanOrThrow(catalog, planId) {
|
|
153
|
+
const plan = findPlan(catalog, planId);
|
|
154
|
+
if (!plan) {
|
|
155
|
+
throw new Error(`Plan "${planId}" nicht im Catalog (${catalog.projectKey}) gefunden`);
|
|
156
|
+
}
|
|
157
|
+
return plan;
|
|
158
|
+
}
|
|
159
|
+
__name(getPlanOrThrow, "getPlanOrThrow");
|
|
160
|
+
function getMarketedPlans(catalog) {
|
|
161
|
+
return (catalog.plans ?? []).filter((p) => p.marketed !== false);
|
|
162
|
+
}
|
|
163
|
+
__name(getMarketedPlans, "getMarketedPlans");
|
|
164
|
+
function getPlanPriceNet(catalog, planId, cycle) {
|
|
165
|
+
const plan = findPlan(catalog, planId);
|
|
166
|
+
if (!plan) return null;
|
|
167
|
+
if (plan.marketed === false) return null;
|
|
168
|
+
const net = cycle === "YEARLY" ? plan.yearlyNet : plan.monthlyNet;
|
|
169
|
+
return net ?? null;
|
|
170
|
+
}
|
|
171
|
+
__name(getPlanPriceNet, "getPlanPriceNet");
|
|
172
|
+
function getPlanPriceGross(catalog, planId, cycle, vatRate) {
|
|
173
|
+
const net = getPlanPriceNet(catalog, planId, cycle);
|
|
174
|
+
if (net === null) return null;
|
|
175
|
+
const rate = vatRate ?? catalog.vatRate;
|
|
176
|
+
return round2(net * (1 + rate / 100));
|
|
177
|
+
}
|
|
178
|
+
__name(getPlanPriceGross, "getPlanPriceGross");
|
|
179
|
+
function isFeatureInPlan(catalog, planId, featureKey) {
|
|
180
|
+
const plan = findPlan(catalog, planId);
|
|
181
|
+
if (!plan) return false;
|
|
182
|
+
return plan.features.includes(featureKey);
|
|
183
|
+
}
|
|
184
|
+
__name(isFeatureInPlan, "isFeatureInPlan");
|
|
185
|
+
function getPlanQuota(catalog, planId, quotaKey) {
|
|
186
|
+
const plan = findPlan(catalog, planId);
|
|
187
|
+
return plan?.quotas[quotaKey];
|
|
188
|
+
}
|
|
189
|
+
__name(getPlanQuota, "getPlanQuota");
|
|
190
|
+
function getActiveFeatureKeys(catalog) {
|
|
191
|
+
return (catalog.features ?? []).filter((f) => !f.plannedOnly).map((f) => f.key);
|
|
192
|
+
}
|
|
193
|
+
__name(getActiveFeatureKeys, "getActiveFeatureKeys");
|
|
194
|
+
function isFeaturePlannedOnly(catalog, featureKey) {
|
|
195
|
+
const def = (catalog.features ?? []).find((f) => f.key === featureKey);
|
|
196
|
+
return def?.plannedOnly === true;
|
|
197
|
+
}
|
|
198
|
+
__name(isFeaturePlannedOnly, "isFeaturePlannedOnly");
|
|
199
|
+
|
|
200
|
+
export {
|
|
201
|
+
round2,
|
|
202
|
+
grossFromNet,
|
|
203
|
+
computeIncludedVat,
|
|
204
|
+
buildPlanCatalogFromSnapshot,
|
|
205
|
+
PLAN_CATALOG_TOKEN,
|
|
206
|
+
PLAN_CATALOG_READ_SINK_TOKEN,
|
|
207
|
+
PlanCatalogModule,
|
|
208
|
+
findPlan,
|
|
209
|
+
getPlanOrThrow,
|
|
210
|
+
getMarketedPlans,
|
|
211
|
+
getPlanPriceNet,
|
|
212
|
+
getPlanPriceGross,
|
|
213
|
+
isFeatureInPlan,
|
|
214
|
+
getPlanQuota,
|
|
215
|
+
getActiveFeatureKeys,
|
|
216
|
+
isFeaturePlannedOnly
|
|
217
|
+
};
|