@saasicat/nest 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +53 -0
  3. package/dist/admin/index.cjs +1283 -0
  4. package/dist/admin/index.d.cts +225 -0
  5. package/dist/admin/index.d.ts +225 -0
  6. package/dist/admin/index.js +58 -0
  7. package/dist/admin-audit.service-9IqXMlZm.d.cts +30 -0
  8. package/dist/admin-audit.service-9IqXMlZm.d.ts +30 -0
  9. package/dist/admin-manifest.config-DyrQNT7M.d.cts +10 -0
  10. package/dist/admin-manifest.config-DyrQNT7M.d.ts +10 -0
  11. package/dist/aggregation-CJ3qQf92.d.cts +288 -0
  12. package/dist/aggregation-Dvz9e8X2.d.ts +288 -0
  13. package/dist/billing/index.cjs +5205 -0
  14. package/dist/billing/index.d.cts +1326 -0
  15. package/dist/billing/index.d.ts +1326 -0
  16. package/dist/billing/index.js +197 -0
  17. package/dist/catalog/index.cjs +6449 -0
  18. package/dist/catalog/index.d.cts +1073 -0
  19. package/dist/catalog/index.d.ts +1073 -0
  20. package/dist/catalog/index.js +158 -0
  21. package/dist/checkout-offer/index.cjs +807 -0
  22. package/dist/checkout-offer/index.d.cts +120 -0
  23. package/dist/checkout-offer/index.d.ts +120 -0
  24. package/dist/checkout-offer/index.js +20 -0
  25. package/dist/chunk-2FR6ZL7R.js +0 -0
  26. package/dist/chunk-2PNX2QL2.js +347 -0
  27. package/dist/chunk-2VNZYRDV.js +326 -0
  28. package/dist/chunk-7MLGHWKO.js +12 -0
  29. package/dist/chunk-ABB2CRAS.js +31 -0
  30. package/dist/chunk-AJ3EPELS.js +157 -0
  31. package/dist/chunk-E56W4U2P.js +90 -0
  32. package/dist/chunk-HRU3CP57.js +71 -0
  33. package/dist/chunk-L4SUWH5B.js +14 -0
  34. package/dist/chunk-M6CZQYPY.js +314 -0
  35. package/dist/chunk-MDIZUVIK.js +87 -0
  36. package/dist/chunk-MV6LW5UB.js +217 -0
  37. package/dist/chunk-NYLON2VC.js +6 -0
  38. package/dist/chunk-P6MYZMXQ.js +951 -0
  39. package/dist/chunk-Q53N43LQ.js +13 -0
  40. package/dist/chunk-QJVPRD3R.js +573 -0
  41. package/dist/chunk-RIGLQ4BV.js +3367 -0
  42. package/dist/chunk-SABTXESR.js +24 -0
  43. package/dist/chunk-SHUYVCID.js +6 -0
  44. package/dist/chunk-SYEVGPFC.js +686 -0
  45. package/dist/chunk-V7HUZ6UQ.js +5923 -0
  46. package/dist/chunk-WPUVJRIN.js +29 -0
  47. package/dist/chunk-XCZ3TTFK.js +22 -0
  48. package/dist/chunk-XSGU2Y2O.js +1611 -0
  49. package/dist/chunk-YNEW3L63.js +190 -0
  50. package/dist/chunk-YXGHPILG.js +614 -0
  51. package/dist/chunk-ZL4LLE2Z.js +465 -0
  52. package/dist/di-CcNeq9v-.d.cts +16 -0
  53. package/dist/di-CcNeq9v-.d.ts +16 -0
  54. package/dist/discovery/index.cjs +588 -0
  55. package/dist/discovery/index.d.cts +299 -0
  56. package/dist/discovery/index.d.ts +299 -0
  57. package/dist/discovery/index.js +49 -0
  58. package/dist/discovery.scanner-CUYLKlYT.d.cts +59 -0
  59. package/dist/discovery.scanner-CUYLKlYT.d.ts +59 -0
  60. package/dist/entitlement/index.cjs +805 -0
  61. package/dist/entitlement/index.d.cts +60 -0
  62. package/dist/entitlement/index.d.ts +60 -0
  63. package/dist/entitlement/index.js +57 -0
  64. package/dist/index.cjs +15918 -0
  65. package/dist/index.d.cts +85 -0
  66. package/dist/index.d.ts +85 -0
  67. package/dist/index.js +956 -0
  68. package/dist/plan-resolution-CFCoUkrE.d.cts +41 -0
  69. package/dist/plan-resolution-CFCoUkrE.d.ts +41 -0
  70. package/dist/platform/index.cjs +2772 -0
  71. package/dist/platform/index.d.cts +118 -0
  72. package/dist/platform/index.d.ts +118 -0
  73. package/dist/platform/index.js +37 -0
  74. package/dist/promo/index.cjs +995 -0
  75. package/dist/promo/index.d.cts +121 -0
  76. package/dist/promo/index.d.ts +121 -0
  77. package/dist/promo/index.js +58 -0
  78. package/dist/registration/index.cjs +1687 -0
  79. package/dist/registration/index.d.cts +444 -0
  80. package/dist/registration/index.d.ts +444 -0
  81. package/dist/registration/index.js +82 -0
  82. package/dist/saas-platform.module-CgZ2omgg.d.cts +180 -0
  83. package/dist/saas-platform.module-DT9TnCzk.d.ts +180 -0
  84. package/dist/service-DX8KbGXl.d.cts +109 -0
  85. package/dist/service-DX8KbGXl.d.ts +109 -0
  86. package/dist/subscription-contract/index.cjs +471 -0
  87. package/dist/subscription-contract/index.d.cts +17 -0
  88. package/dist/subscription-contract/index.d.ts +17 -0
  89. package/dist/subscription-contract/index.js +21 -0
  90. package/dist/subscription-contract.service--cm47ZJJ.d.cts +39 -0
  91. package/dist/subscription-contract.service--cm47ZJJ.d.ts +39 -0
  92. package/dist/testing/index.cjs +3942 -0
  93. package/dist/testing/index.d.cts +279 -0
  94. package/dist/testing/index.d.ts +279 -0
  95. package/dist/testing/index.js +1195 -0
  96. package/package.json +185 -0
@@ -0,0 +1,299 @@
1
+ import * as _nestjs_common from '@nestjs/common';
2
+ import { Type, CanActivate, DynamicModule, ForwardReference, LogLevel } from '@nestjs/common';
3
+ import { DiscoveredQuotaPolicy, DiscoveryCodeStatus, CapabilityKind, DiscoverySnapshot } from '@saasicat/types';
4
+ export { DiscoveredCapability, DiscoveredFeature, DiscoveredQuota, DiscoverySnapshot } from '@saasicat/types';
5
+ import { D as DiscoveryAppInfo } from '../discovery.scanner-CUYLKlYT.cjs';
6
+ export { a as DISCOVERY_APP_INFO_TOKEN, b as DISCOVERY_SNAPSHOT_PATH_TOKEN, c as DiscoveryScanner, d as computeSnapshotHash } from '../discovery.scanner-CUYLKlYT.cjs';
7
+ import '@nestjs/core';
8
+
9
+ /**
10
+ * Options for `@ImplementsCapability(key, options)`. `key` itself comes as a
11
+ * separate argument; everything else is optional to keep boilerplate low.
12
+ */
13
+ interface ImplementsCapabilityOptions {
14
+ /** Human-readable label (for the discovery UI). */
15
+ label?: string;
16
+ /**
17
+ * Aggregation wrapper: the feature key this capability belongs to.
18
+ * Bundles are planned exclusively in the SuperAdmin UI and are
19
+ * **deliberately not** part of the code decorator (SPEC_V2 §3.1).
20
+ */
21
+ feature?: string;
22
+ /**
23
+ * Code status. Default is `active` (visible in discovery, can be
24
+ * referenced in plans). `deprecated` recommends a replacement;
25
+ * `experimental` marks WIP capabilities that the SuperAdmin sees with a
26
+ * warning in the UI; `internal` does not appear in the UI, but does in the
27
+ * snapshot hash.
28
+ */
29
+ status?: DiscoveryCodeStatus;
30
+ /**
31
+ * Implementation kind. Not detected by the scanner itself — the user
32
+ * declares it explicitly (Endpoint, Service, Job, Event).
33
+ */
34
+ kind?: CapabilityKind;
35
+ /** Code-owner tag (e.g. 'accounting', 'membership'). Audit-relevant. */
36
+ owner?: string;
37
+ /** Recommended when `status: 'deprecated'`. */
38
+ replacementKey?: string;
39
+ /** Recommended when `status: 'deprecated'`. ISO date. */
40
+ removalPlannedAt?: string;
41
+ /** Free-text reason for deprecation/internal. */
42
+ reason?: string;
43
+ /**
44
+ * Feature keys that this capability's feature requires at runtime (#35)
45
+ * — e.g. `TRAINING_PLANNER` ⟹ `RESOURCE_MANAGEMENT`.
46
+ * The scanner aggregates the union of all capability requires per feature
47
+ * (minus its own featureKey); the strict-mode check and the configurator
48
+ * use it to surface unmet dependencies.
49
+ */
50
+ requires?: string[];
51
+ /**
52
+ * Old feature keys that this capability's feature replaces (#39).
53
+ * Hard path of replacement: the declaration lives on the NEW feature, the
54
+ * old code can be deleted in the same commit. (`replacementKey` remains
55
+ * the soft path on the still-existing, deprecated old code.)
56
+ */
57
+ replaces?: string[];
58
+ }
59
+ /** Options for `@RequiresCapability(...keys)` — currently no options, just the key list. */
60
+ type RequiresCapabilityKeys = readonly string[];
61
+ /**
62
+ * Options for `@DefinesQuota(options)`. `key` is the QuotaKey that
63
+ * `@EnforceQuota(...)` checks against at runtime.
64
+ */
65
+ interface DefinesQuotaOptions {
66
+ key: string;
67
+ label: string;
68
+ /** Unit for the UI (e.g. 'invoices', 'GB', 'requests'). */
69
+ unit: string;
70
+ policy: DiscoveredQuotaPolicy;
71
+ /** Aggregation wrapper: which feature the quota is bound to. */
72
+ feature?: string;
73
+ /** Old QuotaKeys that this quota replaces (#39, analogous to Capability `replaces`). */
74
+ replaces?: string[];
75
+ }
76
+ /** Options for `@EnforceQuota(key, options)`. */
77
+ interface EnforceQuotaOptions {
78
+ /**
79
+ * Increment step per call (default 1). Negative values allow a decrement
80
+ * (e.g. reversal of an invoice).
81
+ */
82
+ incrementBy?: number;
83
+ /**
84
+ * When the quota is checked:
85
+ * - `before` — before the handler runs (default; prevents the write)
86
+ * - `after` — after successful execution (for counters that need the
87
+ * result — e.g. actual storage size)
88
+ */
89
+ timing?: 'before' | 'after';
90
+ }
91
+
92
+ /**
93
+ * Internal metadata shape under which `@ImplementsCapability` stores its data.
94
+ * The scanner reads it back out.
95
+ */
96
+ interface ImplementsCapabilityMetadata extends ImplementsCapabilityOptions {
97
+ capabilityKey: string;
98
+ }
99
+ /**
100
+ * Marks a method as the implementation of a technical capability.
101
+ *
102
+ * The capability is the smallest checkable unit (e.g. `invoice.create`).
103
+ * Multiple methods may declare the same capability — the
104
+ * Discovery scanner deduplicates by `capabilityKey`.
105
+ *
106
+ * Optional fields:
107
+ * - `feature` aggregates capabilities into feature wrappers that the
108
+ * SuperAdmin can reference in plans.
109
+ * - `status` controls visibility/lifecycle (default `active`).
110
+ * - `kind` declares the implementation kind (default `endpoint`).
111
+ *
112
+ * **Bundles are deliberately NOT declared in the decorator** (SPEC_V2 §3.1).
113
+ * They are created exclusively in the SuperAdmin UI (DB table `bundles`),
114
+ * by the admin grouping features there.
115
+ *
116
+ * @example
117
+ * ```ts
118
+ * @Post()
119
+ * @ImplementsCapability('invoice.create', {
120
+ * label: 'Rechnung erstellen',
121
+ * feature: 'INVOICE_MANAGEMENT',
122
+ * kind: 'endpoint',
123
+ * owner: 'accounting',
124
+ * })
125
+ * createInvoice() { … }
126
+ * ```
127
+ */
128
+ declare const ImplementsCapability: (capabilityKey: string, options?: ImplementsCapabilityOptions) => _nestjs_common.CustomDecorator<string>;
129
+ /**
130
+ * Runtime guard: the tenant must have **all** specified capabilities.
131
+ * Multiple calls (or multiple keys) are evaluated as a logical AND.
132
+ *
133
+ * In contrast to `@RequireFeature(...)` (defined in
134
+ * `billing/require-feature.decorator.ts`, logical OR), the capability guard
135
+ * checks at the capability level — finer-grained, because a feature has
136
+ * multiple capabilities.
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * @Post()
141
+ * @ImplementsCapability('invoice.create', { … })
142
+ * @RequiresCapability('invoice.create')
143
+ * createInvoice() { … }
144
+ * ```
145
+ */
146
+ declare const RequiresCapability: (...capabilityKeys: string[]) => _nestjs_common.CustomDecorator<string>;
147
+ /**
148
+ * Marks a class as a QuotaProvider for a QuotaKey. The
149
+ * Discovery scanner aggregates from it the list of all quotas defined in
150
+ * code; the SuperAdmin can reference these as limits in plans/bundles.
151
+ *
152
+ * A class may have multiple `@DefinesQuota` decorators (e.g. a
153
+ * provider that holds counters for multiple QuotaKeys).
154
+ *
155
+ * @example
156
+ * ```ts
157
+ * @Injectable()
158
+ * @DefinesQuota({
159
+ * key: 'invoicesPerMonth',
160
+ * label: 'Rechnungen pro Monat',
161
+ * unit: 'invoices',
162
+ * policy: 'monthlyReset',
163
+ * feature: 'INVOICE_MANAGEMENT',
164
+ * })
165
+ * class InvoiceQuotaProvider implements QuotaProvider { … }
166
+ * ```
167
+ */
168
+ declare const DefinesQuota: (options: DefinesQuotaOptions) => _nestjs_common.CustomDecorator<string>;
169
+ /**
170
+ * Internal metadata shape under which `@EnforceQuota` stores its data.
171
+ */
172
+ interface EnforceQuotaMetadata extends EnforceQuotaOptions {
173
+ quotaKey: string;
174
+ }
175
+ /**
176
+ * Runtime enforcement: checks + increments a quota counter per call.
177
+ * The corresponding `@DefinesQuota` provider must be registered in at least
178
+ * one class (the strict-mode check verifies this).
179
+ *
180
+ * Default `incrementBy: 1`, `timing: 'before'`. On quota overrun the
181
+ * QuotaGuard throws a `LimitExceededError` (see
182
+ * `entitlement/limit-exceeded-error.ts`).
183
+ *
184
+ * @example
185
+ * ```ts
186
+ * @Post()
187
+ * @ImplementsCapability('invoice.create', { … })
188
+ * @EnforceQuota('invoicesPerMonth', { incrementBy: 1 })
189
+ * createInvoice() { … }
190
+ * ```
191
+ */
192
+ declare const EnforceQuota: (quotaKey: string, options?: EnforceQuotaOptions) => _nestjs_common.CustomDecorator<string>;
193
+
194
+ interface DiscoveryControllerConfig {
195
+ /**
196
+ * Class-level guards for `GET /admin/discovery`. MANDATORY — `forRoot()`
197
+ * throws at boot otherwise. Pass `[]` explicitly if the endpoint should
198
+ * deliberately be auth-free (e.g. CI/smoke test).
199
+ */
200
+ guards: Array<Type<CanActivate>>;
201
+ }
202
+ interface DiscoveryModuleOptions {
203
+ /** App identity that is adopted into the snapshot (`app.key`/`app.version`). */
204
+ app: DiscoveryAppInfo;
205
+ /**
206
+ * Controller mount for `GET /admin/discovery`. If omitted, the endpoint
207
+ * is not registered — consumers can then build it themselves (e.g. with
208
+ * a different path or additional guards) and inject the scanner via
209
+ * `DISCOVERY_SNAPSHOT_TOKEN`.
210
+ */
211
+ controller?: DiscoveryControllerConfig;
212
+ /**
213
+ * Modules whose providers must be visible in the DI scope of this
214
+ * DynamicModule — typically: `AuthModule`, which exports the `JwtAuthGuard`
215
+ * from `controller.guards`. Without this entry, NestJS fails with an
216
+ * `UnknownDependenciesException` for the guard if the AuthModule is not
217
+ * global.
218
+ */
219
+ imports?: Array<Type<unknown> | DynamicModule | Promise<DynamicModule> | ForwardReference>;
220
+ /**
221
+ * Optional: register the module globally — `DISCOVERY_SNAPSHOT_TOKEN` and
222
+ * `DiscoveryScanner` are then available without another `imports` entry.
223
+ * Default: `true`.
224
+ */
225
+ isGlobal?: boolean;
226
+ /**
227
+ * Optional: path into which the DiscoveryScanner writes the snapshot as
228
+ * JSON on `OnApplicationBootstrap`. Consumers (CI gates, preflight CLIs)
229
+ * can read the file with `loadDiscoverySnapshotFromFile(path)` without
230
+ * booting all app modules themselves. If omitted, nothing is written.
231
+ */
232
+ snapshotPath?: string | null;
233
+ }
234
+ declare class DiscoveryModule {
235
+ static forRoot(options: DiscoveryModuleOptions): DynamicModule;
236
+ }
237
+
238
+ declare class DiscoverySnapshotNotFoundError extends Error {
239
+ readonly path: string;
240
+ constructor(path: string);
241
+ }
242
+ /**
243
+ * Reads a snapshot JSON file persisted by the DiscoveryScanner and returns
244
+ * it as a `DiscoverySnapshot`. Throws `DiscoverySnapshotNotFoundError`
245
+ * when the file is missing — that is a hard boot condition for CI gates
246
+ * (a Preflight without a current snapshot would be worthless).
247
+ */
248
+ declare function loadDiscoverySnapshotFromFile(path: string): DiscoverySnapshot;
249
+
250
+ interface HeadlessScanOptions {
251
+ /** Log level of the boot context. Default `['error', 'warn', 'log']`. */
252
+ logger?: LogLevel[] | false;
253
+ /**
254
+ * Exit function on errors — default `process.exit(4)` (analogous to
255
+ * Seed-Gate/Preflight). Injectable in tests.
256
+ */
257
+ exit?: (code: number) => never;
258
+ }
259
+ /**
260
+ * Runs the headless scan and returns the snapshot. On boot/scan
261
+ * errors the error is logged and the process exits with code 4 —
262
+ * the staged rollout (non-fatal) is controlled by the consumer's
263
+ * migrate path (`… || echo WARN`), not by this runner.
264
+ */
265
+ declare function runHeadlessDiscoveryScan(appModule: Type, options?: HeadlessScanOptions): Promise<DiscoverySnapshot>;
266
+
267
+ /**
268
+ * Builds a controller class at boot time with the guards configured by the
269
+ * consumer. Called by DiscoveryModule.forRoot() when the `controller`
270
+ * option is set.
271
+ */
272
+ declare function buildDiscoveryController(guards: Array<Type<CanActivate>>): Type;
273
+
274
+ /** Method level: marks a capability implementation. */
275
+ declare const IMPLEMENTS_CAPABILITY_KEY = "discovery:implements-capability";
276
+ /** Method/class level: runtime guard, tenant must have the capability. */
277
+ declare const REQUIRES_CAPABILITY_KEY = "discovery:requires-capability";
278
+ /** Class level: marks a class as a QuotaProvider for a QuotaKey. */
279
+ declare const DEFINES_QUOTA_KEY = "discovery:defines-quota";
280
+ /** Method level: runtime enforcement (increment/check of a quota counter). */
281
+ declare const ENFORCE_QUOTA_KEY = "discovery:enforce-quota";
282
+ /**
283
+ * Provider token for the discovery snapshot — provided by the DiscoveryModule
284
+ * and consumed via `@Inject(DISCOVERY_SNAPSHOT_TOKEN)`
285
+ * (e.g. AdminController `/admin/discovery`, CatalogModule auto-sync).
286
+ *
287
+ * `Symbol.for` (NOT `Symbol`) is mandatory here: this package is built with tsup/
288
+ * esbuild, which cannot code-split CJS — each entry point
289
+ * (`./discovery`, `./catalog`, …) therefore inlines its OWN copy of this
290
+ * file. A plain `Symbol()` would be a different symbol per copy; a consumer
291
+ * that imports DiscoveryModule from `./discovery` and CatalogModule from `./catalog`
292
+ * would get two different tokens → the DI match
293
+ * fails → the snapshot does not reach the CatalogModule (prod incident
294
+ * 2026-06-09). `Symbol.for` uses the process-wide registry → identical across
295
+ * all bundle copies. (#25)
296
+ */
297
+ declare const DISCOVERY_SNAPSHOT_TOKEN: unique symbol;
298
+
299
+ export { DEFINES_QUOTA_KEY, DISCOVERY_SNAPSHOT_TOKEN, DefinesQuota, type DefinesQuotaOptions, DiscoveryAppInfo, type DiscoveryControllerConfig, DiscoveryModule, type DiscoveryModuleOptions, DiscoverySnapshotNotFoundError, ENFORCE_QUOTA_KEY, EnforceQuota, type EnforceQuotaMetadata, type EnforceQuotaOptions, type HeadlessScanOptions, IMPLEMENTS_CAPABILITY_KEY, ImplementsCapability, type ImplementsCapabilityMetadata, type ImplementsCapabilityOptions, REQUIRES_CAPABILITY_KEY, RequiresCapability, type RequiresCapabilityKeys, buildDiscoveryController, loadDiscoverySnapshotFromFile, runHeadlessDiscoveryScan };
@@ -0,0 +1,299 @@
1
+ import * as _nestjs_common from '@nestjs/common';
2
+ import { Type, CanActivate, DynamicModule, ForwardReference, LogLevel } from '@nestjs/common';
3
+ import { DiscoveredQuotaPolicy, DiscoveryCodeStatus, CapabilityKind, DiscoverySnapshot } from '@saasicat/types';
4
+ export { DiscoveredCapability, DiscoveredFeature, DiscoveredQuota, DiscoverySnapshot } from '@saasicat/types';
5
+ import { D as DiscoveryAppInfo } from '../discovery.scanner-CUYLKlYT.js';
6
+ export { a as DISCOVERY_APP_INFO_TOKEN, b as DISCOVERY_SNAPSHOT_PATH_TOKEN, c as DiscoveryScanner, d as computeSnapshotHash } from '../discovery.scanner-CUYLKlYT.js';
7
+ import '@nestjs/core';
8
+
9
+ /**
10
+ * Options for `@ImplementsCapability(key, options)`. `key` itself comes as a
11
+ * separate argument; everything else is optional to keep boilerplate low.
12
+ */
13
+ interface ImplementsCapabilityOptions {
14
+ /** Human-readable label (for the discovery UI). */
15
+ label?: string;
16
+ /**
17
+ * Aggregation wrapper: the feature key this capability belongs to.
18
+ * Bundles are planned exclusively in the SuperAdmin UI and are
19
+ * **deliberately not** part of the code decorator (SPEC_V2 §3.1).
20
+ */
21
+ feature?: string;
22
+ /**
23
+ * Code status. Default is `active` (visible in discovery, can be
24
+ * referenced in plans). `deprecated` recommends a replacement;
25
+ * `experimental` marks WIP capabilities that the SuperAdmin sees with a
26
+ * warning in the UI; `internal` does not appear in the UI, but does in the
27
+ * snapshot hash.
28
+ */
29
+ status?: DiscoveryCodeStatus;
30
+ /**
31
+ * Implementation kind. Not detected by the scanner itself — the user
32
+ * declares it explicitly (Endpoint, Service, Job, Event).
33
+ */
34
+ kind?: CapabilityKind;
35
+ /** Code-owner tag (e.g. 'accounting', 'membership'). Audit-relevant. */
36
+ owner?: string;
37
+ /** Recommended when `status: 'deprecated'`. */
38
+ replacementKey?: string;
39
+ /** Recommended when `status: 'deprecated'`. ISO date. */
40
+ removalPlannedAt?: string;
41
+ /** Free-text reason for deprecation/internal. */
42
+ reason?: string;
43
+ /**
44
+ * Feature keys that this capability's feature requires at runtime (#35)
45
+ * — e.g. `TRAINING_PLANNER` ⟹ `RESOURCE_MANAGEMENT`.
46
+ * The scanner aggregates the union of all capability requires per feature
47
+ * (minus its own featureKey); the strict-mode check and the configurator
48
+ * use it to surface unmet dependencies.
49
+ */
50
+ requires?: string[];
51
+ /**
52
+ * Old feature keys that this capability's feature replaces (#39).
53
+ * Hard path of replacement: the declaration lives on the NEW feature, the
54
+ * old code can be deleted in the same commit. (`replacementKey` remains
55
+ * the soft path on the still-existing, deprecated old code.)
56
+ */
57
+ replaces?: string[];
58
+ }
59
+ /** Options for `@RequiresCapability(...keys)` — currently no options, just the key list. */
60
+ type RequiresCapabilityKeys = readonly string[];
61
+ /**
62
+ * Options for `@DefinesQuota(options)`. `key` is the QuotaKey that
63
+ * `@EnforceQuota(...)` checks against at runtime.
64
+ */
65
+ interface DefinesQuotaOptions {
66
+ key: string;
67
+ label: string;
68
+ /** Unit for the UI (e.g. 'invoices', 'GB', 'requests'). */
69
+ unit: string;
70
+ policy: DiscoveredQuotaPolicy;
71
+ /** Aggregation wrapper: which feature the quota is bound to. */
72
+ feature?: string;
73
+ /** Old QuotaKeys that this quota replaces (#39, analogous to Capability `replaces`). */
74
+ replaces?: string[];
75
+ }
76
+ /** Options for `@EnforceQuota(key, options)`. */
77
+ interface EnforceQuotaOptions {
78
+ /**
79
+ * Increment step per call (default 1). Negative values allow a decrement
80
+ * (e.g. reversal of an invoice).
81
+ */
82
+ incrementBy?: number;
83
+ /**
84
+ * When the quota is checked:
85
+ * - `before` — before the handler runs (default; prevents the write)
86
+ * - `after` — after successful execution (for counters that need the
87
+ * result — e.g. actual storage size)
88
+ */
89
+ timing?: 'before' | 'after';
90
+ }
91
+
92
+ /**
93
+ * Internal metadata shape under which `@ImplementsCapability` stores its data.
94
+ * The scanner reads it back out.
95
+ */
96
+ interface ImplementsCapabilityMetadata extends ImplementsCapabilityOptions {
97
+ capabilityKey: string;
98
+ }
99
+ /**
100
+ * Marks a method as the implementation of a technical capability.
101
+ *
102
+ * The capability is the smallest checkable unit (e.g. `invoice.create`).
103
+ * Multiple methods may declare the same capability — the
104
+ * Discovery scanner deduplicates by `capabilityKey`.
105
+ *
106
+ * Optional fields:
107
+ * - `feature` aggregates capabilities into feature wrappers that the
108
+ * SuperAdmin can reference in plans.
109
+ * - `status` controls visibility/lifecycle (default `active`).
110
+ * - `kind` declares the implementation kind (default `endpoint`).
111
+ *
112
+ * **Bundles are deliberately NOT declared in the decorator** (SPEC_V2 §3.1).
113
+ * They are created exclusively in the SuperAdmin UI (DB table `bundles`),
114
+ * by the admin grouping features there.
115
+ *
116
+ * @example
117
+ * ```ts
118
+ * @Post()
119
+ * @ImplementsCapability('invoice.create', {
120
+ * label: 'Rechnung erstellen',
121
+ * feature: 'INVOICE_MANAGEMENT',
122
+ * kind: 'endpoint',
123
+ * owner: 'accounting',
124
+ * })
125
+ * createInvoice() { … }
126
+ * ```
127
+ */
128
+ declare const ImplementsCapability: (capabilityKey: string, options?: ImplementsCapabilityOptions) => _nestjs_common.CustomDecorator<string>;
129
+ /**
130
+ * Runtime guard: the tenant must have **all** specified capabilities.
131
+ * Multiple calls (or multiple keys) are evaluated as a logical AND.
132
+ *
133
+ * In contrast to `@RequireFeature(...)` (defined in
134
+ * `billing/require-feature.decorator.ts`, logical OR), the capability guard
135
+ * checks at the capability level — finer-grained, because a feature has
136
+ * multiple capabilities.
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * @Post()
141
+ * @ImplementsCapability('invoice.create', { … })
142
+ * @RequiresCapability('invoice.create')
143
+ * createInvoice() { … }
144
+ * ```
145
+ */
146
+ declare const RequiresCapability: (...capabilityKeys: string[]) => _nestjs_common.CustomDecorator<string>;
147
+ /**
148
+ * Marks a class as a QuotaProvider for a QuotaKey. The
149
+ * Discovery scanner aggregates from it the list of all quotas defined in
150
+ * code; the SuperAdmin can reference these as limits in plans/bundles.
151
+ *
152
+ * A class may have multiple `@DefinesQuota` decorators (e.g. a
153
+ * provider that holds counters for multiple QuotaKeys).
154
+ *
155
+ * @example
156
+ * ```ts
157
+ * @Injectable()
158
+ * @DefinesQuota({
159
+ * key: 'invoicesPerMonth',
160
+ * label: 'Rechnungen pro Monat',
161
+ * unit: 'invoices',
162
+ * policy: 'monthlyReset',
163
+ * feature: 'INVOICE_MANAGEMENT',
164
+ * })
165
+ * class InvoiceQuotaProvider implements QuotaProvider { … }
166
+ * ```
167
+ */
168
+ declare const DefinesQuota: (options: DefinesQuotaOptions) => _nestjs_common.CustomDecorator<string>;
169
+ /**
170
+ * Internal metadata shape under which `@EnforceQuota` stores its data.
171
+ */
172
+ interface EnforceQuotaMetadata extends EnforceQuotaOptions {
173
+ quotaKey: string;
174
+ }
175
+ /**
176
+ * Runtime enforcement: checks + increments a quota counter per call.
177
+ * The corresponding `@DefinesQuota` provider must be registered in at least
178
+ * one class (the strict-mode check verifies this).
179
+ *
180
+ * Default `incrementBy: 1`, `timing: 'before'`. On quota overrun the
181
+ * QuotaGuard throws a `LimitExceededError` (see
182
+ * `entitlement/limit-exceeded-error.ts`).
183
+ *
184
+ * @example
185
+ * ```ts
186
+ * @Post()
187
+ * @ImplementsCapability('invoice.create', { … })
188
+ * @EnforceQuota('invoicesPerMonth', { incrementBy: 1 })
189
+ * createInvoice() { … }
190
+ * ```
191
+ */
192
+ declare const EnforceQuota: (quotaKey: string, options?: EnforceQuotaOptions) => _nestjs_common.CustomDecorator<string>;
193
+
194
+ interface DiscoveryControllerConfig {
195
+ /**
196
+ * Class-level guards for `GET /admin/discovery`. MANDATORY — `forRoot()`
197
+ * throws at boot otherwise. Pass `[]` explicitly if the endpoint should
198
+ * deliberately be auth-free (e.g. CI/smoke test).
199
+ */
200
+ guards: Array<Type<CanActivate>>;
201
+ }
202
+ interface DiscoveryModuleOptions {
203
+ /** App identity that is adopted into the snapshot (`app.key`/`app.version`). */
204
+ app: DiscoveryAppInfo;
205
+ /**
206
+ * Controller mount for `GET /admin/discovery`. If omitted, the endpoint
207
+ * is not registered — consumers can then build it themselves (e.g. with
208
+ * a different path or additional guards) and inject the scanner via
209
+ * `DISCOVERY_SNAPSHOT_TOKEN`.
210
+ */
211
+ controller?: DiscoveryControllerConfig;
212
+ /**
213
+ * Modules whose providers must be visible in the DI scope of this
214
+ * DynamicModule — typically: `AuthModule`, which exports the `JwtAuthGuard`
215
+ * from `controller.guards`. Without this entry, NestJS fails with an
216
+ * `UnknownDependenciesException` for the guard if the AuthModule is not
217
+ * global.
218
+ */
219
+ imports?: Array<Type<unknown> | DynamicModule | Promise<DynamicModule> | ForwardReference>;
220
+ /**
221
+ * Optional: register the module globally — `DISCOVERY_SNAPSHOT_TOKEN` and
222
+ * `DiscoveryScanner` are then available without another `imports` entry.
223
+ * Default: `true`.
224
+ */
225
+ isGlobal?: boolean;
226
+ /**
227
+ * Optional: path into which the DiscoveryScanner writes the snapshot as
228
+ * JSON on `OnApplicationBootstrap`. Consumers (CI gates, preflight CLIs)
229
+ * can read the file with `loadDiscoverySnapshotFromFile(path)` without
230
+ * booting all app modules themselves. If omitted, nothing is written.
231
+ */
232
+ snapshotPath?: string | null;
233
+ }
234
+ declare class DiscoveryModule {
235
+ static forRoot(options: DiscoveryModuleOptions): DynamicModule;
236
+ }
237
+
238
+ declare class DiscoverySnapshotNotFoundError extends Error {
239
+ readonly path: string;
240
+ constructor(path: string);
241
+ }
242
+ /**
243
+ * Reads a snapshot JSON file persisted by the DiscoveryScanner and returns
244
+ * it as a `DiscoverySnapshot`. Throws `DiscoverySnapshotNotFoundError`
245
+ * when the file is missing — that is a hard boot condition for CI gates
246
+ * (a Preflight without a current snapshot would be worthless).
247
+ */
248
+ declare function loadDiscoverySnapshotFromFile(path: string): DiscoverySnapshot;
249
+
250
+ interface HeadlessScanOptions {
251
+ /** Log level of the boot context. Default `['error', 'warn', 'log']`. */
252
+ logger?: LogLevel[] | false;
253
+ /**
254
+ * Exit function on errors — default `process.exit(4)` (analogous to
255
+ * Seed-Gate/Preflight). Injectable in tests.
256
+ */
257
+ exit?: (code: number) => never;
258
+ }
259
+ /**
260
+ * Runs the headless scan and returns the snapshot. On boot/scan
261
+ * errors the error is logged and the process exits with code 4 —
262
+ * the staged rollout (non-fatal) is controlled by the consumer's
263
+ * migrate path (`… || echo WARN`), not by this runner.
264
+ */
265
+ declare function runHeadlessDiscoveryScan(appModule: Type, options?: HeadlessScanOptions): Promise<DiscoverySnapshot>;
266
+
267
+ /**
268
+ * Builds a controller class at boot time with the guards configured by the
269
+ * consumer. Called by DiscoveryModule.forRoot() when the `controller`
270
+ * option is set.
271
+ */
272
+ declare function buildDiscoveryController(guards: Array<Type<CanActivate>>): Type;
273
+
274
+ /** Method level: marks a capability implementation. */
275
+ declare const IMPLEMENTS_CAPABILITY_KEY = "discovery:implements-capability";
276
+ /** Method/class level: runtime guard, tenant must have the capability. */
277
+ declare const REQUIRES_CAPABILITY_KEY = "discovery:requires-capability";
278
+ /** Class level: marks a class as a QuotaProvider for a QuotaKey. */
279
+ declare const DEFINES_QUOTA_KEY = "discovery:defines-quota";
280
+ /** Method level: runtime enforcement (increment/check of a quota counter). */
281
+ declare const ENFORCE_QUOTA_KEY = "discovery:enforce-quota";
282
+ /**
283
+ * Provider token for the discovery snapshot — provided by the DiscoveryModule
284
+ * and consumed via `@Inject(DISCOVERY_SNAPSHOT_TOKEN)`
285
+ * (e.g. AdminController `/admin/discovery`, CatalogModule auto-sync).
286
+ *
287
+ * `Symbol.for` (NOT `Symbol`) is mandatory here: this package is built with tsup/
288
+ * esbuild, which cannot code-split CJS — each entry point
289
+ * (`./discovery`, `./catalog`, …) therefore inlines its OWN copy of this
290
+ * file. A plain `Symbol()` would be a different symbol per copy; a consumer
291
+ * that imports DiscoveryModule from `./discovery` and CatalogModule from `./catalog`
292
+ * would get two different tokens → the DI match
293
+ * fails → the snapshot does not reach the CatalogModule (prod incident
294
+ * 2026-06-09). `Symbol.for` uses the process-wide registry → identical across
295
+ * all bundle copies. (#25)
296
+ */
297
+ declare const DISCOVERY_SNAPSHOT_TOKEN: unique symbol;
298
+
299
+ export { DEFINES_QUOTA_KEY, DISCOVERY_SNAPSHOT_TOKEN, DefinesQuota, type DefinesQuotaOptions, DiscoveryAppInfo, type DiscoveryControllerConfig, DiscoveryModule, type DiscoveryModuleOptions, DiscoverySnapshotNotFoundError, ENFORCE_QUOTA_KEY, EnforceQuota, type EnforceQuotaMetadata, type EnforceQuotaOptions, type HeadlessScanOptions, IMPLEMENTS_CAPABILITY_KEY, ImplementsCapability, type ImplementsCapabilityMetadata, type ImplementsCapabilityOptions, REQUIRES_CAPABILITY_KEY, RequiresCapability, type RequiresCapabilityKeys, buildDiscoveryController, loadDiscoverySnapshotFromFile, runHeadlessDiscoveryScan };
@@ -0,0 +1,49 @@
1
+ import {
2
+ DefinesQuota,
3
+ EnforceQuota,
4
+ ImplementsCapability,
5
+ RequiresCapability,
6
+ runHeadlessDiscoveryScan
7
+ } from "../chunk-HRU3CP57.js";
8
+ import {
9
+ DiscoverySnapshotNotFoundError,
10
+ loadDiscoverySnapshotFromFile
11
+ } from "../chunk-ABB2CRAS.js";
12
+ import {
13
+ DiscoveryModule,
14
+ buildDiscoveryController
15
+ } from "../chunk-AJ3EPELS.js";
16
+ import {
17
+ DISCOVERY_APP_INFO_TOKEN,
18
+ DISCOVERY_SNAPSHOT_PATH_TOKEN,
19
+ DiscoveryScanner,
20
+ computeSnapshotHash
21
+ } from "../chunk-M6CZQYPY.js";
22
+ import {
23
+ DEFINES_QUOTA_KEY,
24
+ DISCOVERY_SNAPSHOT_TOKEN,
25
+ ENFORCE_QUOTA_KEY,
26
+ IMPLEMENTS_CAPABILITY_KEY,
27
+ REQUIRES_CAPABILITY_KEY
28
+ } from "../chunk-L4SUWH5B.js";
29
+ import "../chunk-SHUYVCID.js";
30
+ export {
31
+ DEFINES_QUOTA_KEY,
32
+ DISCOVERY_APP_INFO_TOKEN,
33
+ DISCOVERY_SNAPSHOT_PATH_TOKEN,
34
+ DISCOVERY_SNAPSHOT_TOKEN,
35
+ DefinesQuota,
36
+ DiscoveryModule,
37
+ DiscoveryScanner,
38
+ DiscoverySnapshotNotFoundError,
39
+ ENFORCE_QUOTA_KEY,
40
+ EnforceQuota,
41
+ IMPLEMENTS_CAPABILITY_KEY,
42
+ ImplementsCapability,
43
+ REQUIRES_CAPABILITY_KEY,
44
+ RequiresCapability,
45
+ buildDiscoveryController,
46
+ computeSnapshotHash,
47
+ loadDiscoverySnapshotFromFile,
48
+ runHeadlessDiscoveryScan
49
+ };