@saasicat/nest 0.18.1 → 0.21.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 (56) hide show
  1. package/dist/_entries.cjs +1341 -590
  2. package/dist/admin/index.js +2 -1
  3. package/dist/billing/index.d.cts +10 -6
  4. package/dist/billing/index.d.ts +10 -6
  5. package/dist/billing/index.js +7 -6
  6. package/dist/catalog/index.js +4 -4
  7. package/dist/checkout-offer/index.js +2 -2
  8. package/dist/{chunk-RZVSKKXX.js → chunk-3DRZ5GCP.js} +56 -105
  9. package/dist/{chunk-GLLCB4BP.js → chunk-4O6R6ARQ.js} +23 -13
  10. package/dist/{chunk-7RRWI5LH.js → chunk-55QGAZRQ.js} +10 -10
  11. package/dist/{chunk-Z3FWN3HJ.js → chunk-5ZXBXYXU.js} +3 -3
  12. package/dist/{chunk-V34AA7C7.js → chunk-6WL6VQ7Z.js} +1 -1
  13. package/dist/{chunk-ABB2CRAS.js → chunk-74SCJ4HF.js} +1 -1
  14. package/dist/{chunk-EXOLOJFZ.js → chunk-AGOG5B77.js} +36 -9
  15. package/dist/{chunk-32EMXPU2.js → chunk-B7NRY2LV.js} +48 -17
  16. package/dist/{chunk-MDIZUVIK.js → chunk-BSK6YBLI.js} +1 -1
  17. package/dist/{chunk-R2QOSVAA.js → chunk-DQKCK7DX.js} +1 -1
  18. package/dist/chunk-EBJJNTW6.js +22 -0
  19. package/dist/{chunk-PLJEAUFU.js → chunk-FYWLPYBK.js} +79 -39
  20. package/dist/{chunk-P3DP6GJE.js → chunk-GC5AKN44.js} +487 -160
  21. package/dist/{chunk-5KH2O4SF.js → chunk-NZLAYCEX.js} +286 -110
  22. package/dist/{chunk-E7AJ42AB.js → chunk-VQQWF7MW.js} +172 -28
  23. package/dist/{chunk-GJBXOCRZ.js → chunk-XTVAFJ3P.js} +46 -17
  24. package/dist/{chunk-BOADBIGB.js → chunk-YYUXKLXK.js} +2 -2
  25. package/dist/{chunk-YITTE646.js → chunk-ZH5PYEMI.js} +41 -23
  26. package/dist/{define-saasicat-Rc0rXrYG.d.ts → define-saasicat-B20Ms8UC.d.ts} +1 -1
  27. package/dist/{define-saasicat-CeDBX9ji.d.cts → define-saasicat-Cf9DpEsY.d.cts} +1 -1
  28. package/dist/discovery/index.d.cts +2 -2
  29. package/dist/discovery/index.d.ts +2 -2
  30. package/dist/discovery/index.js +4 -4
  31. package/dist/{enforce-quota.interceptor-CmbWX39L.d.ts → enforce-quota.interceptor-CUyfR4cQ.d.ts} +1 -1
  32. package/dist/{enforce-quota.interceptor-DMw-bPyk.d.cts → enforce-quota.interceptor-Dtg5WdFj.d.cts} +1 -1
  33. package/dist/entitlement/index.js +3 -2
  34. package/dist/index.d.cts +27 -8
  35. package/dist/index.d.ts +27 -8
  36. package/dist/index.js +21 -17
  37. package/dist/{module-DgYJur6f.d.ts → module-BE17XFLg.d.ts} +1 -1
  38. package/dist/{module-CSN8j8Tp.d.cts → module-CHMIyWlf.d.cts} +1 -1
  39. package/dist/platform/index.d.cts +8 -8
  40. package/dist/platform/index.d.ts +8 -8
  41. package/dist/platform/index.js +14 -13
  42. package/dist/promo/index.d.cts +3 -3
  43. package/dist/promo/index.d.ts +3 -3
  44. package/dist/promo/index.js +4 -3
  45. package/dist/registration/index.js +2 -1
  46. package/dist/{saas-platform.module-D3rUTiho.d.cts → saas-platform.module-BfaYfL-G.d.cts} +1 -1
  47. package/dist/{saas-platform.module-DLhoqKNA.d.ts → saas-platform.module-DChhDrR-.d.ts} +1 -1
  48. package/dist/{service-DX8KbGXl.d.cts → service-D48clObR.d.cts} +3 -3
  49. package/dist/{service-DX8KbGXl.d.ts → service-D48clObR.d.ts} +3 -3
  50. package/dist/subscription-contract/index.js +2 -2
  51. package/dist/{tenant-billing.controller-CG4CaFUP.d.cts → tenant-billing.controller-BIF-mstX.d.cts} +2 -2
  52. package/dist/{tenant-billing.controller-BwZRnJ3q.d.ts → tenant-billing.controller-DT5UrnDX.d.ts} +2 -2
  53. package/dist/testing/index.d.cts +5 -5
  54. package/dist/testing/index.d.ts +5 -5
  55. package/dist/testing/index.js +34 -33
  56. package/package.json +3 -3
@@ -25,7 +25,7 @@ import {
25
25
  SuperAdminGuard,
26
26
  SuspendTenantDto,
27
27
  buildAdminStatsController
28
- } from "../chunk-PLJEAUFU.js";
28
+ } from "../chunk-FYWLPYBK.js";
29
29
  import {
30
30
  AUDIT_PORT_TOKEN,
31
31
  AdminAuditService,
@@ -33,6 +33,7 @@ import {
33
33
  RLS_BYPASS_PORT_TOKEN
34
34
  } from "../chunk-E56W4U2P.js";
35
35
  import "../chunk-YSC46BGM.js";
36
+ import "../chunk-EBJJNTW6.js";
36
37
  import "../chunk-SABTXESR.js";
37
38
  import "../chunk-SHUYVCID.js";
38
39
  export {
@@ -8,10 +8,10 @@ import { c as EntitlementService } from '../aggregation-SKfXu09x.cjs';
8
8
  import { P as ProviderSpec } from '../di-CcNeq9v-.cjs';
9
9
  import { f as ContractFreezePort, g as ContractFreezeSourcePort } from '../subscription-bundles.module-DCpV-Pb5.cjs';
10
10
  export { A as AUDIT_CONTEXT_RESOLVER_TOKEN, a as AuditContextResolver, b as AuthGuardList, C as CONTRACT_FREEZE_PORT_TOKEN, c as CONTRACT_FREEZE_PROJECT_KEY_TOKEN, d as CONTRACT_FREEZE_SOURCE_PORT_TOKEN, e as ContractFreezeBundleSnapshot, D as DuePendingPlanChange, P as PENDING_PLAN_QUERY_PORT_TOKEN, h as PendingPlanQueryPort, S as SELF_SERVICE_BLOCKED_BUNDLES_TOKEN, i as SELF_SERVICE_BLOCKED_PLANS_TOKEN, j as SUBSCRIPTION_USAGE_PORT_TOKEN, k as SUBSCRIPTION_WRITE_PORT_TOKEN, l as SelfServiceBlockedBundles, m as SelfServiceBlockedPlans, n as SubscriptionBundleControllerOptions, o as SubscriptionBundleModule, p as SubscriptionBundleModuleOptions, T as TENANT_AUTH_GUARDS_TOKEN, q as TENANT_ID_RESOLVER_TOKEN, r as TRIAL_PROJECTION_PORT_TOKEN, s as TenantBillingModule, t as TenantBillingModuleOptions, u as TenantIdResolver, v as TrialProjectionInput, w as TrialProjectionPort, U as USAGE_SNAPSHOT_PORT_TOKEN, x as USER_EMAIL_RESOLVER_TOKEN, y as USER_ID_RESOLVER_TOKEN, z as UserEmailResolver, B as UserIdResolver } from '../subscription-bundles.module-DCpV-Pb5.cjs';
11
- export { A as AddBundleToSubscriptionInput, B as BundlePreviewSnapshot, C as CancelBundleFromSubscriptionInput, a as CancelSubscriptionDto, b as ChangePlanDto, c as CompleteOnboardingSubscriptionDto, d as ComposedTenantAuthGuard, L as LimitsCheckRow, P as PendingPlanMaterializationService, e as PlanChangeContext, f as PlanChangePreviewDto, g as PlanChangePreviewIssue, h as PlanChangePreviewService, i as PlanChangeType, j as PlanSnapshotDto, k as PreviewPlanChangeDto, l as ProrationDto, m as ProrationInput, R as RedundantFeatureHint, S as SubscriptionBundleAddPreviewDto, n as SubscriptionBundleCancelPreviewDto, o as SubscriptionBundleConfig, p as SubscriptionBundlePreviewContext, q as SubscriptionBundlePreviewIssue, r as SubscriptionBundlePreviewService, s as SubscriptionBundlesService, T as TenantAdminGuard, t as TenantBillingController, U as UsageResponse, u as addMonths, v as computeProration, w as resolveBundleCancelEffectiveAt, x as resolveBundlePriceNet } from '../tenant-billing.controller-CG4CaFUP.cjs';
11
+ export { A as AddBundleToSubscriptionInput, B as BundlePreviewSnapshot, C as CancelBundleFromSubscriptionInput, a as CancelSubscriptionDto, b as ChangePlanDto, c as CompleteOnboardingSubscriptionDto, d as ComposedTenantAuthGuard, L as LimitsCheckRow, P as PendingPlanMaterializationService, e as PlanChangeContext, f as PlanChangePreviewDto, g as PlanChangePreviewIssue, h as PlanChangePreviewService, i as PlanChangeType, j as PlanSnapshotDto, k as PreviewPlanChangeDto, l as ProrationDto, m as ProrationInput, R as RedundantFeatureHint, S as SubscriptionBundleAddPreviewDto, n as SubscriptionBundleCancelPreviewDto, o as SubscriptionBundleConfig, p as SubscriptionBundlePreviewContext, q as SubscriptionBundlePreviewIssue, r as SubscriptionBundlePreviewService, s as SubscriptionBundlesService, T as TenantAdminGuard, t as TenantBillingController, U as UsageResponse, u as addMonths, v as computeProration, w as resolveBundleCancelEffectiveAt, x as resolveBundlePriceNet } from '../tenant-billing.controller-BIF-mstX.cjs';
12
12
  import { S as SubscriptionContractService } from '../subscription-contract.service--cm47ZJJ.cjs';
13
13
  import '../plan-resolution-CFCoUkrE.cjs';
14
- import '../service-DX8KbGXl.cjs';
14
+ import '../service-D48clObR.cjs';
15
15
  import '../admin-audit.service-9IqXMlZm.cjs';
16
16
 
17
17
  /**
@@ -197,15 +197,19 @@ declare class FeatureGuard implements CanActivate {
197
197
  constructor(reflector: Reflector, entitlements: EntitlementService, config?: FeatureGuardConfig | null, upsellResolver?: UpsellOfferResolver | null);
198
198
  canActivate(context: ExecutionContext): Promise<boolean>;
199
199
  /**
200
- * Upsell response (#36): with a registered `UpsellOfferResolver` the 403
201
- * becomes machine-readable (`FeatureNotLicensedBody`), so consumer UIs can
202
- * render a purchase offer. Without a resolver the previous plain 403
203
- * remainsno breaking change.
200
+ * Upsell response (#36): the 403 body is always the full
201
+ * `FeatureNotLicensedBody`, so a consumer that matches on `code` can read
202
+ * `featureKey`/`offers` unconditionally. Without a registered
203
+ * `UpsellOfferResolver` there is simply nothing to offer `offers` is
204
+ * empty, the same degradation a failing resolver produces.
204
205
  *
205
206
  * Deliberately 403 + `code` field instead of 402 — rationale in
206
207
  * `@saasicat/types` upsell.types.ts (402 is reserved / inconsistently
207
208
  * supported; SPA interceptors must not treat the 403 as an auth error, the
208
209
  * distinction runs through `code`).
210
+ *
211
+ * The message must keep starting with the word `Feature` — consumers
212
+ * classify the response by that prefix.
209
213
  */
210
214
  private buildNotLicensedException;
211
215
  /**
@@ -8,10 +8,10 @@ import { c as EntitlementService } from '../aggregation-o0LMenvl.js';
8
8
  import { P as ProviderSpec } from '../di-CcNeq9v-.js';
9
9
  import { f as ContractFreezePort, g as ContractFreezeSourcePort } from '../subscription-bundles.module-BQmoozm5.js';
10
10
  export { A as AUDIT_CONTEXT_RESOLVER_TOKEN, a as AuditContextResolver, b as AuthGuardList, C as CONTRACT_FREEZE_PORT_TOKEN, c as CONTRACT_FREEZE_PROJECT_KEY_TOKEN, d as CONTRACT_FREEZE_SOURCE_PORT_TOKEN, e as ContractFreezeBundleSnapshot, D as DuePendingPlanChange, P as PENDING_PLAN_QUERY_PORT_TOKEN, h as PendingPlanQueryPort, S as SELF_SERVICE_BLOCKED_BUNDLES_TOKEN, i as SELF_SERVICE_BLOCKED_PLANS_TOKEN, j as SUBSCRIPTION_USAGE_PORT_TOKEN, k as SUBSCRIPTION_WRITE_PORT_TOKEN, l as SelfServiceBlockedBundles, m as SelfServiceBlockedPlans, n as SubscriptionBundleControllerOptions, o as SubscriptionBundleModule, p as SubscriptionBundleModuleOptions, T as TENANT_AUTH_GUARDS_TOKEN, q as TENANT_ID_RESOLVER_TOKEN, r as TRIAL_PROJECTION_PORT_TOKEN, s as TenantBillingModule, t as TenantBillingModuleOptions, u as TenantIdResolver, v as TrialProjectionInput, w as TrialProjectionPort, U as USAGE_SNAPSHOT_PORT_TOKEN, x as USER_EMAIL_RESOLVER_TOKEN, y as USER_ID_RESOLVER_TOKEN, z as UserEmailResolver, B as UserIdResolver } from '../subscription-bundles.module-BQmoozm5.js';
11
- export { A as AddBundleToSubscriptionInput, B as BundlePreviewSnapshot, C as CancelBundleFromSubscriptionInput, a as CancelSubscriptionDto, b as ChangePlanDto, c as CompleteOnboardingSubscriptionDto, d as ComposedTenantAuthGuard, L as LimitsCheckRow, P as PendingPlanMaterializationService, e as PlanChangeContext, f as PlanChangePreviewDto, g as PlanChangePreviewIssue, h as PlanChangePreviewService, i as PlanChangeType, j as PlanSnapshotDto, k as PreviewPlanChangeDto, l as ProrationDto, m as ProrationInput, R as RedundantFeatureHint, S as SubscriptionBundleAddPreviewDto, n as SubscriptionBundleCancelPreviewDto, o as SubscriptionBundleConfig, p as SubscriptionBundlePreviewContext, q as SubscriptionBundlePreviewIssue, r as SubscriptionBundlePreviewService, s as SubscriptionBundlesService, T as TenantAdminGuard, t as TenantBillingController, U as UsageResponse, u as addMonths, v as computeProration, w as resolveBundleCancelEffectiveAt, x as resolveBundlePriceNet } from '../tenant-billing.controller-BwZRnJ3q.js';
11
+ export { A as AddBundleToSubscriptionInput, B as BundlePreviewSnapshot, C as CancelBundleFromSubscriptionInput, a as CancelSubscriptionDto, b as ChangePlanDto, c as CompleteOnboardingSubscriptionDto, d as ComposedTenantAuthGuard, L as LimitsCheckRow, P as PendingPlanMaterializationService, e as PlanChangeContext, f as PlanChangePreviewDto, g as PlanChangePreviewIssue, h as PlanChangePreviewService, i as PlanChangeType, j as PlanSnapshotDto, k as PreviewPlanChangeDto, l as ProrationDto, m as ProrationInput, R as RedundantFeatureHint, S as SubscriptionBundleAddPreviewDto, n as SubscriptionBundleCancelPreviewDto, o as SubscriptionBundleConfig, p as SubscriptionBundlePreviewContext, q as SubscriptionBundlePreviewIssue, r as SubscriptionBundlePreviewService, s as SubscriptionBundlesService, T as TenantAdminGuard, t as TenantBillingController, U as UsageResponse, u as addMonths, v as computeProration, w as resolveBundleCancelEffectiveAt, x as resolveBundlePriceNet } from '../tenant-billing.controller-DT5UrnDX.js';
12
12
  import { S as SubscriptionContractService } from '../subscription-contract.service--cm47ZJJ.js';
13
13
  import '../plan-resolution-CFCoUkrE.js';
14
- import '../service-DX8KbGXl.js';
14
+ import '../service-D48clObR.js';
15
15
  import '../admin-audit.service-9IqXMlZm.js';
16
16
 
17
17
  /**
@@ -197,15 +197,19 @@ declare class FeatureGuard implements CanActivate {
197
197
  constructor(reflector: Reflector, entitlements: EntitlementService, config?: FeatureGuardConfig | null, upsellResolver?: UpsellOfferResolver | null);
198
198
  canActivate(context: ExecutionContext): Promise<boolean>;
199
199
  /**
200
- * Upsell response (#36): with a registered `UpsellOfferResolver` the 403
201
- * becomes machine-readable (`FeatureNotLicensedBody`), so consumer UIs can
202
- * render a purchase offer. Without a resolver the previous plain 403
203
- * remainsno breaking change.
200
+ * Upsell response (#36): the 403 body is always the full
201
+ * `FeatureNotLicensedBody`, so a consumer that matches on `code` can read
202
+ * `featureKey`/`offers` unconditionally. Without a registered
203
+ * `UpsellOfferResolver` there is simply nothing to offer `offers` is
204
+ * empty, the same degradation a failing resolver produces.
204
205
  *
205
206
  * Deliberately 403 + `code` field instead of 402 — rationale in
206
207
  * `@saasicat/types` upsell.types.ts (402 is reserved / inconsistently
207
208
  * supported; SPA interceptors must not treat the 403 as an auth error, the
208
209
  * distinction runs through `code`).
210
+ *
211
+ * The message must keep starting with the word `Feature` — consumers
212
+ * classify the response by that prefix.
209
213
  */
210
214
  private buildNotLicensedException;
211
215
  /**
@@ -58,9 +58,9 @@ import {
58
58
  periodEndWithMinLead,
59
59
  resolveBundleCancelEffectiveAt,
60
60
  resolveBundlePriceNet
61
- } from "../chunk-5KH2O4SF.js";
62
- import "../chunk-E7AJ42AB.js";
63
- import "../chunk-32EMXPU2.js";
61
+ } from "../chunk-NZLAYCEX.js";
62
+ import "../chunk-VQQWF7MW.js";
63
+ import "../chunk-B7NRY2LV.js";
64
64
  import {
65
65
  AUDIT_CONTEXT_RESOLVER_TOKEN,
66
66
  PENDING_PLAN_QUERY_PORT_TOKEN,
@@ -76,12 +76,12 @@ import {
76
76
  classifyPlanDiff
77
77
  } from "../chunk-WPUVJRIN.js";
78
78
  import "../chunk-E56W4U2P.js";
79
- import "../chunk-MDIZUVIK.js";
79
+ import "../chunk-BSK6YBLI.js";
80
80
  import "../chunk-YSC46BGM.js";
81
81
  import {
82
82
  SUBSCRIPTION_BUNDLE_CONFIG_TOKEN,
83
83
  SUBSCRIPTION_BUNDLE_REPOSITORY_TOKEN
84
- } from "../chunk-EXOLOJFZ.js";
84
+ } from "../chunk-AGOG5B77.js";
85
85
  import {
86
86
  PLAN_CATALOG_READ_SINK_TOKEN,
87
87
  PLAN_CATALOG_TOKEN,
@@ -96,8 +96,9 @@ import {
96
96
  getPlanQuota,
97
97
  isFeatureInPlan,
98
98
  isFeaturePlannedOnly
99
- } from "../chunk-R2QOSVAA.js";
99
+ } from "../chunk-DQKCK7DX.js";
100
100
  import "../chunk-NYLON2VC.js";
101
+ import "../chunk-EBJJNTW6.js";
101
102
  import "../chunk-WKVBNTYC.js";
102
103
  import "../chunk-L4KTQ6CL.js";
103
104
  import "../chunk-SABTXESR.js";
@@ -6,8 +6,8 @@ import {
6
6
  runSeedGateFromFile,
7
7
  seedGateExitCode,
8
8
  validateSeedAgainstSnapshot
9
- } from "../chunk-7RRWI5LH.js";
10
- import "../chunk-ABB2CRAS.js";
9
+ } from "../chunk-55QGAZRQ.js";
10
+ import "../chunk-74SCJ4HF.js";
11
11
  import {
12
12
  ADVISORY_STRICT_MODE_CODES,
13
13
  BundlesService,
@@ -56,11 +56,11 @@ import {
56
56
  quotaApprovalSignature,
57
57
  validateBundleDraft,
58
58
  validatePlanDraft
59
- } from "../chunk-P3DP6GJE.js";
59
+ } from "../chunk-GC5AKN44.js";
60
60
  import "../chunk-WPUVJRIN.js";
61
61
  import "../chunk-E56W4U2P.js";
62
62
  import "../chunk-YSC46BGM.js";
63
- import "../chunk-Z3FWN3HJ.js";
63
+ import "../chunk-5ZXBXYXU.js";
64
64
  import "../chunk-WKVBNTYC.js";
65
65
  import {
66
66
  BUNDLE_REPOSITORY_TOKEN,
@@ -6,8 +6,8 @@ import {
6
6
  CreateCheckoutOfferDto,
7
7
  UpdateCheckoutOfferDto,
8
8
  buildCheckoutOfferController
9
- } from "../chunk-GJBXOCRZ.js";
10
- import "../chunk-MDIZUVIK.js";
9
+ } from "../chunk-XTVAFJ3P.js";
10
+ import "../chunk-BSK6YBLI.js";
11
11
  import "../chunk-YSC46BGM.js";
12
12
  import "../chunk-L4KTQ6CL.js";
13
13
  import "../chunk-SABTXESR.js";
@@ -1,3 +1,6 @@
1
+ import {
2
+ codedError
3
+ } from "./chunk-EBJJNTW6.js";
1
4
  import {
2
5
  asProvider
3
6
  } from "./chunk-SABTXESR.js";
@@ -54,7 +57,7 @@ var REGISTRATION_CONFIG_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/Regist
54
57
 
55
58
  // src/registration/pending-registration.service.ts
56
59
  import { BadRequestException, Inject, Injectable, Logger, NotFoundException, Optional } from "@nestjs/common";
57
- import { REGISTRATION_RESUME_TTL_MINUTES } from "@saasicat/types";
60
+ import { REGISTRATION_ERROR_CODES, REGISTRATION_RESUME_TTL_MINUTES } from "@saasicat/types";
58
61
  import { OTP_RATE_LIMIT_MAX_SENDS, OTP_RATE_LIMIT_WINDOW_MINUTES, OTP_TTL_MINUTES, OTP_VERIFY_MAX_ATTEMPTS, PENDING_CHECKOUT_TTL_DAYS, PENDING_EMAIL_TTL_HOURS, PENDING_ONBOARDING_TTL_DAYS, REGISTRATION_STEP_BY_STATUS } from "@saasicat/types";
59
62
 
60
63
  // src/registration/pricing.ts
@@ -173,7 +176,7 @@ var PendingRegistrationService = class _PendingRegistrationService {
173
176
  });
174
177
  } catch (error) {
175
178
  const message = error instanceof Error ? error.message : String(error);
176
- this.logger.warn(`Audit-Log fehlgeschlagen (${eventType}): ${message}`);
179
+ this.logger.warn(`Audit log write failed (${eventType}): ${message}`);
177
180
  }
178
181
  }
179
182
  /** Public signup plan list for step 3 (frontend `usePublicPlans()`). */
@@ -183,25 +186,17 @@ var PendingRegistrationService = class _PendingRegistrationService {
183
186
  async selectPlan(input, context) {
184
187
  const pending = await this.repo.findById(input.pendingRegistrationId);
185
188
  if (!pending) {
186
- throw new NotFoundException({
187
- code: "PENDING_REGISTRATION_NOT_FOUND"
188
- });
189
+ throw new NotFoundException(codedError(REGISTRATION_ERROR_CODES.PENDING_REGISTRATION_NOT_FOUND));
189
190
  }
190
191
  if (pending.expiresAt.getTime() < Date.now()) {
191
- throw new BadRequestException({
192
- code: "PENDING_REGISTRATION_EXPIRED"
193
- });
192
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.PENDING_REGISTRATION_EXPIRED));
194
193
  }
195
194
  if (pending.status !== "EMAIL_VERIFIED" && pending.status !== "PLAN_SELECTED" && pending.status !== "CHECKOUT_STARTED") {
196
- throw new BadRequestException({
197
- code: "INVALID_REGISTRATION_STATE"
198
- });
195
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.INVALID_REGISTRATION_STATE));
199
196
  }
200
197
  const plan = await this.planCatalog.findPublicSignupPlan(input.planId);
201
198
  if (!plan) {
202
- throw new BadRequestException({
203
- code: "PLAN_NOT_AVAILABLE"
204
- });
199
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.PLAN_NOT_AVAILABLE));
205
200
  }
206
201
  const now = /* @__PURE__ */ new Date();
207
202
  const expiresAt = new Date(now.getTime() + PENDING_ONBOARDING_TTL_DAYS * 24 * 60 * 60 * 1e3);
@@ -224,30 +219,20 @@ var PendingRegistrationService = class _PendingRegistrationService {
224
219
  async startCheckout(input, context) {
225
220
  const pending = await this.repo.findById(input.pendingRegistrationId);
226
221
  if (!pending) {
227
- throw new NotFoundException({
228
- code: "PENDING_REGISTRATION_NOT_FOUND"
229
- });
222
+ throw new NotFoundException(codedError(REGISTRATION_ERROR_CODES.PENDING_REGISTRATION_NOT_FOUND));
230
223
  }
231
224
  if (pending.expiresAt.getTime() < Date.now()) {
232
- throw new BadRequestException({
233
- code: "PENDING_REGISTRATION_EXPIRED"
234
- });
225
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.PENDING_REGISTRATION_EXPIRED));
235
226
  }
236
227
  if (pending.status !== "PLAN_SELECTED" && pending.status !== "CHECKOUT_STARTED") {
237
- throw new BadRequestException({
238
- code: "PLAN_NOT_SELECTED"
239
- });
228
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.PLAN_NOT_SELECTED));
240
229
  }
241
230
  if (!pending.selectedPlanId) {
242
- throw new BadRequestException({
243
- code: "PLAN_NOT_SELECTED"
244
- });
231
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.PLAN_NOT_SELECTED));
245
232
  }
246
233
  const plan = await this.planCatalog.findPublicSignupPlan(pending.selectedPlanId);
247
234
  if (!plan) {
248
- throw new BadRequestException({
249
- code: "PLAN_NOT_AVAILABLE"
250
- });
235
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.PLAN_NOT_AVAILABLE));
251
236
  }
252
237
  const session = await this.paymentProvider.createCheckoutSession({
253
238
  pendingRegistrationId: pending.id,
@@ -298,7 +283,7 @@ var PendingRegistrationService = class _PendingRegistrationService {
298
283
  rawPayload: input.rawPayload
299
284
  });
300
285
  if (!claimed) {
301
- this.logger.warn(`Idempotenz: PaymentEvent ${input.eventId} bereits verarbeitet \u2014 Duplikat verworfen.`);
286
+ this.logger.warn(`Idempotency: payment event ${input.eventId} already processed \u2014 duplicate discarded.`);
302
287
  await this.record("PAYMENT_DUPLICATE_IGNORED", null, context, {
303
288
  eventId: input.eventId,
304
289
  sessionId: input.sessionId
@@ -348,7 +333,7 @@ var PendingRegistrationService = class _PendingRegistrationService {
348
333
  tenantId: result.tenantId,
349
334
  subscriptionId: result.subscriptionId
350
335
  });
351
- this.logger.log(`Aktivierung erfolgreich: pending=${pending.id} \u2192 user=${result.userId} tenant=${result.tenantId} subscription=${result.subscriptionId}`);
336
+ this.logger.log(`Activation succeeded: pending=${pending.id} \u2192 user=${result.userId} tenant=${result.tenantId} subscription=${result.subscriptionId}`);
352
337
  return {
353
338
  activated: true,
354
339
  result
@@ -376,11 +361,11 @@ var PendingRegistrationService = class _PendingRegistrationService {
376
361
  await this.repo.delete(pending.id);
377
362
  } catch (error) {
378
363
  const message = error instanceof Error ? error.message : String(error);
379
- this.logger.warn(`Cleanup: PendingRegistration ${pending.id} konnte nicht geloescht werden (${message}).`);
364
+ this.logger.warn(`Cleanup: pending registration ${pending.id} could not be deleted (${message}).`);
380
365
  }
381
366
  }
382
367
  if (expired.length > 0) {
383
- this.logger.log(`Cleanup: ${expired.length} abgelaufene PendingRegistrations geloescht.`);
368
+ this.logger.log(`Cleanup: ${expired.length} expired PendingRegistrations deleted.`);
384
369
  }
385
370
  return {
386
371
  deleted: expired.length,
@@ -390,7 +375,7 @@ var PendingRegistrationService = class _PendingRegistrationService {
390
375
  async start(input, context) {
391
376
  const email = normalizeEmail(input.email);
392
377
  if (await this.userLookup.hasActiveUser(email)) {
393
- this.logger.log(`register/start fuer aktiven Account ignoriert (${email}).`);
378
+ this.logger.log(`register/start ignored for an active account (${email}).`);
394
379
  await this.record("REGISTRATION_NEUTRAL_ACTIVE_USER", null, context);
395
380
  return {
396
381
  neutral: true
@@ -456,9 +441,7 @@ var PendingRegistrationService = class _PendingRegistrationService {
456
441
  await this.record("OTP_VERIFY_FAILED", null, context, {
457
442
  reason: "unknown_email"
458
443
  });
459
- throw new BadRequestException({
460
- code: "OTP_INVALID"
461
- });
444
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.OTP_INVALID));
462
445
  }
463
446
  if (pending.status !== "PENDING_EMAIL_VERIFICATION") {
464
447
  return {
@@ -471,43 +454,33 @@ var PendingRegistrationService = class _PendingRegistrationService {
471
454
  await this.record("OTP_VERIFY_FAILED", pending.id, context, {
472
455
  reason: "no_otp_set"
473
456
  });
474
- throw new BadRequestException({
475
- code: "OTP_INVALID"
476
- });
457
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.OTP_INVALID));
477
458
  }
478
459
  if (pending.otpExpiresAt.getTime() < Date.now()) {
479
460
  await this.record("OTP_VERIFY_FAILED", pending.id, context, {
480
461
  reason: "expired"
481
462
  });
482
- throw new BadRequestException({
483
- code: "OTP_EXPIRED"
484
- });
463
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.OTP_EXPIRED));
485
464
  }
486
465
  const maxAttempts = resolveOtpVerifyMaxAttempts();
487
466
  if (pending.otpAttemptCount >= maxAttempts) {
488
467
  await this.record("OTP_VERIFY_FAILED", pending.id, context, {
489
468
  reason: "locked"
490
469
  });
491
- throw new BadRequestException({
492
- code: "OTP_LOCKED"
493
- });
470
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.OTP_LOCKED));
494
471
  }
495
472
  const attemptCount = await this.repo.incrementOtpAttemptCount(pending.id);
496
473
  if (attemptCount > maxAttempts) {
497
474
  await this.record("OTP_VERIFY_FAILED", pending.id, context, {
498
475
  reason: "locked"
499
476
  });
500
- throw new BadRequestException({
501
- code: "OTP_LOCKED"
502
- });
477
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.OTP_LOCKED));
503
478
  }
504
479
  if (!verifyOtpCode(pending.otpHash, code)) {
505
480
  await this.record("OTP_VERIFY_FAILED", pending.id, context, {
506
481
  reason: "wrong_code"
507
482
  });
508
- throw new BadRequestException({
509
- code: "OTP_INVALID"
510
- });
483
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.OTP_INVALID));
511
484
  }
512
485
  const now = /* @__PURE__ */ new Date();
513
486
  const updated = await this.repo.update(pending.id, {
@@ -549,7 +522,7 @@ var PendingRegistrationService = class _PendingRegistrationService {
549
522
  const windowMs = OTP_RATE_LIMIT_WINDOW_MINUTES * 60 * 1e3;
550
523
  const withinWindow = pending.lastOtpSentAt !== null && now.getTime() - pending.lastOtpSentAt.getTime() < windowMs;
551
524
  if (withinWindow && pending.otpSendCount >= OTP_RATE_LIMIT_MAX_SENDS) {
552
- this.logger.warn(`OTP-Rate-Limit erreicht fuer ${pending.email} \u2014 Send wird still verworfen.`);
525
+ this.logger.warn(`OTP rate limit reached for ${pending.email} \u2014 send is being skipped.`);
553
526
  return;
554
527
  }
555
528
  const code = generateOtpCode();
@@ -577,7 +550,7 @@ var PendingRegistrationService = class _PendingRegistrationService {
577
550
  });
578
551
  } catch (error) {
579
552
  const message = error instanceof Error ? error.message : String(error);
580
- this.logger.error(`OTP-Mail an ${pending.email} fehlgeschlagen: ${message}`);
553
+ this.logger.error(`OTP email to ${pending.email} failed: ${message}`);
581
554
  }
582
555
  }
583
556
  /**
@@ -604,7 +577,7 @@ var PendingRegistrationService = class _PendingRegistrationService {
604
577
  });
605
578
  } catch (error) {
606
579
  const message = error instanceof Error ? error.message : String(error);
607
- this.logger.error(`Resume-Mail an ${pending.email} fehlgeschlagen: ${message}`);
580
+ this.logger.error(`Resume email to ${pending.email} failed: ${message}`);
608
581
  }
609
582
  }
610
583
  /**
@@ -618,28 +591,20 @@ var PendingRegistrationService = class _PendingRegistrationService {
618
591
  */
619
592
  async resumeWithToken(input, context) {
620
593
  if (!this.resumeTokenSigner) {
621
- throw new BadRequestException({
622
- code: "RESUME_NOT_CONFIGURED"
623
- });
594
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.RESUME_NOT_CONFIGURED));
624
595
  }
625
596
  let decoded;
626
597
  try {
627
598
  decoded = await this.resumeTokenSigner.verify(input.token);
628
599
  } catch {
629
- throw new BadRequestException({
630
- code: "RESUME_TOKEN_INVALID"
631
- });
600
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.RESUME_TOKEN_INVALID));
632
601
  }
633
602
  const pending = await this.repo.findById(decoded.pendingRegistrationId);
634
603
  if (!pending) {
635
- throw new BadRequestException({
636
- code: "RESUME_TOKEN_INVALID"
637
- });
604
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.RESUME_TOKEN_INVALID));
638
605
  }
639
606
  if (pending.expiresAt.getTime() < Date.now()) {
640
- throw new BadRequestException({
641
- code: "PENDING_REGISTRATION_EXPIRED"
642
- });
607
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.PENDING_REGISTRATION_EXPIRED));
643
608
  }
644
609
  await this.record("REGISTRATION_NEUTRAL_REPLAY", pending.id, context, {
645
610
  via: "resume_token",
@@ -667,9 +632,7 @@ var PendingRegistrationService = class _PendingRegistrationService {
667
632
  /** Returns the (app-specific) configurator catalog. */
668
633
  async getConfiguratorCatalog() {
669
634
  if (!this.configuratorLookup) {
670
- throw new BadRequestException({
671
- code: "CONFIGURATOR_NOT_CONFIGURED"
672
- });
635
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.CONFIGURATOR_NOT_CONFIGURED));
673
636
  }
674
637
  return this.configuratorLookup.getCatalog();
675
638
  }
@@ -686,32 +649,22 @@ var PendingRegistrationService = class _PendingRegistrationService {
686
649
  */
687
650
  async saveConfiguration(input, context) {
688
651
  if (!this.configuratorLookup) {
689
- throw new BadRequestException({
690
- code: "CONFIGURATOR_NOT_CONFIGURED"
691
- });
652
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.CONFIGURATOR_NOT_CONFIGURED));
692
653
  }
693
654
  const pending = await this.repo.findById(input.pendingRegistrationId);
694
655
  if (!pending) {
695
- throw new NotFoundException({
696
- code: "PENDING_REGISTRATION_NOT_FOUND"
697
- });
656
+ throw new NotFoundException(codedError(REGISTRATION_ERROR_CODES.PENDING_REGISTRATION_NOT_FOUND));
698
657
  }
699
658
  if (pending.expiresAt.getTime() < Date.now()) {
700
- throw new BadRequestException({
701
- code: "PENDING_REGISTRATION_EXPIRED"
702
- });
659
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.PENDING_REGISTRATION_EXPIRED));
703
660
  }
704
661
  if (pending.status !== "EMAIL_VERIFIED" && pending.status !== "PLAN_SELECTED" && pending.status !== "CHECKOUT_STARTED") {
705
- throw new BadRequestException({
706
- code: "INVALID_REGISTRATION_STATE"
707
- });
662
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.INVALID_REGISTRATION_STATE));
708
663
  }
709
664
  const catalog = await this.configuratorLookup.getCatalog();
710
665
  const model = catalog.models.find((m) => m.id === input.selection.modelId);
711
666
  if (!model) {
712
- throw new BadRequestException({
713
- code: "MODEL_NOT_AVAILABLE"
714
- });
667
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.MODEL_NOT_AVAILABLE));
715
668
  }
716
669
  let promoEval;
717
670
  if (input.selection.appliedPromoCode && this.promoPreview) {
@@ -775,28 +728,20 @@ var PendingRegistrationService = class _PendingRegistrationService {
775
728
  */
776
729
  async previewConfigPromo(pendingRegistrationId, code) {
777
730
  if (!this.configuratorLookup) {
778
- throw new BadRequestException({
779
- code: "CONFIGURATOR_NOT_CONFIGURED"
780
- });
731
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.CONFIGURATOR_NOT_CONFIGURED));
781
732
  }
782
733
  const pending = await this.repo.findById(pendingRegistrationId);
783
734
  if (!pending) {
784
- throw new NotFoundException({
785
- code: "PENDING_REGISTRATION_NOT_FOUND"
786
- });
735
+ throw new NotFoundException(codedError(REGISTRATION_ERROR_CODES.PENDING_REGISTRATION_NOT_FOUND));
787
736
  }
788
737
  const config = pending.configJson;
789
738
  if (!config) {
790
- throw new BadRequestException({
791
- code: "CONFIG_NOT_SAVED"
792
- });
739
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.CONFIG_NOT_SAVED));
793
740
  }
794
741
  const catalog = await this.configuratorLookup.getCatalog();
795
742
  const model = catalog.models.find((m) => m.id === config.modelId);
796
743
  if (!model) {
797
- throw new BadRequestException({
798
- code: "MODEL_NOT_AVAILABLE"
799
- });
744
+ throw new BadRequestException(codedError(REGISTRATION_ERROR_CODES.MODEL_NOT_AVAILABLE));
800
745
  }
801
746
  let promoEval;
802
747
  if (code && this.promoPreview) {
@@ -854,7 +799,7 @@ var PendingRegistrationService = class _PendingRegistrationService {
854
799
  return candidate;
855
800
  }
856
801
  }
857
- throw new Error(`Konnte keinen freien Slug fuer "${tenantName}" finden.`);
802
+ throw new Error(`Could not find a free slug for "${tenantName}".`);
858
803
  }
859
804
  };
860
805
  PendingRegistrationService = _ts_decorate([
@@ -979,7 +924,7 @@ var RegistrationCleanupCron = class _RegistrationCleanupCron {
979
924
  }
980
925
  }
981
926
  if (totalDeleted > 0) {
982
- this.logger.log(`PendingRegistration-Cleanup abgeschlossen: ${totalDeleted} geloescht in ${iterations} Batches.`);
927
+ this.logger.log(`Pending registration cleanup finished: ${totalDeleted} deleted in ${iterations} batches.`);
983
928
  }
984
929
  }
985
930
  };
@@ -1001,6 +946,7 @@ RegistrationCleanupCron = _ts_decorate2([
1001
946
 
1002
947
  // src/registration/ip-rate-limit.guard.ts
1003
948
  import { HttpException, HttpStatus, Injectable as Injectable3 } from "@nestjs/common";
949
+ import { REGISTRATION_ERROR_CODES as REGISTRATION_ERROR_CODES2 } from "@saasicat/types";
1004
950
  function _ts_decorate3(decorators, target, key, desc) {
1005
951
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1006
952
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1029,9 +975,14 @@ var BaseIpRateLimitGuard = class {
1029
975
  const key = ipFingerprint(req);
1030
976
  if (this.exceeded(key)) {
1031
977
  throw new HttpException({
1032
- code: "RATE_LIMITED",
1033
- reason: this.options.name,
1034
- retryAfterSeconds: Math.ceil(this.options.windowMs / 1e3)
978
+ ...codedError(REGISTRATION_ERROR_CODES2.RATE_LIMITED, {
979
+ retryAfterSeconds: Math.ceil(this.options.windowMs / 1e3)
980
+ }),
981
+ retryAfterSeconds: Math.ceil(this.options.windowMs / 1e3),
982
+ // Superseded by `code`. Note this carries the limiter name,
983
+ // not the code — the two emitters of RATE_LIMITED disagree
984
+ // on what `reason` means, which is why it is going away.
985
+ reason: this.options.name
1035
986
  }, HttpStatus.TOO_MANY_REQUESTS);
1036
987
  }
1037
988
  return true;
@@ -1173,7 +1124,7 @@ _ts_decorate5([
1173
1124
  IsOptional(),
1174
1125
  IsString(),
1175
1126
  Matches(/^[a-z0-9-]+$/, {
1176
- message: "tenantSlug darf nur Kleinbuchstaben, Ziffern und Bindestriche enthalten."
1127
+ message: "tenantSlug may only contain lower-case letters, digits and hyphens."
1177
1128
  }),
1178
1129
  MaxLength(60),
1179
1130
  _ts_metadata4("design:type", String)
@@ -1209,7 +1160,7 @@ _ts_decorate5([
1209
1160
  ], RegisterStartDto.prototype, "password", void 0);
1210
1161
  _ts_decorate5([
1211
1162
  Equals(true, {
1212
- message: "AGB, Datenschutzerklaerung und Auftragsverarbeitung muessen zugestimmt werden."
1163
+ message: "The terms of service, privacy policy and data processing agreement must be accepted."
1213
1164
  }),
1214
1165
  _ts_metadata4("design:type", Boolean)
1215
1166
  ], RegisterStartDto.prototype, "termsAccepted", void 0);
@@ -1249,7 +1200,7 @@ _ts_decorate6([
1249
1200
  _ts_decorate6([
1250
1201
  IsString2(),
1251
1202
  Matches2(/^\d{6}$/, {
1252
- message: "OTP muss 6 Ziffern sein."
1203
+ message: "The OTP must be 6 digits."
1253
1204
  }),
1254
1205
  _ts_metadata5("design:type", String)
1255
1206
  ], VerifyRegistrationOtpDto.prototype, "otp", void 0);