@saasicat/core 1.0.0-rc.22 → 1.0.0-rc.24

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/dist/index.d.ts CHANGED
@@ -24,11 +24,17 @@ interface DateAtOrAfter {
24
24
  interface DateAfter {
25
25
  gt: Date;
26
26
  }
27
- /** `OR` block of the validity window; exactly one date field per variant. */
27
+ /** `OR` block of the validity window; exactly one field per variant. */
28
28
  type ValidityWindowClause = {
29
29
  validFrom: DateAtOrBefore | null;
30
30
  } | {
31
31
  validUntil: DateAtOrAfter | null;
32
+ } | {
33
+ supersededAt: null;
34
+ } | {
35
+ validUntil: {
36
+ not: null;
37
+ };
32
38
  };
33
39
  /** Optional `OR` block for models with `endsAt` (precise admin termination). */
34
40
  type EndsAtClause = {
@@ -60,6 +66,7 @@ interface ActivePlanVersionWhereWithEndsAt {
60
66
  * `publishedAt IS NOT NULL`
61
67
  * `(validFrom IS NULL OR validFrom <= asOf)`
62
68
  * `(validUntil IS NULL OR validUntil >= startOfUtcDay(asOf))` // day-inclusive
69
+ * `(supersededAt IS NULL OR validUntil IS NOT NULL)` // superseded: only with a last day
63
70
  * with `withEndsAt`: additionally `(endsAt IS NULL OR endsAt > asOf)`. // precise
64
71
  *
65
72
  * `planId` stays with the caller (repo-specific type). Matching Prisma
@@ -79,6 +86,22 @@ declare function buildActivePlanVersionWhere(asOf: Date, options: {
79
86
  type ActiveVersionWhere = ActivePlanVersionWhere;
80
87
  /** Model-neutral counterpart that additionally checks an `endsAt` timestamp. */
81
88
  type ActiveVersionWhereWithEndsAt = ActivePlanVersionWhereWithEndsAt;
89
+ /** The dates of a version that decide whether it takes bookings, as a row carries them. */
90
+ interface VersionWindow {
91
+ validFrom?: string | Date | null;
92
+ validUntil?: string | Date | null;
93
+ endsAt?: string | Date | null;
94
+ supersededAt?: string | Date | null;
95
+ }
96
+ /**
97
+ * Whether a version that is published takes bookings at `asOf` — the same
98
+ * window {@link buildActivePlanVersionWhere} asks the database for, for a row
99
+ * already read: `validFrom` at or before `asOf`, `validUntil` not before the
100
+ * day of `asOf`, `endsAt` after `asOf`, and a superseded version only within
101
+ * a last day it carries. A date that is absent does not close the window.
102
+ * Whether the row is published is the caller's to know.
103
+ */
104
+ declare function isVersionActiveAt(version: VersionWindow, asOf: Date): boolean;
82
105
  /**
83
106
  * Model-neutral alias for {@link buildActivePlanVersionWhere}. The original
84
107
  * export remains available for backwards compatibility.
@@ -770,9 +793,18 @@ interface SelfServiceBlockedPlans {
770
793
  * passed it in code" attached. The file is read at boot, so an edit lands on
771
794
  * the next restart.
772
795
  */
796
+ /**
797
+ * Whether the operator may retire a plan version for the subscriptions already
798
+ * on it. It reaches contracts customers have made, so it rests on a clause in
799
+ * the operator's terms; `termsConfirmed` states that they carry it.
800
+ */
801
+ interface OrderlyRetirementSettings {
802
+ termsConfirmed: boolean;
803
+ }
773
804
  interface PlanCatalogTenantBilling {
774
805
  cancellationNoticeDays: CancellationNoticePeriods;
775
806
  selfServiceBlockedPlans: SelfServiceBlockedPlans;
807
+ orderlyRetirement: OrderlyRetirementSettings;
776
808
  }
777
809
  /**
778
810
  * Who is told when the settings in the file change between two starts.
@@ -975,6 +1007,48 @@ interface PromoCodeValidationLog {
975
1007
  createdAt: string;
976
1008
  }
977
1009
 
1010
+ /**
1011
+ * A tenant's own limits beside its plan — a negotiated contract, a pilot.
1012
+ * `quotas[key]` replaces the plan's value for that quota, `-1` meaning
1013
+ * unlimited; `features` adds to the plan's. Stored as JSON in
1014
+ * `subscriptions.customLimits`.
1015
+ */
1016
+ interface CustomLimits {
1017
+ quotas?: Record<QuotaKey, number>;
1018
+ features?: FeatureKey[];
1019
+ }
1020
+ /**
1021
+ * The keys a stored `customLimits` value is read by. The normative admin API
1022
+ * schema (`CustomLimits` in `@saasicat/spec`) is held to the same list.
1023
+ */
1024
+ declare const CUSTOM_LIMITS_KEYS: readonly ["quotas", "features"];
1025
+ /** What a stored `customLimits` value gave, and what in it went unread. */
1026
+ interface ReadCustomLimits {
1027
+ limits: CustomLimits | null;
1028
+ /**
1029
+ * The parts of the stored value the platform does not read — a key it does
1030
+ * not know, or a `quotas`/`features` of the wrong kind. Empty when every
1031
+ * part was read.
1032
+ */
1033
+ unread: string[];
1034
+ }
1035
+ /**
1036
+ * Reads a stored `customLimits` value the way the platform applies it.
1037
+ *
1038
+ * A shape the platform does not read is reported rather than refused: the
1039
+ * tenant keeps its plan's limits and goes on working, as a limit nothing can
1040
+ * count does not block anybody (`SC-ENTL-010`), and the caller says out loud
1041
+ * which limits were not applied. Each quota value goes through
1042
+ * `readQuotaValue`, the one reading of a quota in JSON.
1043
+ */
1044
+ declare function readCustomLimits(value: unknown): ReadCustomLimits;
1045
+ /**
1046
+ * Reads stored limits for an adapter, and reports what went unread once per
1047
+ * subscription — every entitlement read passes through here, so reporting on
1048
+ * each would bury the one line an operator needs under thousands.
1049
+ */
1050
+ declare function customLimitsReader(report: (line: string) => void): (subscriptionId: string, stored: unknown) => CustomLimits | null;
1051
+
978
1052
  type SubscriptionStatus = 'TRIAL' | 'ACTIVE' | 'PAST_DUE' | 'CANCELED' | 'PENDING_SALES';
979
1053
  interface Subscription {
980
1054
  id: string;
@@ -984,8 +1058,8 @@ interface Subscription {
984
1058
  planVersionId: string;
985
1059
  billingCycle: BillingCycle;
986
1060
  status: SubscriptionStatus;
987
- /** Override per Tenant; unset fields fall back to the PlanVersion. */
988
- customLimits?: Partial<Record<QuotaKey, number>>;
1061
+ /** Override per tenant; unset fields fall back to the PlanVersion. */
1062
+ customLimits?: CustomLimits | null;
989
1063
  /** ENTERPRISE special contract. */
990
1064
  customMonthlyNet?: number | null;
991
1065
  isPilot: boolean;
@@ -996,15 +1070,7 @@ interface Subscription {
996
1070
  canceledAt: string | null;
997
1071
  currentPeriodStart: string | null;
998
1072
  currentPeriodEnd: string | null;
999
- /** Plan version migration (see ROADMAP §6). */
1000
- pendingPlanVersionId: string | null;
1001
- pendingPlanVersionEffectiveAt: string | null;
1002
- pendingPlanVersionAccepted: boolean;
1003
- pendingPlanVersionAcceptedAt: string | null;
1004
- pendingPlanVersionAcceptedByUserId: string | null;
1005
- pendingPlanVersionNotifiedAt: string | null;
1006
- pendingPlanVersionReminderSentAt: string | null;
1007
- /** Plan change at period end (orthogonal to pendingPlanVersionId). */
1073
+ /** Plan change at period end. */
1008
1074
  pendingPlanId: PlanId | null;
1009
1075
  pendingBillingCycle: BillingCycle | null;
1010
1076
  pendingEffectiveAt: string | null;
@@ -1067,7 +1133,7 @@ interface VersionedEntityBase {
1067
1133
  * has become part of the contract. Optional for backwards-compat reasons;
1068
1134
  * the helper defensively interprets `undefined` as `>0` (= frozen). How the
1069
1135
  * adapter counts depends on the version type: PlanVersion via
1070
- * `Subscription.planVersionId` (+ `pendingPlanVersionId`), BundleVersion
1136
+ * `Subscription.planVersionId` (+ `pendingChangeVersionId`), BundleVersion
1071
1137
  * via the respective app-specific Subscription→Bundle binding.
1072
1138
  */
1073
1139
  subscriptionCount?: number;
@@ -1514,7 +1580,7 @@ interface AdminManifest {
1514
1580
  actions?: AuditActionDef[];
1515
1581
  };
1516
1582
  }
1517
- type StandardPageKey = 'dashboard' | 'tenants' | 'subscriptions' | 'promoCodes' | 'plans' | 'audit' | 'users' | 'pilots' | 'discovery' | 'bundles' | 'marketingCatalog' | 'platformEmail' | 'platformEmailHistory' | 'settings';
1583
+ type StandardPageKey = 'dashboard' | 'tenants' | 'subscriptions' | 'promoCodes' | 'plans' | 'audit' | 'users' | 'pilots' | 'discovery' | 'bundles' | 'marketingCatalog' | 'platformEmail' | 'platformEmailHistory' | 'settings' | 'maintenance';
1518
1584
  interface StandardPageDef {
1519
1585
  enabled: boolean;
1520
1586
  requiredCapability?: CapabilityKey;
@@ -1627,7 +1693,13 @@ interface AuditQuery {
1627
1693
  entity?: string;
1628
1694
  entityId?: string;
1629
1695
  action?: string;
1630
- /** Wildcard-capable, e.g. 'cli:*'. */
1696
+ /**
1697
+ * A tag, matched exactly, or a pattern with `*` at its start, its end or
1698
+ * both, matched without regard to case: `cli:*` is every entry from the
1699
+ * command line, `*:ops@example.com:*` every entry of one person, whatever
1700
+ * they acted through. Everything between the stars is literal. A tag reads
1701
+ * `<source>:<email>:<context>`.
1702
+ */
1631
1703
  actorTag?: string;
1632
1704
  from?: string;
1633
1705
  to?: string;
@@ -1780,6 +1852,23 @@ interface PromotionOnPrice {
1780
1852
  */
1781
1853
  declare function promotionOnPrice(promotions: PromotionRow[], targetKey: string, locale: string, cycle: 'monthly' | 'yearly', basePrice: number | null, today?: Date, targetType?: PromotionTargetType): PromotionOnPrice | null;
1782
1854
 
1855
+ /** An amount as written, to the cent. */
1856
+ declare function roundToCents(amount: number): number;
1857
+ /** The terms added as written, exactly, and rounded once to the cent. */
1858
+ declare function sumToCents(...terms: number[]): number;
1859
+ /** The amount in whole cents, rounded once. */
1860
+ declare function toCents(amount: number): number;
1861
+ /** `percent` % of `amount`, to the cent — the discount a percentage takes off. */
1862
+ declare function percentOf(amount: number, percent: number): number;
1863
+ /** `amount × part / whole`, to the cent — a share of a period, say. */
1864
+ declare function prorate(amount: number, part: number, whole: number): number;
1865
+ /** Gross from net at a tax rate in per cent: net × (100 + rate) / 100. */
1866
+ declare function grossFromNet(net: number, vatRate: number): number;
1867
+ /** Net from gross at a tax rate in per cent: gross × 100 / (100 + rate). */
1868
+ declare function netFromGross(gross: number, vatRate: number): number;
1869
+ /** The tax a gross amount includes at a rate in per cent: gross × rate / (100 + rate). */
1870
+ declare function computeIncludedVat(gross: number, vatRate: number): number;
1871
+
1783
1872
  type CheckoutOfferLineItemKind = 'plan' | 'bundle' | 'discount';
1784
1873
  /** Frozen billable line item in the offer. */
1785
1874
  interface CheckoutOfferLineItem {
@@ -2303,6 +2392,79 @@ declare function classifyPlanDiff(oldV: PlanVersionFields, newV: PlanVersionFiel
2303
2392
  */
2304
2393
  declare function classifyBundleVersionDiff(oldV: BundleVersionFields, newV: BundleVersionFields): DiffResult;
2305
2394
 
2395
+ /**
2396
+ * - `improvement` — nothing dearer in any rhythm, no feature missing, no quota
2397
+ * lower. Taken at once and free, the term kept.
2398
+ * - `more-for-more` — nothing missing or lower, but dearer in at least one
2399
+ * rhythm. Taken like an upgrade: at once, the difference prorated. The rule
2400
+ * reads every rhythm, so in the subscriber's own the difference can be zero
2401
+ * or below — monthly 49 to 45 with yearly 490 to 500 is more for more for a
2402
+ * monthly subscriber too.
2403
+ * - `takes-something-away` — a feature missing or a quota lower, whatever the
2404
+ * price. Taken like a downgrade: at the end of the running term, and only
2405
+ * when the subscriber asks for it.
2406
+ * - `same` — the two agree on everything compared: there is nothing to offer.
2407
+ */
2408
+ type VersionOfferClass = 'improvement' | 'more-for-more' | 'takes-something-away' | 'same';
2409
+ /**
2410
+ * A version as the comparison reads it. A price may be absent: that is a
2411
+ * rhythm the version is not sold in, and a rhythm that stops being sold counts
2412
+ * against the candidate like a price that went up.
2413
+ */
2414
+ type VersionOfferFields = BundleVersionFields;
2415
+ interface VersionOffer {
2416
+ readonly class: VersionOfferClass;
2417
+ /** Every difference, bound to candidate, as the catalogue's diff states it. */
2418
+ readonly changes: readonly VersionChange[];
2419
+ }
2420
+ /** One version as a subscriber compares it. A price of null is a rhythm it is not sold in. */
2421
+ interface VersionOfferSide {
2422
+ readonly planVersionId: string;
2423
+ readonly version: number;
2424
+ readonly features: readonly string[];
2425
+ readonly quotas: Readonly<Record<string, number>>;
2426
+ readonly monthlyNet: number | null;
2427
+ readonly yearlyNet: number | null;
2428
+ /** The last day it is sold on, where a successor closes its window — ISO, the whole day included. */
2429
+ readonly validUntil: string | null;
2430
+ /** When an operator ended it, where one did — ISO; from then it is not sold. */
2431
+ readonly endsAt: string | null;
2432
+ }
2433
+ /**
2434
+ * A newer version of the subscriber's plan, offered: the version bound and the
2435
+ * one offered side by side, what kind of offer it is, and when a switch taken
2436
+ * now would take effect — at once, or, for one that takes something away, at
2437
+ * the end of the running term. `GET billing/version-offer` answers with it.
2438
+ */
2439
+ interface VersionOfferView {
2440
+ readonly plan: string;
2441
+ readonly bound: VersionOfferSide;
2442
+ readonly offered: VersionOfferSide;
2443
+ readonly class: Exclude<VersionOfferClass, 'same'>;
2444
+ readonly changes: readonly VersionChange[];
2445
+ /** ISO instant. */
2446
+ readonly takesEffectAt: string;
2447
+ }
2448
+ /**
2449
+ * What taking an offer did: the version left and the one taken, and whether it
2450
+ * applied at once or waits for `takesEffectAt`. `POST billing/version-offer/accept`
2451
+ * answers with it.
2452
+ */
2453
+ interface VersionSwitchResult {
2454
+ readonly class: Exclude<VersionOfferClass, 'same'>;
2455
+ readonly fromPlanVersionId: string;
2456
+ readonly planVersionId: string;
2457
+ readonly immediate: boolean;
2458
+ /** ISO instant. */
2459
+ readonly takesEffectAt: string;
2460
+ }
2461
+ /**
2462
+ * Classifies `candidate` against `bound`. The comparison is the catalogue's
2463
+ * own diff — prices with a rhythm that may be absent, as a bundle version's
2464
+ * are — so an offer and a published version describe their changes alike.
2465
+ */
2466
+ declare function classifyVersionOffer(bound: VersionOfferFields, candidate: VersionOfferFields): VersionOffer;
2467
+
2306
2468
  interface PromoPreviewRequest {
2307
2469
  /** Promo code (case-insensitive, normalized to uppercase in the service). */
2308
2470
  code: string;
@@ -2443,6 +2605,560 @@ interface SettingsDifference {
2443
2605
  after: unknown;
2444
2606
  }
2445
2607
 
2608
+ /** One window, as a store reads it back. */
2609
+ interface MaintenanceWindowRecord {
2610
+ id: string;
2611
+ /** The announced start; null for a window locked without being announced first. */
2612
+ startsAt: Date | null;
2613
+ /** The announced, expected end; null where nobody said. */
2614
+ endsAt: Date | null;
2615
+ /** The operator's words to the tenants, shown as written; null for none. */
2616
+ message: string | null;
2617
+ /** When the window was recorded — announced, or locked at once. */
2618
+ createdAt: Date;
2619
+ /** Who recorded it, as the audit log tags an actor. */
2620
+ createdBy: string;
2621
+ /** When the lock began; null while the window is only announced. */
2622
+ lockedAt: Date | null;
2623
+ lockedBy: string | null;
2624
+ /** When the window was over — unlocked, or cancelled before it locked. */
2625
+ endedAt: Date | null;
2626
+ endedBy: string | null;
2627
+ }
2628
+ /** A window to record: it is open, so it has not ended. */
2629
+ type NewMaintenanceWindow = Omit<MaintenanceWindowRecord, 'id' | 'endedAt' | 'endedBy'>;
2630
+ /**
2631
+ * Where an open window stands. A write names the stage it expects, so that it
2632
+ * cannot move a window that moved on in the meantime — one that another
2633
+ * operator locked, unlocked or cancelled a moment earlier.
2634
+ */
2635
+ type MaintenanceWindowStage = 'announced' | 'locked';
2636
+ /** What a write may change about an open window. */
2637
+ type MaintenanceWindowChanges = Partial<Pick<MaintenanceWindowRecord, 'startsAt' | 'endsAt' | 'message' | 'lockedAt' | 'lockedBy' | 'endedAt' | 'endedBy'>>;
2638
+ /**
2639
+ * What became of a window, read off its record. `cancelled` is a window that
2640
+ * ended without ever locking anybody out.
2641
+ */
2642
+ type MaintenanceWindowStatus = 'announced' | 'locked' | 'ended' | 'cancelled';
2643
+ /**
2644
+ * What SaaSiCat tells the application about a window, so that it can write to
2645
+ * its own users — in its own words, from its own sender, in their language.
2646
+ * The lock itself is not among them: tenants see it in the application.
2647
+ */
2648
+ type MaintenanceWindowEvent = {
2649
+ kind: 'announced';
2650
+ window: MaintenanceWindowRecord;
2651
+ } | {
2652
+ kind: 'rescheduled';
2653
+ window: MaintenanceWindowRecord;
2654
+ /** The window as it was announced before this change. */
2655
+ previous: MaintenanceWindowRecord;
2656
+ } | {
2657
+ kind: 'cancelled';
2658
+ window: MaintenanceWindowRecord;
2659
+ };
2660
+ /** The longest message a window carries; the operator's words, not a document. */
2661
+ declare const MAINTENANCE_MESSAGE_MAX_LENGTH = 500;
2662
+ /**
2663
+ * How long a process may go on reading a lock state it read before. Every
2664
+ * request asks, so each process keeps the answer this long rather than asking
2665
+ * the database per request; the command that locks waits this long before it
2666
+ * returns, so that every process has seen the lock by then.
2667
+ *
2668
+ * Part of the contract between two versions of an application: the command
2669
+ * that locks comes from the release being deployed, while the processes it
2670
+ * waits for may still run the one before. It is safe only while the figure it
2671
+ * waits by is at least theirs, so a later release may raise it and never lower
2672
+ * it.
2673
+ */
2674
+ declare const MAINTENANCE_STATE_MAX_AGE_SECONDS = 5;
2675
+ /**
2676
+ * The manifest capability that announces the maintenance screen. The platform
2677
+ * sets it where it keeps maintenance windows, and the administration offers the
2678
+ * screen — and its lock banner — only where it is set.
2679
+ */
2680
+ declare const MAINTENANCE_CAPABILITY = "maintenance.manage";
2681
+ /**
2682
+ * What a tenant's page is told: nothing, a window ahead, or a lock. Dates are
2683
+ * ISO strings, and `overrun` is the server's answer rather than the browser's,
2684
+ * whose clock is not the one the window was announced by.
2685
+ */
2686
+ type MaintenanceStatusView = {
2687
+ state: 'none';
2688
+ } | {
2689
+ state: 'announced';
2690
+ startsAt: string;
2691
+ endsAt: string;
2692
+ message: string | null;
2693
+ } | {
2694
+ state: 'locked';
2695
+ lockedAt: string;
2696
+ /** The announced end; null where nobody said. */
2697
+ endsAt: string | null;
2698
+ message: string | null;
2699
+ /** The announced end has passed, and the lock still holds. */
2700
+ overrun: boolean;
2701
+ };
2702
+ /** A window as the administration shows it. */
2703
+ interface MaintenanceWindowView {
2704
+ id: string;
2705
+ status: MaintenanceWindowStatus;
2706
+ startsAt: string | null;
2707
+ endsAt: string | null;
2708
+ message: string | null;
2709
+ createdAt: string;
2710
+ createdBy: string;
2711
+ lockedAt: string | null;
2712
+ lockedBy: string | null;
2713
+ endedAt: string | null;
2714
+ endedBy: string | null;
2715
+ /** Locked, and past its announced end. */
2716
+ overrun: boolean;
2717
+ /** Announced, never locked, and past its announced end: tenants no longer see it. */
2718
+ lapsed: boolean;
2719
+ }
2720
+ /** `GET /admin/maintenance`: the open window, if any, and the ones before it. */
2721
+ interface MaintenanceOverview {
2722
+ open: MaintenanceWindowView | null;
2723
+ /** Newest first, the open one included. */
2724
+ recent: MaintenanceWindowView[];
2725
+ /** How long a lock takes to reach every process. */
2726
+ takesEffectWithinSeconds: number;
2727
+ }
2728
+ /** `POST /admin/maintenance/lock`: the locked window, and whether it was locked before. */
2729
+ interface MaintenanceLockView {
2730
+ window: MaintenanceWindowView;
2731
+ /** The lock already held; nothing was changed. */
2732
+ alreadyLocked: boolean;
2733
+ /** How long the lock takes to reach every process. */
2734
+ takesEffectWithinSeconds: number;
2735
+ }
2736
+ /** `POST /admin/maintenance/unlock`: the window it ended, or the open one it left alone. */
2737
+ interface MaintenanceUnlockView {
2738
+ window: MaintenanceWindowView | null;
2739
+ /** Whether a lock held and was ended by this call. */
2740
+ wasLocked: boolean;
2741
+ }
2742
+
2743
+ /** What the switch costs, per period of the subscriber's rhythm. */
2744
+ interface RetirementSwitchTerms {
2745
+ /** The replacement's net price. */
2746
+ readonly priceNet: number;
2747
+ /**
2748
+ * Where the replacement costs more: the price the subscriber goes on paying,
2749
+ * the difference that is held, and the last day it is held — the day before
2750
+ * the date they were told. Null where it costs the same or less: its price
2751
+ * then applies from the subscriber's next period.
2752
+ */
2753
+ readonly held: {
2754
+ readonly priceNet: number;
2755
+ readonly amountNet: number;
2756
+ readonly lastDay: string;
2757
+ } | null;
2758
+ }
2759
+ /**
2760
+ * What switching to the replacement now costs in `billingCycle`, or null where
2761
+ * the replacement has no price in it.
2762
+ */
2763
+ declare function retirementSwitchTerms(notice: Pick<VersionRetiredNotice, 'retired' | 'replacement' | 'lastDayToCancel'>, billingCycle: string): RetirementSwitchTerms | null;
2764
+
2765
+ /** A version, as an announcement names it. */
2766
+ interface RetiredVersionRef {
2767
+ readonly planVersionId: string;
2768
+ readonly planKey: string;
2769
+ readonly version: number;
2770
+ }
2771
+ /** An announcement as it is kept: one per retirement, whoever it reaches. */
2772
+ interface VersionRetirementRecord {
2773
+ readonly id: string;
2774
+ /** The version retired. */
2775
+ readonly retired: RetiredVersionRef;
2776
+ /** The version its subscriptions continue on, named by the operator. */
2777
+ readonly replacement: RetiredVersionRef;
2778
+ readonly announcedAt: Date;
2779
+ /** Who announced it — an actor tag, as the audit log writes it. */
2780
+ readonly announcedBy: string;
2781
+ }
2782
+ type NewVersionRetirement = Omit<VersionRetirementRecord, 'id'>;
2783
+ /**
2784
+ * The manifest capability that announces retiring a version. The platform sets
2785
+ * it where it serves the routes, and the admin UI offers the action only where
2786
+ * it is set. Whether the operator's terms allow it is a blocker the preview
2787
+ * reports, so an operator who has not confirmed them learns what is missing.
2788
+ */
2789
+ declare const VERSION_RETIREMENT_CAPABILITY = "planVersions.retire";
2790
+ /**
2791
+ * One of the two versions of a retirement, as the subscriber compares them:
2792
+ * price in each rhythm, quotas and features, with the plan it belongs to — the
2793
+ * replacement may be a version of another plan.
2794
+ */
2795
+ interface RetirementSide extends VersionOfferSide {
2796
+ readonly planKey: string;
2797
+ }
2798
+ /**
2799
+ * A plan version a subscription is on is being retired, and the subscription
2800
+ * continues on the named replacement from its effective date. Sent once per
2801
+ * subscription and retirement, when the retirement is announced, and kept as
2802
+ * it was sent: the record shows what the subscriber was told, prices included.
2803
+ */
2804
+ interface VersionRetiredNotice {
2805
+ readonly kind: 'version-retired';
2806
+ readonly tenantId: string;
2807
+ readonly subscriptionId: string;
2808
+ readonly retirementId: string;
2809
+ readonly retired: RetirementSide;
2810
+ readonly replacement: RetirementSide;
2811
+ /** Every difference, retired to replacement, as the catalogue's diff states it. */
2812
+ readonly changes: readonly VersionChange[];
2813
+ /**
2814
+ * The rhythm the subscription is billed in when the retirement takes
2815
+ * effect — a change of rhythm scheduled by then included — which says
2816
+ * which of the two prices applies.
2817
+ */
2818
+ readonly billingCycle: string;
2819
+ /** ISO 8601. */
2820
+ readonly effectiveAt: string;
2821
+ /** ISO date (`YYYY-MM-DD`), UTC. */
2822
+ readonly lastDayToCancel: string;
2823
+ }
2824
+ /**
2825
+ * The one reminder of a retirement, where staying put costs the subscription
2826
+ * something (`SC-SUB-034`): sent 14 days before the retirement takes effect,
2827
+ * once per subscription and retired version. It says again what the
2828
+ * retirement notice said, and what a switch taken now would cost. Its
2829
+ * `billingCycle` is still the rhythm billed at the date, read again from the
2830
+ * subscription as it stands when the reminder is sent: a change of rhythm
2831
+ * scheduled since the announcement is in it.
2832
+ */
2833
+ interface VersionRetirementReminder extends Omit<VersionRetiredNotice, 'kind'> {
2834
+ readonly kind: 'version-retirement-reminder';
2835
+ /**
2836
+ * What switching to the replacement now costs in the subscription's rhythm
2837
+ * (`SC-SUB-032`), or null where it cannot switch now: in a trial, with a
2838
+ * change scheduled, or where either plan is held for a special contract.
2839
+ */
2840
+ readonly switchTerms: RetirementSwitchTerms | null;
2841
+ }
2842
+ /**
2843
+ * Why a retirement does not reach a subscription on the version:
2844
+ *
2845
+ * - `ended` — the subscription is over.
2846
+ * - `cancelled-before` — its cancellation lands by the effective date.
2847
+ * - `changes-before` — a scheduled change moves it to another plan, or to
2848
+ * another version of this one, by then.
2849
+ * - `no-term` — it carries no date a term can be counted from.
2850
+ * - `already-told` — an earlier announcement of this version reached it, and
2851
+ * stands.
2852
+ */
2853
+ type RetirementSkipReason = 'ended' | 'cancelled-before' | 'changes-before' | 'no-term' | 'already-told';
2854
+ /** A refusal as the preview reports it and the announcement throws it. */
2855
+ interface RetirementBlocker {
2856
+ readonly code: string;
2857
+ readonly message: string;
2858
+ readonly params: Record<string, string | number>;
2859
+ }
2860
+ /** A subscription a retirement reaches, with what it means there. */
2861
+ interface RetirementReachedRow {
2862
+ readonly tenantId: string;
2863
+ readonly subscriptionId: string;
2864
+ readonly status: string;
2865
+ /** The rhythm it is billed in when the retirement takes effect. */
2866
+ readonly billingCycle: string;
2867
+ /** ISO 8601. */
2868
+ readonly effectiveAt: string;
2869
+ /** ISO date, UTC. */
2870
+ readonly lastDayToCancel: string;
2871
+ /** Reached by another retirement within the last twelve months: the announcement waits. */
2872
+ readonly reachedRecently: boolean;
2873
+ }
2874
+ /** A subscription on the version a retirement does not reach, and why. */
2875
+ interface RetirementSkippedRow {
2876
+ readonly tenantId: string;
2877
+ readonly subscriptionId: string;
2878
+ readonly reason: RetirementSkipReason;
2879
+ }
2880
+ /** What announcing a retirement now would do. */
2881
+ interface RetirementPreview {
2882
+ readonly retired: RetirementSide;
2883
+ readonly replacement: RetirementSide;
2884
+ /** Every difference, retired to replacement, as the subscribers will be told it. */
2885
+ readonly changes: readonly VersionChange[];
2886
+ /** ISO 8601: the moment it is computed for. */
2887
+ readonly asOf: string;
2888
+ readonly reached: readonly RetirementReachedRow[];
2889
+ readonly skipped: readonly RetirementSkippedRow[];
2890
+ /** Empty where it may be announced as it stands. */
2891
+ readonly blockers: readonly RetirementBlocker[];
2892
+ }
2893
+ /** What an announcement did. */
2894
+ interface RetirementAnnounced {
2895
+ readonly retirement: VersionRetirementRecord;
2896
+ /** Notices sent now; the rest are sent by the next run. */
2897
+ readonly told: number;
2898
+ readonly failed: number;
2899
+ }
2900
+ /**
2901
+ * How far a retirement has come, counted over the subscriptions it reached
2902
+ * (`SC-SUB-033`).
2903
+ */
2904
+ interface RetirementProgress {
2905
+ /** Off the retired version: moved at their date, switched early, or changed on their own. */
2906
+ readonly moved: number;
2907
+ /** Still on the retired version, before their date. */
2908
+ readonly waiting: number;
2909
+ /** Still on the retired version after their date: the run has not moved them yet, or cannot. */
2910
+ readonly overdue: number;
2911
+ /** Still on the retired version, and ended by their date: there is nothing to move. */
2912
+ readonly ended: number;
2913
+ /**
2914
+ * Still on the retired version, and not told yet: their notice has reached
2915
+ * nobody, so their date is not set and nothing moves them (`SC-SUB-036`).
2916
+ */
2917
+ readonly notTold: number;
2918
+ /**
2919
+ * Reminded 14 days before their date (`SC-SUB-034`) — counted beside the
2920
+ * four above, not instead of one: a subscription reminded moves later.
2921
+ */
2922
+ readonly reminded: number;
2923
+ }
2924
+ /** An announcement as the administration lists it, with how far it has come. */
2925
+ interface VersionRetirementView extends VersionRetirementRecord {
2926
+ readonly progress: RetirementProgress;
2927
+ }
2928
+ /** What one run of the moves at the date did. */
2929
+ interface RetirementMoveRun {
2930
+ /** Subscriptions moved onto their replacement, with the contract the move writes. */
2931
+ readonly moved: number;
2932
+ /** Moves that could not be made, and are tried again by the next run. */
2933
+ readonly failed: number;
2934
+ }
2935
+ /** What switching to the replacement before the date did (`SC-SUB-032`). */
2936
+ interface RetirementSwitchResult {
2937
+ readonly fromPlanVersionId: string;
2938
+ readonly planVersionId: string;
2939
+ /** ISO date of the last day the price is held, or null where none is. */
2940
+ readonly heldUntilDay: string | null;
2941
+ }
2942
+
2943
+ /** What a notice is about. */
2944
+ type SubscriptionNoticeKind = 'version-offered' | 'version-retired' | 'version-retirement-reminder';
2945
+ /** Which notice: there is one per subscription, kind and subject. */
2946
+ interface SubscriptionNoticeKey {
2947
+ readonly tenantId: string;
2948
+ readonly subscriptionId: string;
2949
+ readonly kind: SubscriptionNoticeKind;
2950
+ /**
2951
+ * What it is about. For `version-offered`, the id of the version offered;
2952
+ * for `version-retired`, the id of the version retired — so a subscription
2953
+ * is told of a version's retirement once, whichever announcement it came
2954
+ * with. The announcement is in the notice as `retirementId`. For
2955
+ * `version-retirement-reminder`, the id of the version retired as well: a
2956
+ * subscription is reminded of a version's retirement once.
2957
+ */
2958
+ readonly subject: string;
2959
+ }
2960
+ /** Who a notice went to and how, in the application's own terms. */
2961
+ interface SubscriptionNoticeDelivery {
2962
+ /** The addresses, or user ids, it was sent to. Empty where nobody could be told. */
2963
+ readonly recipients: readonly string[];
2964
+ /** The way it went — `email`, `in-app`, whatever the application calls it. */
2965
+ readonly channel: string;
2966
+ }
2967
+ /** A notice as it is kept. */
2968
+ interface SubscriptionNoticeRecord extends SubscriptionNoticeKey {
2969
+ readonly id: string;
2970
+ /** The notice as it was last handed to the application: a `SubscriptionNotice`. */
2971
+ readonly content: unknown;
2972
+ readonly createdAt: Date;
2973
+ /**
2974
+ * When a run took it on to deliver it. Null where no run holds it — before
2975
+ * the first attempt, and again after an attempt that failed.
2976
+ */
2977
+ readonly claimedAt: Date | null;
2978
+ /** When the application confirmed it sent it; null until then. */
2979
+ readonly deliveredAt: Date | null;
2980
+ /** Who it went to and how; set together with `deliveredAt`. */
2981
+ readonly delivery: SubscriptionNoticeDelivery | null;
2982
+ }
2983
+ /**
2984
+ * A newer version of the subscription's plan is now offered to it. Sent once
2985
+ * per version, when the offer appears beside the plan — the notice and the
2986
+ * offer say the same thing.
2987
+ */
2988
+ interface VersionOfferedNotice {
2989
+ readonly kind: 'version-offered';
2990
+ readonly tenantId: string;
2991
+ readonly subscriptionId: string;
2992
+ /** The offer as `GET billing/version-offer` answers it at the moment the notice is due. */
2993
+ readonly offer: VersionOfferView;
2994
+ }
2995
+ /** A notice handed to the application to send. */
2996
+ type SubscriptionNotice = VersionOfferedNotice | VersionRetiredNotice | VersionRetirementReminder;
2997
+
2998
+ /**
2999
+ * A `subscription_notices` row as either adapter reads it back. The two JSON
3000
+ * columns are `unknown`: the database holds whatever was written to them.
3001
+ */
3002
+ interface CanonicalSubscriptionNoticeRow {
3003
+ id: string;
3004
+ tenantId: string;
3005
+ subscriptionId: string;
3006
+ kind: string;
3007
+ subject: string;
3008
+ content: unknown;
3009
+ createdAt: Date;
3010
+ claimedAt: Date | null;
3011
+ deliveredAt: Date | null;
3012
+ recipients: unknown;
3013
+ channel: string | null;
3014
+ }
3015
+ /**
3016
+ * Reads a row back as a record, naming each column rather than spreading the
3017
+ * row. The kind and the delivery are checked rather than cast: the record is
3018
+ * what a subscriber is later shown they were told, and a value nobody can read
3019
+ * must stop at the read and name its row.
3020
+ */
3021
+ declare function toSubscriptionNoticeRecord(row: CanonicalSubscriptionNoticeRow): SubscriptionNoticeRecord;
3022
+
3023
+ /** The dates of a version that decide its state, as a row carries them. */
3024
+ interface VersionSaleDates extends VersionWindow {
3025
+ readonly publishedAt?: string | Date | null;
3026
+ }
3027
+ /**
3028
+ * Where a version stands. A day is an ISO date (`YYYY-MM-DD`, UTC):
3029
+ * - `scheduled` — published and not on sale yet; `from` is its first day.
3030
+ * - `on-sale` — on sale now; `until` is its last day, where one is known.
3031
+ * - `off-sale` — no longer on sale; `since` is its first day off sale, where
3032
+ * one is known — a version superseded without a last day has none.
3033
+ */
3034
+ type VersionSale = {
3035
+ readonly kind: 'draft';
3036
+ } | {
3037
+ readonly kind: 'scheduled';
3038
+ readonly from: string;
3039
+ } | {
3040
+ readonly kind: 'on-sale';
3041
+ readonly until: string | null;
3042
+ } | {
3043
+ readonly kind: 'off-sale';
3044
+ readonly since: string | null;
3045
+ };
3046
+ type VersionSaleKind = VersionSale['kind'];
3047
+ /** The state of `version` at `now`. */
3048
+ declare function versionSale(version: VersionSaleDates, now: Date): VersionSale;
3049
+ /**
3050
+ * The version on sale at `now` among one plan's or add-on's versions, in the
3051
+ * order the platform picks it: the latest `validFrom`, a version without one
3052
+ * last, then the highest number.
3053
+ */
3054
+ declare function versionOnSale<V extends VersionSaleDates & {
3055
+ readonly version?: number;
3056
+ }>(versions: readonly V[], now: Date): V | null;
3057
+ /**
3058
+ * What a listing shows for a plan or add-on: the version on sale, otherwise
3059
+ * the next one scheduled. A draft is not shown by this.
3060
+ */
3061
+ declare function versionOnSaleOrNext<V extends VersionSaleDates & {
3062
+ readonly version?: number;
3063
+ }>(versions: readonly V[], now: Date): V | null;
3064
+
3065
+ /** A `version_retirements` row as either adapter reads it back. */
3066
+ interface CanonicalVersionRetirementRow {
3067
+ id: string;
3068
+ retiredPlanVersionId: string;
3069
+ retiredPlanKey: string;
3070
+ retiredVersion: number;
3071
+ replacementPlanVersionId: string;
3072
+ replacementPlanKey: string;
3073
+ replacementVersion: number;
3074
+ announcedAt: Date;
3075
+ announcedBy: string;
3076
+ }
3077
+ /** Reads a row back as a record, naming each column rather than spreading the row. */
3078
+ declare function toVersionRetirementRecord(row: CanonicalVersionRetirementRow): VersionRetirementRecord;
3079
+
3080
+ /** A scheduled change as a subscription row holds it. */
3081
+ interface ScheduledChangeColumns {
3082
+ readonly pendingPlan: string | null;
3083
+ readonly pendingBillingCycle: string | null;
3084
+ readonly pendingEffectiveAt: Date | null;
3085
+ readonly pendingChangeVersionId: string | null;
3086
+ }
3087
+ /** No change scheduled: what a plan change writes where it does not keep one. */
3088
+ declare const NO_SCHEDULED_CHANGE: ScheduledChangeColumns;
3089
+ /**
3090
+ * The scheduled-change columns a plan change writes onto `current`: all
3091
+ * cleared, unless the write keeps the change. A kept change is left as it is,
3092
+ * with one exception: a change that only moves the rhythm on the plan being
3093
+ * left — the plan it was scheduled on, no version named — follows the
3094
+ * subscription to its new plan. Recorded as it was, it would take the
3095
+ * subscription back to the plan it left when it comes due.
3096
+ */
3097
+ declare function scheduledChangeAfterWrite(current: {
3098
+ readonly plan: string;
3099
+ } & ScheduledChangeColumns, input: {
3100
+ readonly planId: string;
3101
+ readonly keepsPendingChange?: boolean;
3102
+ }): Partial<ScheduledChangeColumns>;
3103
+
3104
+ /** How many days before a retirement takes effect its reminder is due. */
3105
+ declare const RETIREMENT_REMINDER_LEAD_DAYS = 14;
3106
+ /**
3107
+ * When the reminder of a retirement is due: 14 days before it takes effect, at
3108
+ * the same time of day. UTC has no change of clocks, so that is the effective
3109
+ * day less 14 calendar days.
3110
+ */
3111
+ declare function retirementReminderDueAt(notice: Pick<VersionRetiredNotice, 'effectiveAt'>): Date;
3112
+ /**
3113
+ * Whether the reminder is due at `now`: from its day until the retirement
3114
+ * takes effect. A run that did not happen on the day is caught up by the next
3115
+ * one before the date; after it there is nothing left to remind of.
3116
+ */
3117
+ declare function retirementReminderIsDue(notice: Pick<VersionRetiredNotice, 'effectiveAt'>, now: Date): boolean;
3118
+ /**
3119
+ * Whether staying put costs the subscription something when the retirement
3120
+ * takes effect (D9): the replacement takes a feature or a quota away, or is
3121
+ * dearer in `billingCycle`, the rhythm it is billed in then — or not sold in
3122
+ * it. A price that rises only in another rhythm costs it nothing.
3123
+ */
3124
+ declare function retirementCostsTheSubscription(notice: Pick<VersionRetiredNotice, 'retired' | 'replacement' | 'changes' | 'lastDayToCancel'>, billingCycle: string): boolean;
3125
+
3126
+ /** A `maintenance_windows` row as either adapter reads it back. */
3127
+ type CanonicalMaintenanceWindowRow = MaintenanceWindowRecord;
3128
+ /**
3129
+ * Reads a row back as a record, naming each column rather than spreading the
3130
+ * row: a column a later release adds is not handed to the platform by accident,
3131
+ * and a version reading the table a later release extended reads what it knows.
3132
+ */
3133
+ declare function toMaintenanceWindowRecord(row: CanonicalMaintenanceWindowRow): MaintenanceWindowRecord;
3134
+
3135
+ /** What became of a window. */
3136
+ declare function maintenanceWindowStatusOf(window: MaintenanceWindowRecord): MaintenanceWindowStatus;
3137
+ /** Whether the lock still holds after the end it announced. */
3138
+ declare function isMaintenanceOverrun(window: MaintenanceWindowRecord, now: Date): boolean;
3139
+ /**
3140
+ * Whether an announcement has lapsed: never locked, and its announced end has
3141
+ * passed. Tenants are no longer shown it — past information is noise — while
3142
+ * the operator is, because it still stands in the way of the next one.
3143
+ */
3144
+ declare function isMaintenanceAnnouncementLapsed(window: MaintenanceWindowRecord, now: Date): boolean;
3145
+ /** What a tenant's page is told, given the open window — or none. */
3146
+ declare function maintenanceStatusOf(open: MaintenanceWindowRecord | null, now: Date): MaintenanceStatusView;
3147
+ /** A window as the administration shows it. */
3148
+ declare function maintenanceWindowViewOf(window: MaintenanceWindowRecord, now: Date): MaintenanceWindowView;
3149
+ /**
3150
+ * Seconds until the announced end, for the `Retry-After` of a refused request.
3151
+ * Never less than one: a client told zero would retry at once, in a loop.
3152
+ */
3153
+ declare function maintenanceRetryAfterSeconds(window: MaintenanceWindowRecord, now: Date): number;
3154
+
3155
+ /**
3156
+ * The moment an ISO 8601 date and time names, provided it states its zone —
3157
+ * `2026-10-02T22:00+02:00` or `2026-10-02T20:00Z`. Null for anything else: a
3158
+ * date without a time, a time without a zone, or text that is not a date.
3159
+ */
3160
+ declare function parseZonedInstant(text: string): Date | null;
3161
+
2446
3162
  /**
2447
3163
  * The top-level blocks of `config/saas.yaml` that are the catalogue rather than
2448
3164
  * the configuration, and the format marker.
@@ -2555,11 +3271,8 @@ interface PlanVersionRow extends VersionedEntityBase {
2555
3271
  * `endsAt` is user-initiated: `POST /admin/catalog/plan-versions/:id/terminate`
2556
3272
  * sets the field. When `endsAt < NOW()` the version is no longer live for
2557
3273
  * new bookings — existing subscriptions (P1) stay bound.
2558
- *
2559
- * Optional, because consumer backends add the column additively — if it
2560
- * is missing, there is no end date.
2561
3274
  */
2562
- endsAt?: string | null;
3275
+ endsAt: string | null;
2563
3276
  /** @deprecated Read from `quotas['users']` once available. */
2564
3277
  maxUsers?: number;
2565
3278
  /** @deprecated Legacy field; read from `quotas['vehicles']`. */
@@ -2624,12 +3337,12 @@ interface PlanCatalogReadSnapshot {
2624
3337
  /** Plan stems (deletedAt IS NULL). */
2625
3338
  plans: PlanRow[];
2626
3339
  /**
2627
- * Live PlanVersions: per `planId` (= planKey) the currently published
2628
- * version (publishedAt IS NOT NULL AND supersededAt IS NULL).
2629
- * Apps with plans without a live version take over the plan, but without
2630
- * pricing/quotas — the importer probably emitted a warning.
3340
+ * Per `planId` (= planKey) the version on sale at the moment the snapshot
3341
+ * was asked for — the rule of `PlanRepository.findActivePlanVersion`, so
3342
+ * the catalogue names the version a booking made at that moment binds.
3343
+ * A plan with nothing on sale is taken over without pricing or quotas.
2631
3344
  */
2632
- livePlanVersions: PlanVersionRow[];
3345
+ versionsOnSale: PlanVersionRow[];
2633
3346
  /** Feature catalog entries (deletedAt IS NULL). */
2634
3347
  featureEntries: FeatureCatalogEntryRow[];
2635
3348
  }
@@ -2638,7 +3351,8 @@ interface PlanCatalogReadSnapshot {
2638
3351
  * implement it against their Prisma tables.
2639
3352
  */
2640
3353
  interface PlanCatalogReadSink {
2641
- loadSnapshot(): Promise<PlanCatalogReadSnapshot>;
3354
+ /** The catalogue as it stands at `asOf`. */
3355
+ loadSnapshot(asOf: Date): Promise<PlanCatalogReadSnapshot>;
2642
3356
  }
2643
3357
 
2644
3358
  /**
@@ -2897,13 +3611,34 @@ interface EmailPort {
2897
3611
  }
2898
3612
  /** Adapter for MFA secret persistence. */
2899
3613
  interface MfaPort {
2900
- /** Returns the stored TOTP secret or null. */
3614
+ /**
3615
+ * Returns the stored secret or null. What is stored is the value the
3616
+ * platform handed to `setSecret`: sealed by the installation's
3617
+ * `SecretSealer`, and opened by the platform, not here.
3618
+ */
2901
3619
  getSecret(userId: string): Promise<string | null>;
2902
- /** Persists or deletes (null) the TOTP secret. */
3620
+ /** Persists or deletes (null) the sealed secret, as it is given. */
2903
3621
  setSecret(userId: string, secret: string | null): Promise<void>;
2904
3622
  /** The platform calls this during the mfa-setup command. */
2905
3623
  isEnabled(userId: string): Promise<boolean>;
2906
3624
  }
3625
+ /**
3626
+ * Seals a secret before it is stored and opens it after it is read.
3627
+ *
3628
+ * The TOTP secret of a SuperAdmin is the second factor of the accounts that can
3629
+ * change every tenant's plan, codes and contracts. Stored as it is, whoever
3630
+ * holds a dump, a backup or a replica holds it too, and the second factor adds
3631
+ * nothing against them. The key stays with the installation — in its
3632
+ * configuration, not in the database the sealed value lives in.
3633
+ *
3634
+ * `open` refuses what it did not seal: a value from another key, a format it
3635
+ * does not know, a secret stored before sealing. The platform then treats the
3636
+ * code as wrong and the administrator enrols again.
3637
+ */
3638
+ interface SecretSealer {
3639
+ seal(plain: string): Promise<string>;
3640
+ open(sealed: string): Promise<string>;
3641
+ }
2907
3642
  /**
2908
3643
  * Opaque transaction context. The consumer determines the concrete type
2909
3644
  * (e.g. `Prisma.TransactionClient`). Platform code only passes it
@@ -3034,8 +3769,18 @@ interface PromoCodeRedemptionListItem extends PromoCodeRedemptionRecord {
3034
3769
  */
3035
3770
  interface PromoCodeRepository {
3036
3771
  findById(id: string): Promise<PromoCodeRecord | null>;
3772
+ /**
3773
+ * The code with this name, deleted ones included: a deleted code keeps its
3774
+ * name (`SC-PROMO-028`), so the platform's duplicate check has to see it.
3775
+ * Every caller that redeems or previews checks `deletedAt` itself.
3776
+ */
3037
3777
  findByCode(code: string, tx?: TransactionContext): Promise<PromoCodeRecord | null>;
3038
3778
  findMany(filter: PromoCodeFilter): Promise<PromoCodeRecord[]>;
3779
+ /**
3780
+ * Refuses a name that is taken — by a live or a deleted code — with
3781
+ * `promoCodeTaken`: two creates can pass the platform's duplicate check
3782
+ * together, and the second one's answer is the refusal, not a driver error.
3783
+ */
3039
3784
  create(data: CreatePromoCodeData): Promise<PromoCodeRecord>;
3040
3785
  update(id: string, data: UpdatePromoCodeData): Promise<PromoCodeRecord>;
3041
3786
  softDelete(id: string): Promise<void>;
@@ -3057,7 +3802,8 @@ interface PromoCodeRepository {
3057
3802
  releaseSlot(id: string, tx?: TransactionContext): Promise<void>;
3058
3803
  /**
3059
3804
  * Bulk-expire cron: sets all codes with `validUntil < now` and status
3060
- * ACTIVE/PAUSED to EXPIRED. Returns: number of updated rows.
3805
+ * ACTIVE/PAUSED to EXPIRED, leaving deleted codes as they were — a write
3806
+ * to a row the operator removed. Returns: number of updated rows.
3061
3807
  */
3062
3808
  expireDueCodes(now: Date): Promise<number>;
3063
3809
  }
@@ -3157,7 +3903,14 @@ interface PromoCodeHoldRepository {
3157
3903
  interface PromoCodeRedemptionRepository {
3158
3904
  findBySubscription(subscriptionId: string, tx?: TransactionContext): Promise<PromoCodeRedemptionRecord | null>;
3159
3905
  create(data: Omit<PromoCodeRedemptionRecord, 'id' | 'redeemedAt' | 'status' | 'reversedAt'>, tx?: TransactionContext): Promise<PromoCodeRedemptionRecord>;
3160
- setReversed(id: string, tx?: TransactionContext): Promise<PromoCodeRedemptionRecord>;
3906
+ /**
3907
+ * Marks the redemption reversed while it is not reversed yet, in one
3908
+ * conditional statement, and answers with it; `null` where it already was.
3909
+ * Two reversals of one redemption at the same moment then find one winner,
3910
+ * and only the winner gives the slot back — without the claim both would,
3911
+ * and a limited code would gain a slot it never issued.
3912
+ */
3913
+ setReversed(id: string, tx?: TransactionContext): Promise<PromoCodeRedemptionRecord | null>;
3161
3914
  countByPromoCode(promoCodeId: string, status?: PromoCodeRedemptionStatus): Promise<number>;
3162
3915
  listByPromoCode(promoCodeId: string): Promise<PromoCodeRedemptionListItem[]>;
3163
3916
  expireDueRedemptions(now: Date): Promise<number>;
@@ -3356,8 +4109,21 @@ interface CreateSubscriptionContractData {
3356
4109
  /** What a repository writes: the contract as asked for, with the parties the platform copied. */
3357
4110
  interface NewSubscriptionContractData extends CreateSubscriptionContractData {
3358
4111
  parties: SubscriptionContractParties;
4112
+ /**
4113
+ * The parties are a copy the migration made rather than what was agreed —
4114
+ * set where a successor takes them over from a contract that says so, so
4115
+ * that the copy is not passed off as agreed one contract later. Absent
4116
+ * means `false`.
4117
+ */
4118
+ partiesMigrated?: boolean;
3359
4119
  }
3360
4120
  interface TerminateSubscriptionContractData {
4121
+ /**
4122
+ * The tenant the caller read the contract under. The write matches it as
4123
+ * well as the id, so an id that reaches the call by mistake cannot end
4124
+ * another tenant's contract, whether or not a row policy would stop it.
4125
+ */
4126
+ tenantId: string;
3361
4127
  effectiveUntil: Date;
3362
4128
  /**
3363
4129
  * The terminal status, or `null` to end the contract by date alone.
@@ -3374,6 +4140,25 @@ interface TerminateSubscriptionContractData {
3374
4140
  */
3375
4141
  status: Extract<SubscriptionContractStatus, 'terminated' | 'superseded'> | null;
3376
4142
  }
4143
+ /**
4144
+ * Ends a contract because a successor takes its place — on the condition that
4145
+ * it is still as the caller read it.
4146
+ */
4147
+ interface SupersedeSubscriptionContractData {
4148
+ /**
4149
+ * The tenant the caller read the contract under. The write matches it as
4150
+ * well as the id, so an id that reaches the call by mistake cannot end
4151
+ * another tenant's contract, whether or not a row policy would stop it.
4152
+ */
4153
+ tenantId: string;
4154
+ /** When the successor takes over; written as the contract's `effectiveUntil`. */
4155
+ at: Date;
4156
+ /**
4157
+ * The `effectiveUntil` the caller read. A contract whose end moved since —
4158
+ * a cancellation capped it, or ended it — is not superseded.
4159
+ */
4160
+ readEffectiveUntil: Date | null;
4161
+ }
3377
4162
  interface SubscriptionContractFilter {
3378
4163
  tenantId?: string;
3379
4164
  status?: SubscriptionContractStatus;
@@ -3663,10 +4448,7 @@ interface SubscriptionRecord {
3663
4448
  trialEntitlementPlan?: string | null;
3664
4449
  pendingPlan?: string | null;
3665
4450
  pendingEffectiveAt?: Date | null;
3666
- customLimits?: {
3667
- quotas?: Record<string, number>;
3668
- features?: string[];
3669
- } | null;
4451
+ customLimits?: CustomLimits | null;
3670
4452
  planVersionId: string;
3671
4453
  planVersion: PlanVersionRecord;
3672
4454
  /**
@@ -3710,7 +4492,8 @@ interface SubscriptionRepository {
3710
4492
  findByTenantIdLocked(tenantId: string, tx: TransactionContext): Promise<SubscriptionRecord | null>;
3711
4493
  /**
3712
4494
  * Counts subscriptions that bind a specific PlanVersion — both
3713
- * via the active `planVersionId` and via the scheduled `pendingPlanVersionId`.
4495
+ * via the active `planVersionId` and via the version a scheduled change will
4496
+ * bind (`pendingChangeVersionId`).
3714
4497
  * Needed by the `PlanVersionsService` for the editability decision:
3715
4498
  * a published-but-future PlanVersion stays correctable only as long
3716
4499
  * as no booking references it.
@@ -3720,6 +4503,14 @@ interface SubscriptionRepository {
3720
4503
  * frozen (fail-closed). Implementation hint: count in a single
3721
4504
  * COUNT(*) over the subscription table with an OR over the two
3722
4505
  * FK columns — not in two separate queries, to avoid race conditions.
4506
+ *
4507
+ * Counts across every tenant. The platform calls it inside
4508
+ * `RlsBypassPort`; an implementation on a tenant-scoped client has to see
4509
+ * every tenant's rows there. Whether the frame lifts the policy for this
4510
+ * query is up to the application's bypass and the database layer behind it
4511
+ * — one that lifts only reads such as `find*` leaves a `count` filtered, and
4512
+ * then the implementation lifts the policy itself. A subscriber hidden from
4513
+ * the count counts as nobody, which opens the version for editing.
3723
4514
  */
3724
4515
  countByPlanVersionId?(planVersionId: string): Promise<number>;
3725
4516
  /**
@@ -3739,6 +4530,9 @@ interface SubscriptionRepository {
3739
4530
  *
3740
4531
  * Optional — if not implemented, the service defensively treats the
3741
4532
  * version as frozen (fail-closed).
4533
+ *
4534
+ * Counts across every tenant, inside `RlsBypassPort`, and has to see every
4535
+ * tenant's rows there, as `countByPlanVersionId` does.
3742
4536
  */
3743
4537
  countByBundleVersionId?(bundleVersionId: string): Promise<number>;
3744
4538
  /**
@@ -3749,8 +4543,9 @@ interface SubscriptionRepository {
3749
4543
  * (subscriptions on superseded versions are included).
3750
4544
  *
3751
4545
  * Returns a map `planKey → count`; plans without an active subscription
3752
- * are missing (UI defaults to 0). Platform-wide count across all tenants →
3753
- * adapters must count RLS-exempt.
4546
+ * are missing (UI defaults to 0). Platform-wide count across all tenants:
4547
+ * the platform calls it inside `RlsBypassPort`, and an implementation has to
4548
+ * see every tenant's rows there, as for `countByPlanVersionId`.
3754
4549
  *
3755
4550
  * Optional — if not implemented, the tenant column stays 0.
3756
4551
  */
@@ -3826,6 +4621,22 @@ interface SubscriptionContractRepository {
3826
4621
  */
3827
4622
  findByOriginalOfferId(offerId: string, tx?: TransactionContext): Promise<SubscriptionContractRecord | null>;
3828
4623
  terminate(contractId: string, data: TerminateSubscriptionContractData): Promise<SubscriptionContractRecord>;
4624
+ /**
4625
+ * Ends a contract at `data.at` with the status `superseded`, where it is
4626
+ * still as the caller read it: `active` or `scheduled`, and ending when it
4627
+ * ended then. `null`, with nothing written, where it has moved since —
4628
+ * another successor took its place, or a cancellation capped or ended it.
4629
+ *
4630
+ * The condition belongs to the write rather than to a read before it. Two
4631
+ * callers superseding one contract at once — a plan change and an
4632
+ * operator's refresh, say — then end with one successor: the second write
4633
+ * finds the status the first one left and changes nothing, where a read
4634
+ * followed by a plain update would give the tenant two contracts in force.
4635
+ *
4636
+ * With `tx`, written on that transaction and undone with it, so that the
4637
+ * successor written beside it lands with it or not at all.
4638
+ */
4639
+ supersede(contractId: string, data: SupersedeSubscriptionContractData, tx?: TransactionContext): Promise<SubscriptionContractRecord | null>;
3829
4640
  /**
3830
4641
  * The contracts concluded and not yet over, with the issuer each was
3831
4642
  * concluded under: how many there are, and the first `limit` of them,
@@ -3848,8 +4659,8 @@ interface SubscriptionContractRepository {
3848
4659
  *
3849
4660
  * Platform-wide: unlike every other read here it is anchored by no tenant,
3850
4661
  * no contract and no offer, and a start makes it before anything is served.
3851
- * An implementation on a tenant-scoped client must count RLS-exempt, as
3852
- * `countActiveByPlanKey` must — the platform wraps the call in
4662
+ * An implementation on a tenant-scoped client counts inside the bypass
4663
+ * frame, as `countActiveByPlanKey` does — the platform wraps the call in
3853
4664
  * `RlsBypassPort`, and one that answers with the caller's tenant scope
3854
4665
  * instead returns nothing at a boot, where there is no tenant. The
3855
4666
  * persistence contract runs with no policy forced, so it cannot catch that
@@ -3926,7 +4737,7 @@ interface SubscriberLedgerRepository {
3926
4737
  *
3927
4738
  * The platform controller `GET /billing/usage` maps this form 1:1 into the
3928
4739
  * response body. The consumer adapter loads from its own subscription
3929
- * table (Prisma include planVersion + pendingPlanVersion).
4740
+ * table (Prisma include planVersion).
3930
4741
  */
3931
4742
  interface SubscriptionUsageRecord {
3932
4743
  /**
@@ -3985,6 +4796,17 @@ interface SubscriptionUsageRecord {
3985
4796
  pendingPlan: string | null;
3986
4797
  pendingBillingCycle: string | null;
3987
4798
  pendingEffectiveAt: Date | null;
4799
+ /**
4800
+ * The version a scheduled change binds when it lands, where it names one —
4801
+ * a newer version taken for the end of the term, or the version a change
4802
+ * to another plan was quoted at. Read where it matters whether the
4803
+ * subscription stays on its version: a change on the same plan in the same
4804
+ * rhythm is otherwise indistinguishable from staying put.
4805
+ *
4806
+ * Optional, because an adapter written before it does not pass it through;
4807
+ * both shipped adapters do.
4808
+ */
4809
+ pendingChangeVersionId?: string | null;
3988
4810
  planVersion: {
3989
4811
  id: string;
3990
4812
  planId: string;
@@ -3993,18 +4815,6 @@ interface SubscriptionUsageRecord {
3993
4815
  supersededAt: Date | null;
3994
4816
  changeNote: string | null;
3995
4817
  };
3996
- pendingPlanVersion: {
3997
- id: string;
3998
- planId: string;
3999
- version: number;
4000
- nonRegressive: boolean;
4001
- changeNote: string | null;
4002
- /** Catalog diff form from version-publish; free-form JSON structure. */
4003
- publishedChanges: unknown;
4004
- } | null;
4005
- pendingPlanVersionEffectiveAt: Date | null;
4006
- pendingPlanVersionAccepted: boolean;
4007
- pendingPlanVersionAcceptedAt: Date | null;
4008
4818
  /**
4009
4819
  * P11.4: frozen package snapshot from the
4010
4820
  * `CheckoutOffer` that was activated during onboarding. Read-only —
@@ -4025,8 +4835,34 @@ interface SubscriptionUsageRecord {
4025
4835
  * Read adapter for the UI/display form of a subscription. Used by
4026
4836
  * `TenantBillingController.getUsage`.
4027
4837
  */
4838
+ /** A subscription read across tenants, with the tenant it belongs to. */
4839
+ interface TenantSubscriptionUsage {
4840
+ readonly tenantId: string;
4841
+ readonly subscription: SubscriptionUsageRecord & {
4842
+ readonly id: string;
4843
+ };
4844
+ }
4028
4845
  interface SubscriptionUsagePort {
4029
4846
  findForTenant(tenantId: string): Promise<SubscriptionUsageRecord | null>;
4847
+ /**
4848
+ * Every subscription of `planKey`, in every tenant, bound to a version of
4849
+ * that plan numbered below `version`. The platform reads it to find the
4850
+ * subscriptions a newer version is offered to, inside the RLS bypass.
4851
+ *
4852
+ * Optional, so a port written before it keeps working; an application that
4853
+ * turns version notices on over a port without it is refused at start-up.
4854
+ */
4855
+ listBoundToEarlierVersions?(planKey: string, version: number): Promise<TenantSubscriptionUsage[]>;
4856
+ /**
4857
+ * Every subscription, in every tenant, bound to the plan version
4858
+ * `planVersionId`, whatever its status. The platform reads it to find the
4859
+ * subscriptions a retirement reaches, inside the RLS bypass, and decides
4860
+ * itself which of them are still running.
4861
+ *
4862
+ * Optional, so a port written before it keeps working; a retirement over a
4863
+ * port without it is refused at start-up.
4864
+ */
4865
+ listBoundToVersion?(planVersionId: string): Promise<TenantSubscriptionUsage[]>;
4030
4866
  }
4031
4867
  /**
4032
4868
  * Returns the current usage for all quotaKeys of a tenant declared via
@@ -4072,14 +4908,98 @@ interface ImmediatePlanChangeInput {
4072
4908
  * no cancellation, and loses against one that has acquired one.
4073
4909
  */
4074
4910
  expectedCanceledAt: Date | null;
4911
+ /**
4912
+ * Whether a change that leaves the plan as it is keeps the plan version
4913
+ * the subscription is bound to.
4914
+ *
4915
+ * A scheduled change that only moves the rhythm passes `true`: the
4916
+ * subscriber agreed to the version they are on, and moving them to a newer
4917
+ * one is theirs to decide, by taking it when it is offered. A sale passes
4918
+ * `false` — a change of plan, or onboarding, where the customer chose at
4919
+ * the version in effect — and the write binds the version of `planId` in effect at
4920
+ * `periodStart`. Where `planId` is another plan than the one bound, the
4921
+ * version in effect is bound either way, and so it is where the
4922
+ * subscription is bound to no version at all: it has none to keep.
4923
+ */
4924
+ keepsBoundVersion: boolean;
4925
+ /**
4926
+ * The version of `planId` the change was quoted at: a scheduled change to
4927
+ * another plan passes the version its preview showed when it was
4928
+ * scheduled, recorded as `pendingChangeVersionId`. The write binds it
4929
+ * rather than the version in effect, so a version published in between
4930
+ * does not reach the customer by a change they already agreed to. `null`
4931
+ * binds the version in effect — a sale, onboarding, and a change quoted
4932
+ * where no repository reads versions. A version kept by
4933
+ * `keepsBoundVersion` wins over it.
4934
+ */
4935
+ quotedPlanVersionId: string | null;
4936
+ /**
4937
+ * `true`: bind `quotedPlanVersionId` or nothing. Where that version no
4938
+ * longer takes bookings by `periodStart` (or now), the write claims nothing
4939
+ * and answers `claimed: false`, rather than binding the version in effect.
4940
+ * A switch to a version offered passes it: the subscriber agreed to that
4941
+ * version, and what the switch answers and records names it. Left out, a
4942
+ * version that stopped taking bookings gives way to the one in effect.
4943
+ */
4944
+ quotedVersionOnly?: boolean;
4945
+ /**
4946
+ * The version the caller read the subscription bound to, where its
4947
+ * decision rests on it: the write claims the row only while that binding
4948
+ * still holds. A switch to a version offered passes it — it writes back the
4949
+ * plan it read, and a change made in between would otherwise be undone.
4950
+ * `null` claims a row bound to no version. Left out, the binding is not
4951
+ * claimed — and a write that binds no version (`bindsPlanVersion: false`)
4952
+ * has none to claim.
4953
+ */
4954
+ expectedPlanVersionId?: string | null;
4955
+ /**
4956
+ * `true`: the change the subscription has scheduled survives the write. A
4957
+ * retirement's move passes it — the change is the subscriber's own, and the
4958
+ * move is not. One that only moves the rhythm on the plan being left follows
4959
+ * the subscription to `planId` (`scheduledChangeAfterWrite`). The write then
4960
+ * claims the row only while the scheduled change is still the one it read.
4961
+ * Left out, the write clears it: a change the subscriber makes replaces the
4962
+ * one they scheduled before.
4963
+ */
4964
+ keepsPendingChange?: boolean;
4965
+ /**
4966
+ * `true`: the write puts back a binding the subscription held a moment
4967
+ * before, so `quotedPlanVersionId` counts whether or not it still takes
4968
+ * bookings — it has to be a version of `planId`, nothing more. A
4969
+ * retirement's move or switch passes it to bind the subscription back to
4970
+ * the version retired, where the contract that has to come with the move
4971
+ * could not be written: that version is off sale, often by its own end, and
4972
+ * undoing a move is not a booking (`SC-PLAN-016` asks only of one). Left
4973
+ * out, a version that no longer takes bookings is not bound.
4974
+ */
4975
+ restoresQuotedVersion?: boolean;
4075
4976
  }
4076
4977
  /** Input for `schedulePlanChange` (change at period end). */
4077
4978
  interface ScheduledPlanChangeInput {
4078
4979
  pendingPlan: string;
4079
4980
  pendingBillingCycle: string;
4080
4981
  pendingEffectiveAt: Date;
4982
+ /**
4983
+ * The version of `pendingPlan` the preview quoted, where the change does
4984
+ * not keep the version bound — a change to another plan, or one on a
4985
+ * subscription bound to none; stored as `pendingChangeVersionId` and bound
4986
+ * when the change comes due. `null` where the plan stays on a bound
4987
+ * version — the version bound then is kept, whatever it is by that day — or
4988
+ * where no repository reads versions.
4989
+ */
4990
+ pendingChangeVersionId: string | null;
4081
4991
  /** See `ImmediatePlanChangeInput.expectedCanceledAt`. */
4082
4992
  expectedCanceledAt: Date | null;
4993
+ /** See `ImmediatePlanChangeInput.expectedPlanVersionId`. */
4994
+ expectedPlanVersionId?: string | null;
4995
+ /**
4996
+ * The change the caller read as scheduled, where its decision rests on it —
4997
+ * `null` for nothing scheduled. The write claims the row only while that
4998
+ * still holds. A switch to a version offered passes `null`: an offer is
4999
+ * made only while nothing is scheduled, and a change scheduled in between
5000
+ * would otherwise be written over. Left out, it is not claimed.
5001
+ */
5002
+ expectedPendingPlan?: string | null;
4083
5003
  }
4084
5004
  /**
4085
5005
  * Input for `applyOnboardingSelection`. Plan-change fields that the
@@ -4174,24 +5094,17 @@ interface TenantSubscriptionWritePort {
4174
5094
  changePlanImmediate(tenantId: string, input: ImmediatePlanChangeInput): Promise<{
4175
5095
  plan: string;
4176
5096
  billingCycle: string;
4177
- /** False when the row's cancellation moved since the caller read it. */
5097
+ /**
5098
+ * False when what the caller claimed moved since it read it — the
5099
+ * cancellation, and the binding where it passed one — or when the
5100
+ * version it required no longer takes bookings.
5101
+ */
4178
5102
  claimed: boolean;
4179
5103
  }>;
4180
5104
  /** Change at period end: set pending fields. */
4181
5105
  schedulePlanChange(tenantId: string, input: ScheduledPlanChangeInput): Promise<{
4182
5106
  claimed: boolean;
4183
5107
  }>;
4184
- /**
4185
- * Marks the pending PlanVersion as accepted. Idempotent — a duplicate
4186
- * accept is a no-op. Returns `alreadyAccepted: true` if the status was
4187
- * already set.
4188
- */
4189
- acceptPendingPlanVersion(tenantId: string, userId: string, now: Date): Promise<{
4190
- accepted: boolean;
4191
- acceptedAt: Date | null;
4192
- effectiveAt: Date | null;
4193
- alreadyAccepted: boolean;
4194
- }>;
4195
5108
  /**
4196
5109
  * Record a cancellation. The dates are decided above this port.
4197
5110
  *
@@ -4233,30 +5146,22 @@ interface TenantSubscriptionWritePort {
4233
5146
  /** Read adapter for PlanVersions. */
4234
5147
  interface PlanVersionRepository {
4235
5148
  /**
4236
- * Currently published (= live) PlanVersion of a plan:
4237
- * `publishedAt IS NOT NULL AND supersededAt IS NULL`. Optionally within a
4238
- * transaction.
4239
- *
4240
- * Note: ignores `validFrom`/`validUntil`. For time-aware
4241
- * resolution (onboarding, plan fallback for TRIAL) use `findActive`.
4242
- *
4243
- * A plan key no plan has finds `null`, not an error.
4244
- */
4245
- findLatestLive(planId: string, tx?: TransactionContext): Promise<PlanVersionRecord | null>;
4246
- /**
4247
- * PlanVersion of a plan active at `asOf`:
5149
+ * The version of a plan on sale at `asOf` — what a booking made then
5150
+ * binds — by the same rule as `PlanRepository.findActivePlanVersion`:
4248
5151
  * `publishedAt IS NOT NULL`
4249
5152
  * `(validFrom IS NULL OR validFrom <= asOf)`
4250
5153
  * `(validUntil IS NULL OR validUntil >= startOfUtcDay(asOf))`
5154
+ * `(endsAt IS NULL OR endsAt > asOf)`
4251
5155
  *
4252
5156
  * `validUntil` is day-inclusive. If multiple versions match, adapters
4253
- * return the highest `validFrom`, explicitly ordering null start dates
4254
- * last as a legacy fallback. Adapters without validity columns may omit
4255
- * the method (consumers fall back to `findLatestLive`).
5157
+ * return the highest `validFrom`, ordering null start dates last, then the
5158
+ * highest version. Adapters build the WHERE with
5159
+ * `buildActivePlanVersionWhere(asOf, { withEndsAt: true })`. Optionally
5160
+ * within a transaction.
4256
5161
  *
4257
5162
  * A plan key no plan has finds `null`, not an error.
4258
5163
  */
4259
- findActive?(planId: string, asOf?: Date, tx?: TransactionContext): Promise<PlanVersionRecord | null>;
5164
+ findActive(planId: string, asOf?: Date, tx?: TransactionContext): Promise<PlanVersionRecord | null>;
4260
5165
  }
4261
5166
 
4262
5167
  /** Aggregate snapshot of subscriptions, orchestrated by `AdminStatsService`. */
@@ -4275,6 +5180,11 @@ interface SubscriptionStatsSnapshot {
4275
5180
  /**
4276
5181
  * Stats adapter for subscriptions. Consumers implement this based on their
4277
5182
  * Prisma `subscription.groupBy(...)` / `count(...)` calls.
5183
+ *
5184
+ * The figures span every tenant: the platform reads them, together with the
5185
+ * promo code and audit figures, inside `RlsBypassPort`, and an implementation
5186
+ * has to see every tenant's rows there — as for
5187
+ * `SubscriptionRepository.countByPlanVersionId`.
4278
5188
  */
4279
5189
  interface SubscriptionStatsPort {
4280
5190
  getStats(): Promise<SubscriptionStatsSnapshot>;
@@ -4321,6 +5231,19 @@ interface AdminActor {
4321
5231
  source: 'web' | 'cli';
4322
5232
  context: string;
4323
5233
  }
5234
+ /**
5235
+ * A platform job acting on its own schedule — no person behind it, so no user:
5236
+ * the canonical `audit_logs.userId` is nullable for exactly this. `context`
5237
+ * names the job, and the actor tag reads `job:platform:<job>`.
5238
+ */
5239
+ interface PlatformJobActor {
5240
+ userId: null;
5241
+ email: 'platform';
5242
+ source: 'job';
5243
+ context: string;
5244
+ }
5245
+ /** Whoever an audit entry names: a person, from the web or the CLI, or a platform job. */
5246
+ type AuditActor = AdminActor | PlatformJobActor;
4324
5247
  /**
4325
5248
  * Audit adapter: platform services write to the audit log through this
4326
5249
  * interface. The consumer implementation persists the records (e.g.
@@ -4332,7 +5255,7 @@ interface AdminActor {
4332
5255
  */
4333
5256
  interface AuditPort {
4334
5257
  write(input: {
4335
- actor: AdminActor;
5258
+ actor: AuditActor;
4336
5259
  entity: string;
4337
5260
  entityId: string;
4338
5261
  action: string;
@@ -4475,6 +5398,14 @@ interface ManifestAccessPort {
4475
5398
  * good at boot as it is in a request.
4476
5399
  */
4477
5400
  interface RlsBypassPort {
5401
+ /**
5402
+ * Runs `fn` with the policy lifted. Where the installation keeps more than
5403
+ * the flag in the frame — the user, the tenant, an audit actor — the bypass
5404
+ * frame carries it over rather than replacing it: an operator's route runs
5405
+ * inside it, and what the request's own frame held is still read there.
5406
+ * `store.run({ ...store.getStore(), bypassRls: true }, fn)` rather than
5407
+ * `store.run({ bypassRls: true }, fn)`.
5408
+ */
4478
5409
  runWithBypass<T>(fn: () => Promise<T>): Promise<T>;
4479
5410
  }
4480
5411
 
@@ -4535,37 +5466,41 @@ interface PlanRepository {
4535
5466
  findVersionById?(versionId: string): Promise<PlanVersionRow | null>;
4536
5467
  findCurrentDraft?(planKey: string): Promise<PlanVersionRow | null>;
4537
5468
  /**
4538
- * Currently published (= live) PlanVersion of a plan:
5469
+ * The newest published version of a plan:
4539
5470
  * `publishedAt IS NOT NULL AND supersededAt IS NULL`.
4540
5471
  *
4541
- * Note: returns the *newest* published version by
4542
- * `version` number and ignores `validFrom`/`validUntil`. For
4543
- * time-aware reads (onboarding, marketing catalog, entitlement
4544
- * fallback) use `findActivePlanVersion(planKey, asOf)`, which returns
4545
- * the version *active at a point in time*.
5472
+ * The version a publish chains to, not the version on sale: a successor
5473
+ * published today with a start next month is the newest at once, and its
5474
+ * predecessor goes on selling until then. What is on sale — for a booking,
5475
+ * a price, a catalogue, an offer — is `findActivePlanVersion`.
4546
5476
  */
4547
5477
  findLatestLivePlanVersion?(planKey: string, tx?: TransactionContext): Promise<PlanVersionRow | null>;
4548
5478
  /**
4549
- * PlanVersion of a plan active at `asOf` ( extended):
5479
+ * The version of a plan on sale at `asOf` — the one a booking made then
5480
+ * binds:
4550
5481
  * `publishedAt IS NOT NULL`
4551
5482
  * `(validFrom IS NULL OR validFrom <= asOf)`
4552
5483
  * `(validUntil IS NULL OR validUntil >= startOfUtcDay(asOf))` — day-inclusive
5484
+ * `(endsAt IS NULL OR endsAt > asOf)`
4553
5485
  *
4554
- * `validFrom IS NULL` is treated like "valid since forever" so that legacy data
4555
- * without a start date (published before the §4.2 publish requirement) does not
4556
- * fall out of the catalog. `validUntil` is day-inclusive (calendar day): the version
4557
- * is valid until the end of its validUntil day, not just until midnight.
4558
- * Adapters build the WHERE via `buildActivePlanVersionWhere`.
5486
+ * `validFrom IS NULL` is treated like "valid since forever" so that a
5487
+ * version published without a start date does not fall out of the
5488
+ * catalogue. `validUntil` is day-inclusive (calendar day): the version is
5489
+ * valid until the end of its validUntil day, not just until midnight.
5490
+ * Adapters build the WHERE via
5491
+ * `buildActivePlanVersionWhere(asOf, { withEndsAt: true })`.
4559
5492
  *
4560
5493
  * If multiple match: the one with the highest `validFrom` (= the
4561
- * "last active"). Adapters must request `NULLS LAST` explicitly so a
4562
- * null start date remains a genuine fallback. Default `asOf` is the call
4563
- * time.
5494
+ * "last active"), then the highest version. Adapters must request
5495
+ * `NULLS LAST` explicitly so a null start date remains a genuine fallback.
5496
+ * Default `asOf` is the call time.
4564
5497
  *
4565
- * Usage: everything that concerns *new* bookings/plan changes
4566
- * (onboarding, public marketing, entitlement fallback on TRIAL).
4567
- * Existing subscriptions stay on their bound `planVersionId`
4568
- * (P1 contract protection).
5498
+ * Every read of what is on sale goes through this — the catalogue, the
5499
+ * plan-change preview, checkout, the public marketing catalogue, every
5500
+ * booking and the offer to existing subscriptions — so all of them name
5501
+ * the same version at the same moment. Existing subscriptions stay on
5502
+ * their bound `planVersionId`. A repository that reads versions and
5503
+ * leaves this out is refused at start-up.
4569
5504
  */
4570
5505
  findActivePlanVersion?(planKey: string, asOf?: Date, tx?: TransactionContext): Promise<PlanVersionRow | null>;
4571
5506
  /**
@@ -4663,27 +5598,30 @@ interface BundleRepository {
4663
5598
  findVersionById(versionId: string, tx?: TransactionContext): Promise<BundleVersionRow | null>;
4664
5599
  findCurrentDraft(bundleId: string): Promise<BundleVersionRow | null>;
4665
5600
  /**
4666
- * Currently published (= live) BundleVersion of a bundle:
5601
+ * The newest published BundleVersion of a bundle:
4667
5602
  * `publishedAt IS NOT NULL AND supersededAt IS NULL`.
4668
5603
  *
4669
- * This deliberately ignores `validFrom`/`validUntil`. For new bookings
4670
- * and other time-aware catalog reads use `findActiveBundleVersion`.
5604
+ * The version a publish chains to, not the version on sale: a successor
5605
+ * published today with a start next month is the newest at once, and its
5606
+ * predecessor goes on selling until then. What is on sale — for a booking,
5607
+ * a catalogue, an upsell — is `findActiveBundleVersion`.
4671
5608
  */
4672
5609
  findLatestLive(bundleId: string, tx?: TransactionContext): Promise<BundleVersionRow | null>;
4673
5610
  /**
4674
- * BundleVersion active at `asOf`:
5611
+ * The BundleVersion on sale at `asOf` — the one a booking made then may
5612
+ * take:
4675
5613
  * `publishedAt IS NOT NULL`
4676
5614
  * `(validFrom IS NULL OR validFrom <= asOf)`
4677
5615
  * `(validUntil IS NULL OR validUntil >= startOfUtcDay(asOf))`
5616
+ * `(supersededAt IS NULL OR validUntil IS NOT NULL)`
4678
5617
  *
4679
- * Both boundaries are inclusive. If multiple versions match, adapters
4680
- * return the highest `validFrom`, then the highest `version`; a null
4681
- * `validFrom` is a legacy fallback. Default `asOf` is the call time.
4682
- *
4683
- * Optional so adapters backed by legacy schemas without validity columns
4684
- * can omit the capability and consumers can fall back explicitly.
5618
+ * Both boundaries are inclusive, and a superseded version sells only
5619
+ * within a last day it carries — the rule of `buildActiveVersionWhere`.
5620
+ * If multiple versions match, adapters return the highest `validFrom`,
5621
+ * then the highest `version`; a null `validFrom` sorts last. Default
5622
+ * `asOf` is the call time.
4685
5623
  */
4686
- findActiveBundleVersion?(bundleId: string, asOf?: Date, tx?: TransactionContext): Promise<BundleVersionRow | null>;
5624
+ findActiveBundleVersion(bundleId: string, asOf?: Date, tx?: TransactionContext): Promise<BundleVersionRow | null>;
4687
5625
  /**
4688
5626
  * Creates a new draft version (`publishedAt = null`). Throws if
4689
5627
  * a draft already exists (partial unique index violation).
@@ -5174,6 +6112,135 @@ interface AppliedSettingsPort {
5174
6112
  acknowledgeChange(id: string, acknowledgedBy: string, acknowledgedAt: Date): Promise<SettingsChangeRecord | null>;
5175
6113
  }
5176
6114
 
6115
+ /**
6116
+ * Keeps the maintenance windows, in the application's own database.
6117
+ *
6118
+ * Every running version of the application reads this table while the lock
6119
+ * holds — the one being replaced as well as the one replacing it — so its shape
6120
+ * is part of the contract between two releases: it gains columns, and never in
6121
+ * a deploy the lock protects.
6122
+ *
6123
+ * An adapter translates. It does not decide whether a window may be locked or
6124
+ * moved; it makes each write conditional on what the caller read, so that two
6125
+ * operators acting at once cannot both succeed.
6126
+ */
6127
+ interface MaintenanceWindowPort {
6128
+ /** The open window — announced or locked, not yet over — or null. There is at most one. */
6129
+ findOpen(): Promise<MaintenanceWindowRecord | null>;
6130
+ /** The most recently recorded windows first, open or over; at most `limit` of them. */
6131
+ listRecent(limit: number): Promise<MaintenanceWindowRecord[]>;
6132
+ /**
6133
+ * Records a new open window and returns it as stored — the id is the
6134
+ * adapter's to assign. Returns null where a window is already open: the
6135
+ * database holds an installation to one, so of two concurrent calls exactly
6136
+ * one lands.
6137
+ */
6138
+ open(window: NewMaintenanceWindow): Promise<MaintenanceWindowRecord | null>;
6139
+ /**
6140
+ * Applies `changes` to window `id`, provided it is still open and at
6141
+ * `stage`. Returns the window as stored, or null where it is not: it ended,
6142
+ * it was locked in the meantime, or no window has that id.
6143
+ */
6144
+ update(id: string, stage: MaintenanceWindowStage, changes: MaintenanceWindowChanges): Promise<MaintenanceWindowRecord | null>;
6145
+ }
6146
+ /**
6147
+ * Hears when a window is announced, moved or cancelled, so the application can
6148
+ * write to its users. Optional: without it tenants learn of a window from the
6149
+ * banner alone.
6150
+ *
6151
+ * Called after the change is recorded, and never waited on by the operator: a
6152
+ * notification that fails is logged, and the window stands as recorded.
6153
+ */
6154
+ interface MaintenanceNotificationPort {
6155
+ windowChanged(event: MaintenanceWindowEvent): Promise<void>;
6156
+ }
6157
+
6158
+ /** A notice to record before anybody is told: its key and what it says. */
6159
+ interface NoticeToRecord extends SubscriptionNoticeKey {
6160
+ readonly content: unknown;
6161
+ }
6162
+ /**
6163
+ * Keeps the record of every notice sent to a subscriber: one per subscription,
6164
+ * kind and subject, with when and to whom it went.
6165
+ *
6166
+ * Delivery takes three writes so that a notice is sent once even where several
6167
+ * instances run the same job: a run claims the notice, the application sends
6168
+ * it, and the run confirms it — or releases it where the sending failed, for the
6169
+ * next run to try again. Each write is conditional on what the caller read; an
6170
+ * adapter translates and does not decide.
6171
+ */
6172
+ interface SubscriptionNoticeRepository {
6173
+ /**
6174
+ * Records the notice named by `key` where it is not recorded yet, and
6175
+ * claims it for this run: where it is not delivered and no run holds it,
6176
+ * or the run that holds it took it on before `staleBefore`. A claim stores
6177
+ * `content` as what the notice says. Returns the notice as claimed, or null
6178
+ * where it is delivered or another run holds a fresh claim — of two
6179
+ * concurrent calls exactly one claims it.
6180
+ */
6181
+ claim(key: SubscriptionNoticeKey, content: unknown, now: Date, staleBefore: Date): Promise<SubscriptionNoticeRecord | null>;
6182
+ /**
6183
+ * Records the notice `id` as delivered, provided it is still held by the
6184
+ * claim taken at `claimedAt` and not delivered. Returns false where it is
6185
+ * not: the claim went stale and another run took the notice on.
6186
+ */
6187
+ confirm(id: string, claimedAt: Date, delivery: SubscriptionNoticeDelivery, now: Date): Promise<boolean>;
6188
+ /**
6189
+ * Lets go of the claim taken at `claimedAt`, so the next run tries again.
6190
+ * Does nothing where the notice is delivered or held by another claim.
6191
+ */
6192
+ release(id: string, claimedAt: Date): Promise<void>;
6193
+ /** The subscriptions a notice of `kind` about `subject` has been delivered to. */
6194
+ listDeliveredSubscriptionIds(kind: SubscriptionNoticeKind, subject: string): Promise<string[]>;
6195
+ /** Every notice of one subscription, the most recently recorded first. */
6196
+ listForSubscription(subscriptionId: string): Promise<SubscriptionNoticeRecord[]>;
6197
+ /**
6198
+ * Records each notice where it is not recorded yet, unclaimed and not
6199
+ * delivered — in the caller's transaction where one is given, so a notice
6200
+ * exists exactly when what it tells of does. A notice already recorded
6201
+ * under the same key is left as it is and not counted: the answer is how
6202
+ * many were recorded now, so a caller can tell that another got there
6203
+ * first — under a transaction running beside it as well.
6204
+ */
6205
+ record(notices: readonly NoticeToRecord[], now: Date, tx?: TransactionContext): Promise<number>;
6206
+ /** Every notice of `kind` recorded at or after `since`, the most recent first. */
6207
+ listOfKindSince(kind: SubscriptionNoticeKind, since: Date): Promise<SubscriptionNoticeRecord[]>;
6208
+ /**
6209
+ * The notices of `kind` that are not delivered and that no run holds — never
6210
+ * claimed, released, or claimed before `staleBefore` — the oldest first.
6211
+ */
6212
+ listUndelivered(kind: SubscriptionNoticeKind, staleBefore: Date): Promise<SubscriptionNoticeRecord[]>;
6213
+ }
6214
+ /**
6215
+ * Sends a notice to the administrators of the tenant it is for, in the
6216
+ * application's words and through its channels. The platform decides when a
6217
+ * notice is due; who the administrators are, what the message says and how it
6218
+ * travels are the application's.
6219
+ *
6220
+ * Resolve with who it went to and how — the platform keeps that as the record.
6221
+ * Throw where it could not be sent and should be tried again: the claim is
6222
+ * released and the next run sends it. Resolving with no recipients records that
6223
+ * nobody could be told, and the notice is not tried again — except a
6224
+ * retirement's notice (`version-retired`), which nobody was told of: it is tried
6225
+ * again by every run until somebody is, and its date counts from then.
6226
+ */
6227
+ interface SubscriptionNoticePort {
6228
+ deliver(notice: SubscriptionNotice): Promise<SubscriptionNoticeDelivery>;
6229
+ }
6230
+
6231
+ /**
6232
+ * Keeps every retirement announcement: which version, which replacement, when
6233
+ * and by whom. What it means for each subscription it reaches is kept beside it
6234
+ * as a notice of kind `version-retired`, written in the same transaction.
6235
+ */
6236
+ interface VersionRetirementRepository {
6237
+ /** Records an announcement, in the caller's transaction where one is given. */
6238
+ create(data: NewVersionRetirement, tx?: TransactionContext): Promise<VersionRetirementRecord>;
6239
+ /** Every announcement, the most recent first. */
6240
+ list(): Promise<VersionRetirementRecord[]>;
6241
+ findById(id: string): Promise<VersionRetirementRecord | null>;
6242
+ }
6243
+
5177
6244
  /** Class reference usable as a DI token (e.g. the consumer's `PrismaService`). */
5178
6245
  type PersistenceClassRef = abstract new (...args: never[]) => unknown;
5179
6246
  /** DI token forms a persistence bundle may reference in `inject`. */
@@ -5211,9 +6278,11 @@ interface PersistenceCapabilities {
5211
6278
  */
5212
6279
  pessimisticLocking: boolean;
5213
6280
  /**
5214
- * The adapter stack integrates with Postgres row-level security (the
5215
- * `RlsBypassPort` frame actually lifts RLS for SuperAdmin reads).
5216
- * Informational — RLS policies themselves remain consumer-owned.
6281
+ * The adapter stack integrates with Postgres row-level security: the
6282
+ * `RlsBypassPort` frame lifts the installation's policies for what the
6283
+ * platform does across tenants, reads and writes alike. Informational —
6284
+ * nothing in the platform reads it, and the policies themselves remain
6285
+ * the installation's.
5217
6286
  */
5218
6287
  rowLevelSecurity: boolean;
5219
6288
  /** Advisory-lock support (`pg_advisory_*`). No platform path requires it today. */
@@ -5221,7 +6290,12 @@ interface PersistenceCapabilities {
5221
6290
  }
5222
6291
  /** Always-required slice: admin surface + transactions. */
5223
6292
  interface SaaSiCatPersistenceCore {
5224
- mfa: PersistenceProvider<MfaPort>;
6293
+ /**
6294
+ * Absent where the schema leaves the SuperAdmin MFA table out
6295
+ * (`notAdopted: ['SuperAdminMfa']`); the application then passes its own
6296
+ * `MfaPort` in `adapters`, and a start without one is refused.
6297
+ */
6298
+ mfa?: PersistenceProvider<MfaPort>;
5225
6299
  audit: PersistenceProvider<AuditPort>;
5226
6300
  rlsBypass: PersistenceProvider<RlsBypassPort>;
5227
6301
  transactionRunner: PersistenceProvider<TransactionRunner>;
@@ -5237,6 +6311,13 @@ interface SaaSiCatPersistenceCore {
5237
6311
  * platform says once at boot that it is not recording.
5238
6312
  */
5239
6313
  appliedSettings?: PersistenceProvider<AppliedSettingsPort>;
6314
+ /**
6315
+ * Where maintenance windows are kept (`maintenance` in
6316
+ * `SaaSiCatModule.forRoot`). Optional so an adapter written before it
6317
+ * existed keeps working; an installation that turns maintenance on without
6318
+ * one is refused at start-up.
6319
+ */
6320
+ maintenanceWindows?: PersistenceProvider<MaintenanceWindowPort>;
5240
6321
  }
5241
6322
  /** Repositories for the entitlement/contract loop (`EntitlementModule`). */
5242
6323
  interface SaaSiCatPersistenceEntitlement {
@@ -5272,6 +6353,18 @@ interface SaaSiCatPersistenceTenantBilling {
5272
6353
  subscriptionUsagePort: PersistenceProvider<SubscriptionUsagePort>;
5273
6354
  subscriptionWritePort: PersistenceProvider<TenantSubscriptionWritePort>;
5274
6355
  usageSnapshotPort?: PersistenceProvider<UsageSnapshotPort>;
6356
+ /**
6357
+ * The record of what each subscriber was told (`tenantBilling.versionNotices`).
6358
+ * Optional so an adapter written before it keeps working; an installation
6359
+ * that turns notices on without one is refused at start-up.
6360
+ */
6361
+ subscriptionNotices?: PersistenceProvider<SubscriptionNoticeRepository>;
6362
+ /**
6363
+ * The record of every retirement announcement. Optional so an adapter
6364
+ * written before it keeps working; an installation that lets an operator
6365
+ * retire versions without one is refused at start-up.
6366
+ */
6367
+ versionRetirements?: PersistenceProvider<VersionRetirementRepository>;
5275
6368
  }
5276
6369
  /**
5277
6370
  * The record of payment gateway callbacks and the payment methods they confirm
@@ -5386,10 +6479,41 @@ declare const CATALOG_ERROR_CODES: {
5386
6479
  readonly PLAN_TERMINATE_INVALID_DATE: "PLAN_TERMINATE_INVALID_DATE";
5387
6480
  readonly PLAN_TERMINATE_DATE_NOT_FUTURE: "PLAN_TERMINATE_DATE_NOT_FUTURE";
5388
6481
  readonly PLAN_TERMINATE_NOT_IMPLEMENTED: "PLAN_TERMINATE_NOT_IMPLEMENTED";
6482
+ /**
6483
+ * Subscriptions told of a retirement still move onto this version, and
6484
+ * nothing moves onto a version that has ended. Carries the version, its
6485
+ * plan key and the first day it may end: the day after the last move.
6486
+ */
6487
+ readonly PLAN_TERMINATE_BEFORE_RETIREMENT_MOVES: "PLAN_TERMINATE_BEFORE_RETIREMENT_MOVES";
6488
+ /**
6489
+ * Subscriptions told of a retirement are past their date and still to be
6490
+ * moved onto this version — a move the platform could not make yet — so it
6491
+ * cannot end until they have moved. Carries how many, the version and its
6492
+ * plan key.
6493
+ */
6494
+ readonly PLAN_TERMINATE_WHILE_MOVES_OVERDUE: "PLAN_TERMINATE_WHILE_MOVES_OVERDUE";
6495
+ /**
6496
+ * Subscriptions on a retired version have not yet been told of their move
6497
+ * onto this version — their notice has reached nobody, so their date is not
6498
+ * set — and it cannot end until they have been. Carries how many, the
6499
+ * version and its plan key.
6500
+ */
6501
+ readonly PLAN_TERMINATE_WHILE_NOTICES_UNDELIVERED: "PLAN_TERMINATE_WHILE_NOTICES_UNDELIVERED";
5389
6502
  readonly BUNDLE_VERSION_ALREADY_PUBLISHED: "BUNDLE_VERSION_ALREADY_PUBLISHED";
5390
6503
  readonly BUNDLE_VERSION_NOT_EDITABLE: "BUNDLE_VERSION_NOT_EDITABLE";
5391
6504
  readonly BUNDLE_VERSION_NOT_PUBLISHED: "BUNDLE_VERSION_NOT_PUBLISHED";
5392
6505
  readonly BUNDLE_VERSION_SUPERSEDED: "BUNDLE_VERSION_SUPERSEDED";
6506
+ /**
6507
+ * The bundle version is published, and its window has not opened: it goes
6508
+ * on sale on `validFrom`, and until then its predecessor is the one sold.
6509
+ */
6510
+ readonly BUNDLE_VERSION_NOT_YET_ON_SALE: "BUNDLE_VERSION_NOT_YET_ON_SALE";
6511
+ /**
6512
+ * The add-on itself has been deleted from the catalogue. Deleting it leaves
6513
+ * its versions' dates as they were, so the version asked for can still be
6514
+ * inside its window; it is not on offer all the same.
6515
+ */
6516
+ readonly BUNDLE_DELETED: "BUNDLE_DELETED";
5393
6517
  readonly BUNDLE_VERSION_REGRESSION: "BUNDLE_VERSION_REGRESSION";
5394
6518
  readonly BUNDLE_VERSION_ZERO_PRICE: "BUNDLE_VERSION_ZERO_PRICE";
5395
6519
  readonly BUNDLE_VERSION_NO_PRICE: "BUNDLE_VERSION_NO_PRICE";
@@ -5446,6 +6570,22 @@ declare const BILLING_ERROR_CODES: {
5446
6570
  readonly BUNDLE_NOT_SELF_SERVICE: "BUNDLE_NOT_SELF_SERVICE";
5447
6571
  readonly BUNDLE_CYCLE_EXCEEDS_PLAN: "BUNDLE_CYCLE_EXCEEDS_PLAN";
5448
6572
  readonly BUNDLE_NOT_PRICED_FOR_THIS_PLAN: "BUNDLE_NOT_PRICED_FOR_THIS_PLAN";
6573
+ /**
6574
+ * The bundle cannot run on a plan the subscription is already set to move
6575
+ * to — by a scheduled change, or by a retirement it has been told of — and
6576
+ * the booking would still be running then: that plan does not book it, or
6577
+ * has no price for it. Carries that plan's key, the rhythm it is billed in
6578
+ * there, and the day the move takes effect from, which can lie in the past
6579
+ * while the move has not run yet.
6580
+ */
6581
+ readonly BUNDLE_CANNOT_RUN_ON_UPCOMING_PLAN: "BUNDLE_CANNOT_RUN_ON_UPCOMING_PLAN";
6582
+ /**
6583
+ * As `BUNDLE_CANNOT_RUN_ON_UPCOMING_PLAN`, where only the rhythm stands in
6584
+ * the way: a yearly bundle, and the subscription billed monthly from the
6585
+ * day of the move — a switch of rhythm on the plan it keeps, for one, where
6586
+ * naming the plan would point at the one it is already on. Same values.
6587
+ */
6588
+ readonly BUNDLE_CANNOT_RUN_ON_UPCOMING_CYCLE: "BUNDLE_CANNOT_RUN_ON_UPCOMING_CYCLE";
5449
6589
  readonly SUBSCRIPTION_BUNDLE_ALREADY_CANCELLED: "SUBSCRIPTION_BUNDLE_ALREADY_CANCELLED";
5450
6590
  readonly SUBSCRIPTION_BUNDLE_NOT_CANCELLED: "SUBSCRIPTION_BUNDLE_NOT_CANCELLED";
5451
6591
  readonly SUBSCRIPTION_BUNDLE_CANCELLATION_EFFECTIVE: "SUBSCRIPTION_BUNDLE_CANCELLATION_EFFECTIVE";
@@ -5464,6 +6604,12 @@ declare const BILLING_ERROR_CODES: {
5464
6604
  * in it: a plan without a yearly price is a monthly plan.
5465
6605
  */
5466
6606
  readonly PLAN_NOT_SOLD_IN_CYCLE: "PLAN_NOT_SOLD_IN_CYCLE";
6607
+ /**
6608
+ * The subscription is bound to a plan version the plan repository does not
6609
+ * find, or finds as a version of another plan. What the subscription pays
6610
+ * cannot be read, so no change is quoted until the binding is repaired.
6611
+ */
6612
+ readonly BOUND_PLAN_VERSION_UNREADABLE: "BOUND_PLAN_VERSION_UNREADABLE";
5467
6613
  /** Plan change refused. Carries `blockers[]` with their own codes. */
5468
6614
  readonly PLAN_CHANGE_BLOCKED: "PLAN_CHANGE_BLOCKED";
5469
6615
  /**
@@ -5486,6 +6632,102 @@ declare const BILLING_ERROR_CODES: {
5486
6632
  * Carries the recomputed dates, so the page can re-ask instead of guessing.
5487
6633
  */
5488
6634
  readonly CANCELLATION_TERMS_CHANGED: "CANCELLATION_TERMS_CHANGED";
6635
+ /**
6636
+ * The version the caller asked to switch to is no longer the one offered —
6637
+ * another is on sale, the subscription moved, or there is no offer. Nothing
6638
+ * was switched. Carries the current `offer`, or `null`, so the page can show
6639
+ * it instead of guessing.
6640
+ */
6641
+ readonly VERSION_OFFER_CHANGED: "VERSION_OFFER_CHANGED";
6642
+ /**
6643
+ * The version a change to another plan named is not the one on sale when
6644
+ * the change was submitted: its preview was shown at another moment.
6645
+ * Nothing was changed. Carries the current `preview`, so the page shows
6646
+ * what the change binds now instead of what it would have.
6647
+ */
6648
+ readonly PLAN_CHANGE_QUOTE_CHANGED: "PLAN_CHANGE_QUOTE_CHANGED";
6649
+ /**
6650
+ * A change to another plan named no version, where its preview names one:
6651
+ * nothing says which price and terms the customer was shown, so nothing
6652
+ * is changed.
6653
+ */
6654
+ readonly PLAN_CHANGE_VERSION_NOT_NAMED: "PLAN_CHANGE_VERSION_NOT_NAMED";
6655
+ /**
6656
+ * A version that takes something away takes effect when the term ends, and
6657
+ * the subscription's cancellation lands no later: the switch would never
6658
+ * happen, so it is refused rather than recorded. Carries both dates.
6659
+ */
6660
+ readonly VERSION_SWITCH_AFTER_CANCELLATION: "VERSION_SWITCH_AFTER_CANCELLATION";
6661
+ /**
6662
+ * A version that takes something away takes effect when the term ends, and
6663
+ * it stops being sold before then — by its window or because an operator
6664
+ * ended it — so it could not be bound when the change comes due. Refused
6665
+ * rather than recorded; carries the dates.
6666
+ */
6667
+ readonly VERSION_ENDS_BEFORE_SWITCH: "VERSION_ENDS_BEFORE_SWITCH";
6668
+ /**
6669
+ * The operator's terms are not confirmed to carry the clause a retirement
6670
+ * rests on (`tenantBilling.orderlyRetirement.termsConfirmed` in
6671
+ * `config/saas.yaml`), so none is accepted.
6672
+ */
6673
+ readonly RETIREMENT_TERMS_NOT_CONFIRMED: "RETIREMENT_TERMS_NOT_CONFIRMED";
6674
+ /**
6675
+ * The version is still on sale: a subscription booked after the
6676
+ * announcement would be on it without having been told. Carries the plan
6677
+ * key and the version.
6678
+ */
6679
+ readonly RETIREMENT_VERSION_ON_SALE: "RETIREMENT_VERSION_ON_SALE";
6680
+ /** The replacement is not on sale, so nobody can continue on it. Carries plan key and version. */
6681
+ readonly RETIREMENT_REPLACEMENT_NOT_ON_SALE: "RETIREMENT_REPLACEMENT_NOT_ON_SALE";
6682
+ /**
6683
+ * The replacement has no price in the rhythm some of the subscriptions it
6684
+ * reaches are billed in, so they cannot continue on it. Carries how many,
6685
+ * and the replacement's plan key and version.
6686
+ */
6687
+ readonly RETIREMENT_REPLACEMENT_NOT_SOLD_IN_RHYTHM: "RETIREMENT_REPLACEMENT_NOT_SOLD_IN_RHYTHM";
6688
+ /**
6689
+ * Some of the subscriptions the retirement reaches hold an add-on that
6690
+ * cannot run on the replacement's plan, so they cannot continue on it.
6691
+ * Carries how many, and the replacement's plan key and version.
6692
+ */
6693
+ readonly RETIREMENT_REPLACEMENT_CANNOT_CARRY_BUNDLES: "RETIREMENT_REPLACEMENT_CANNOT_CARRY_BUNDLES";
6694
+ /** The replacement named is the version being retired. */
6695
+ readonly RETIREMENT_REPLACEMENT_IS_RETIRED: "RETIREMENT_REPLACEMENT_IS_RETIRED";
6696
+ /** No running subscription is on the version: there is nobody to tell. Carries plan key and version. */
6697
+ readonly RETIREMENT_NOTHING_AFFECTED: "RETIREMENT_NOTHING_AFFECTED";
6698
+ /**
6699
+ * Some of the subscriptions it reaches were reached by a retirement within
6700
+ * the last twelve months, and a subscription is reached at most once in that
6701
+ * time. Carries how many and which.
6702
+ */
6703
+ readonly RETIREMENT_WITHIN_TWELVE_MONTHS: "RETIREMENT_WITHIN_TWELVE_MONTHS";
6704
+ /**
6705
+ * The subscriptions the retirement reaches, or their dates, changed since
6706
+ * the preview the operator confirmed. Carries the preview as it stands.
6707
+ */
6708
+ readonly RETIREMENT_PREVIEW_CHANGED: "RETIREMENT_PREVIEW_CHANGED";
6709
+ /** No retirement of the subscription's version waits for its date: there is nothing to switch to. */
6710
+ readonly RETIREMENT_SWITCH_NOT_PENDING: "RETIREMENT_SWITCH_NOT_PENDING";
6711
+ /** The switch opens once the trial has converted: a trial has no contract to hold the price on. */
6712
+ readonly RETIREMENT_SWITCH_IN_TRIAL: "RETIREMENT_SWITCH_IN_TRIAL";
6713
+ /**
6714
+ * An add-on running today cannot run beside the replacement's plan, which
6715
+ * the switch would move the subscription onto today. Carries the add-on,
6716
+ * the plan and the earliest day the booking can end. Its own code rather
6717
+ * than a plan change's: the switch has no other plan to choose and no day
6718
+ * to pick, so the way past it is the add-on ending.
6719
+ */
6720
+ readonly RETIREMENT_SWITCH_BUNDLE_CANNOT_FOLLOW: "RETIREMENT_SWITCH_BUNDLE_CANNOT_FOLLOW";
6721
+ /**
6722
+ * The subscription takes no switch now: a change is scheduled, it has
6723
+ * ended, or its plan is held for a special contract.
6724
+ */
6725
+ readonly RETIREMENT_SWITCH_NOT_OPEN: "RETIREMENT_SWITCH_NOT_OPEN";
6726
+ /**
6727
+ * The version named is not the replacement the retirement names. Carries
6728
+ * the retirement as it now stands.
6729
+ */
6730
+ readonly RETIREMENT_SWITCH_CHANGED: "RETIREMENT_SWITCH_CHANGED";
5489
6731
  /** The subscription has ended; its plan can no longer be changed. */
5490
6732
  readonly SUBSCRIPTION_ENDED: "SUBSCRIPTION_ENDED";
5491
6733
  /** An active special contract blocks self-service plan changes. */
@@ -5503,14 +6745,23 @@ declare const BILLING_ERROR_CODES: {
5503
6745
  readonly CANCELLATION_LOCKS_THE_CYCLE: "CANCELLATION_LOCKS_THE_CYCLE";
5504
6746
  /**
5505
6747
  * A bundle the tenant already holds runs past the cycle they are moving to.
6748
+ * Carries the bundle, the earliest day the booking can end, and the two
6749
+ * rhythms.
5506
6750
  *
5507
6751
  * Its own code rather than `BUNDLE_CYCLE_EXCEEDS_PLAN`, which states the
5508
6752
  * same rule about a booking that has not been made yet. The two need
5509
6753
  * different sentences: this one can name the day the obstacle lifts and
5510
- * tell the reader to cancel the booking, and that advice is wrong for
5511
- * someone who is only about to book. One template cannot serve both.
6754
+ * tell the reader how to get past it, and that advice is wrong for someone
6755
+ * who is only about to book. One template cannot serve both.
5512
6756
  */
5513
6757
  readonly BUNDLE_BOOKING_OUTLASTS_TARGET_CYCLE: "BUNDLE_BOOKING_OUTLASTS_TARGET_CYCLE";
6758
+ /**
6759
+ * A bundle the tenant already holds cannot run on the plan they are moving
6760
+ * to: that plan may not book it, or it has no price there in the rhythm
6761
+ * the booking is billed in. Carries the bundle, the plan and the earliest
6762
+ * day the booking can end.
6763
+ */
6764
+ readonly BUNDLE_BOOKING_DOES_NOT_FIT_TARGET_PLAN: "BUNDLE_BOOKING_DOES_NOT_FIT_TARGET_PLAN";
5514
6765
  /**
5515
6766
  * Features of the previewed bundle are already covered by the plan or by
5516
6767
  * another booked bundle. A warning rather than a blocker: paying twice is
@@ -5532,7 +6783,6 @@ declare const BILLING_ERROR_CODES: {
5532
6783
  * tenant reads and therefore needs a shipped text for.
5533
6784
  */
5534
6785
  readonly BUNDLE_FEATURE_DEPENDENCY_UNSATISFIED: "BUNDLE_FEATURE_DEPENDENCY_UNSATISFIED";
5535
- readonly NO_PENDING_PLAN_VERSION: "NO_PENDING_PLAN_VERSION";
5536
6786
  readonly ONBOARDING_CREATE_FAILED: "ONBOARDING_CREATE_FAILED";
5537
6787
  readonly BUNDLE_PREVIEW_ARGUMENT_AMBIGUOUS: "BUNDLE_PREVIEW_ARGUMENT_AMBIGUOUS";
5538
6788
  /**
@@ -5584,6 +6834,14 @@ declare const CONTRACT_ERROR_CODES: {
5584
6834
  readonly SUBSCRIPTION_CONTRACT_NOT_FOUND: "SUBSCRIPTION_CONTRACT_NOT_FOUND";
5585
6835
  readonly NO_ACTIVE_SUBSCRIPTION_CONTRACT: "NO_ACTIVE_SUBSCRIPTION_CONTRACT";
5586
6836
  readonly SUBSCRIPTION_CONTRACT_ALREADY_CLOSED: "SUBSCRIPTION_CONTRACT_ALREADY_CLOSED";
6837
+ /**
6838
+ * A successor would run beside another contract of the tenant: the
6839
+ * contract in force moved while it was being written — another successor
6840
+ * took its place, or a cancellation capped it — and kept moving on a
6841
+ * second attempt, or a contract begins after the moment the successor
6842
+ * would take effect. Nothing was written.
6843
+ */
6844
+ readonly SUBSCRIPTION_CONTRACT_CHANGED: "SUBSCRIPTION_CONTRACT_CHANGED";
5587
6845
  };
5588
6846
  type ContractErrorCode = (typeof CONTRACT_ERROR_CODES)[keyof typeof CONTRACT_ERROR_CODES];
5589
6847
  /** The parties contracts are concluded with (`SubscriberService`), and their absence. */
@@ -5707,6 +6965,26 @@ declare const SETTINGS_ERROR_CODES: {
5707
6965
  readonly SETTINGS_CHANGE_NOT_FOUND: "SETTINGS_CHANGE_NOT_FOUND";
5708
6966
  };
5709
6967
  type SettingsErrorCode = (typeof SETTINGS_ERROR_CODES)[keyof typeof SETTINGS_ERROR_CODES];
6968
+ /** Codes of maintenance windows: the lock's refusal, and the operator's routes. */
6969
+ declare const MAINTENANCE_ERROR_CODES: {
6970
+ /** The application is locked for maintenance; the body carries the window. */
6971
+ readonly MAINTENANCE: "MAINTENANCE";
6972
+ /** A window is already open: move or cancel it before announcing another. */
6973
+ readonly MAINTENANCE_WINDOW_ALREADY_OPEN: "MAINTENANCE_WINDOW_ALREADY_OPEN";
6974
+ /** No open window has this id — it ended, or it never existed. */
6975
+ readonly MAINTENANCE_WINDOW_NOT_OPEN: "MAINTENANCE_WINDOW_NOT_OPEN";
6976
+ /** The window is locked: its start is history, and unlocking is what ends it. */
6977
+ readonly MAINTENANCE_WINDOW_LOCKED: "MAINTENANCE_WINDOW_LOCKED";
6978
+ /** The announced end is not after the announced start. */
6979
+ readonly MAINTENANCE_WINDOW_END_NOT_AFTER_START: "MAINTENANCE_WINDOW_END_NOT_AFTER_START";
6980
+ /** The announced end has already passed. */
6981
+ readonly MAINTENANCE_WINDOW_END_IN_PAST: "MAINTENANCE_WINDOW_END_IN_PAST";
6982
+ /** A time without its zone, or not a date and time at all; `field` names which. */
6983
+ readonly MAINTENANCE_TIME_INVALID: "MAINTENANCE_TIME_INVALID";
6984
+ /** The message is longer than `MAINTENANCE_MESSAGE_MAX_LENGTH`. */
6985
+ readonly MAINTENANCE_MESSAGE_TOO_LONG: "MAINTENANCE_MESSAGE_TOO_LONG";
6986
+ };
6987
+ type MaintenanceErrorCode = (typeof MAINTENANCE_ERROR_CODES)[keyof typeof MAINTENANCE_ERROR_CODES];
5710
6988
  /**
5711
6989
  * Every exception code the platform emits, in one object.
5712
6990
  *
@@ -5715,6 +6993,22 @@ type SettingsErrorCode = (typeof SETTINGS_ERROR_CODES)[keyof typeof SETTINGS_ERR
5715
6993
  * removing one may not.
5716
6994
  */
5717
6995
  declare const PLATFORM_ERROR_CODES: {
6996
+ /** The application is locked for maintenance; the body carries the window. */
6997
+ readonly MAINTENANCE: "MAINTENANCE";
6998
+ /** A window is already open: move or cancel it before announcing another. */
6999
+ readonly MAINTENANCE_WINDOW_ALREADY_OPEN: "MAINTENANCE_WINDOW_ALREADY_OPEN";
7000
+ /** No open window has this id — it ended, or it never existed. */
7001
+ readonly MAINTENANCE_WINDOW_NOT_OPEN: "MAINTENANCE_WINDOW_NOT_OPEN";
7002
+ /** The window is locked: its start is history, and unlocking is what ends it. */
7003
+ readonly MAINTENANCE_WINDOW_LOCKED: "MAINTENANCE_WINDOW_LOCKED";
7004
+ /** The announced end is not after the announced start. */
7005
+ readonly MAINTENANCE_WINDOW_END_NOT_AFTER_START: "MAINTENANCE_WINDOW_END_NOT_AFTER_START";
7006
+ /** The announced end has already passed. */
7007
+ readonly MAINTENANCE_WINDOW_END_IN_PAST: "MAINTENANCE_WINDOW_END_IN_PAST";
7008
+ /** A time without its zone, or not a date and time at all; `field` names which. */
7009
+ readonly MAINTENANCE_TIME_INVALID: "MAINTENANCE_TIME_INVALID";
7010
+ /** The message is longer than `MAINTENANCE_MESSAGE_MAX_LENGTH`. */
7011
+ readonly MAINTENANCE_MESSAGE_TOO_LONG: "MAINTENANCE_MESSAGE_TOO_LONG";
5718
7012
  /** No recorded change has this id, or the installation keeps no record at all. */
5719
7013
  readonly SETTINGS_CHANGE_NOT_FOUND: "SETTINGS_CHANGE_NOT_FOUND";
5720
7014
  /**
@@ -5812,6 +7106,14 @@ declare const PLATFORM_ERROR_CODES: {
5812
7106
  readonly SUBSCRIPTION_CONTRACT_NOT_FOUND: "SUBSCRIPTION_CONTRACT_NOT_FOUND";
5813
7107
  readonly NO_ACTIVE_SUBSCRIPTION_CONTRACT: "NO_ACTIVE_SUBSCRIPTION_CONTRACT";
5814
7108
  readonly SUBSCRIPTION_CONTRACT_ALREADY_CLOSED: "SUBSCRIPTION_CONTRACT_ALREADY_CLOSED";
7109
+ /**
7110
+ * A successor would run beside another contract of the tenant: the
7111
+ * contract in force moved while it was being written — another successor
7112
+ * took its place, or a cancellation capped it — and kept moving on a
7113
+ * second attempt, or a contract begins after the moment the successor
7114
+ * would take effect. Nothing was written.
7115
+ */
7116
+ readonly SUBSCRIPTION_CONTRACT_CHANGED: "SUBSCRIPTION_CONTRACT_CHANGED";
5815
7117
  /**
5816
7118
  * Feature not covered by the plan. Defined in `upsell.types.ts` because the
5817
7119
  * upsell body type is built around it; re-exported here so an exhaustive
@@ -5823,6 +7125,22 @@ declare const PLATFORM_ERROR_CODES: {
5823
7125
  readonly BUNDLE_NOT_SELF_SERVICE: "BUNDLE_NOT_SELF_SERVICE";
5824
7126
  readonly BUNDLE_CYCLE_EXCEEDS_PLAN: "BUNDLE_CYCLE_EXCEEDS_PLAN";
5825
7127
  readonly BUNDLE_NOT_PRICED_FOR_THIS_PLAN: "BUNDLE_NOT_PRICED_FOR_THIS_PLAN";
7128
+ /**
7129
+ * The bundle cannot run on a plan the subscription is already set to move
7130
+ * to — by a scheduled change, or by a retirement it has been told of — and
7131
+ * the booking would still be running then: that plan does not book it, or
7132
+ * has no price for it. Carries that plan's key, the rhythm it is billed in
7133
+ * there, and the day the move takes effect from, which can lie in the past
7134
+ * while the move has not run yet.
7135
+ */
7136
+ readonly BUNDLE_CANNOT_RUN_ON_UPCOMING_PLAN: "BUNDLE_CANNOT_RUN_ON_UPCOMING_PLAN";
7137
+ /**
7138
+ * As `BUNDLE_CANNOT_RUN_ON_UPCOMING_PLAN`, where only the rhythm stands in
7139
+ * the way: a yearly bundle, and the subscription billed monthly from the
7140
+ * day of the move — a switch of rhythm on the plan it keeps, for one, where
7141
+ * naming the plan would point at the one it is already on. Same values.
7142
+ */
7143
+ readonly BUNDLE_CANNOT_RUN_ON_UPCOMING_CYCLE: "BUNDLE_CANNOT_RUN_ON_UPCOMING_CYCLE";
5826
7144
  readonly SUBSCRIPTION_BUNDLE_ALREADY_CANCELLED: "SUBSCRIPTION_BUNDLE_ALREADY_CANCELLED";
5827
7145
  readonly SUBSCRIPTION_BUNDLE_NOT_CANCELLED: "SUBSCRIPTION_BUNDLE_NOT_CANCELLED";
5828
7146
  readonly SUBSCRIPTION_BUNDLE_CANCELLATION_EFFECTIVE: "SUBSCRIPTION_BUNDLE_CANCELLATION_EFFECTIVE";
@@ -5841,6 +7159,12 @@ declare const PLATFORM_ERROR_CODES: {
5841
7159
  * in it: a plan without a yearly price is a monthly plan.
5842
7160
  */
5843
7161
  readonly PLAN_NOT_SOLD_IN_CYCLE: "PLAN_NOT_SOLD_IN_CYCLE";
7162
+ /**
7163
+ * The subscription is bound to a plan version the plan repository does not
7164
+ * find, or finds as a version of another plan. What the subscription pays
7165
+ * cannot be read, so no change is quoted until the binding is repaired.
7166
+ */
7167
+ readonly BOUND_PLAN_VERSION_UNREADABLE: "BOUND_PLAN_VERSION_UNREADABLE";
5844
7168
  /** Plan change refused. Carries `blockers[]` with their own codes. */
5845
7169
  readonly PLAN_CHANGE_BLOCKED: "PLAN_CHANGE_BLOCKED";
5846
7170
  /**
@@ -5863,6 +7187,102 @@ declare const PLATFORM_ERROR_CODES: {
5863
7187
  * Carries the recomputed dates, so the page can re-ask instead of guessing.
5864
7188
  */
5865
7189
  readonly CANCELLATION_TERMS_CHANGED: "CANCELLATION_TERMS_CHANGED";
7190
+ /**
7191
+ * The version the caller asked to switch to is no longer the one offered —
7192
+ * another is on sale, the subscription moved, or there is no offer. Nothing
7193
+ * was switched. Carries the current `offer`, or `null`, so the page can show
7194
+ * it instead of guessing.
7195
+ */
7196
+ readonly VERSION_OFFER_CHANGED: "VERSION_OFFER_CHANGED";
7197
+ /**
7198
+ * The version a change to another plan named is not the one on sale when
7199
+ * the change was submitted: its preview was shown at another moment.
7200
+ * Nothing was changed. Carries the current `preview`, so the page shows
7201
+ * what the change binds now instead of what it would have.
7202
+ */
7203
+ readonly PLAN_CHANGE_QUOTE_CHANGED: "PLAN_CHANGE_QUOTE_CHANGED";
7204
+ /**
7205
+ * A change to another plan named no version, where its preview names one:
7206
+ * nothing says which price and terms the customer was shown, so nothing
7207
+ * is changed.
7208
+ */
7209
+ readonly PLAN_CHANGE_VERSION_NOT_NAMED: "PLAN_CHANGE_VERSION_NOT_NAMED";
7210
+ /**
7211
+ * A version that takes something away takes effect when the term ends, and
7212
+ * the subscription's cancellation lands no later: the switch would never
7213
+ * happen, so it is refused rather than recorded. Carries both dates.
7214
+ */
7215
+ readonly VERSION_SWITCH_AFTER_CANCELLATION: "VERSION_SWITCH_AFTER_CANCELLATION";
7216
+ /**
7217
+ * A version that takes something away takes effect when the term ends, and
7218
+ * it stops being sold before then — by its window or because an operator
7219
+ * ended it — so it could not be bound when the change comes due. Refused
7220
+ * rather than recorded; carries the dates.
7221
+ */
7222
+ readonly VERSION_ENDS_BEFORE_SWITCH: "VERSION_ENDS_BEFORE_SWITCH";
7223
+ /**
7224
+ * The operator's terms are not confirmed to carry the clause a retirement
7225
+ * rests on (`tenantBilling.orderlyRetirement.termsConfirmed` in
7226
+ * `config/saas.yaml`), so none is accepted.
7227
+ */
7228
+ readonly RETIREMENT_TERMS_NOT_CONFIRMED: "RETIREMENT_TERMS_NOT_CONFIRMED";
7229
+ /**
7230
+ * The version is still on sale: a subscription booked after the
7231
+ * announcement would be on it without having been told. Carries the plan
7232
+ * key and the version.
7233
+ */
7234
+ readonly RETIREMENT_VERSION_ON_SALE: "RETIREMENT_VERSION_ON_SALE";
7235
+ /** The replacement is not on sale, so nobody can continue on it. Carries plan key and version. */
7236
+ readonly RETIREMENT_REPLACEMENT_NOT_ON_SALE: "RETIREMENT_REPLACEMENT_NOT_ON_SALE";
7237
+ /**
7238
+ * The replacement has no price in the rhythm some of the subscriptions it
7239
+ * reaches are billed in, so they cannot continue on it. Carries how many,
7240
+ * and the replacement's plan key and version.
7241
+ */
7242
+ readonly RETIREMENT_REPLACEMENT_NOT_SOLD_IN_RHYTHM: "RETIREMENT_REPLACEMENT_NOT_SOLD_IN_RHYTHM";
7243
+ /**
7244
+ * Some of the subscriptions the retirement reaches hold an add-on that
7245
+ * cannot run on the replacement's plan, so they cannot continue on it.
7246
+ * Carries how many, and the replacement's plan key and version.
7247
+ */
7248
+ readonly RETIREMENT_REPLACEMENT_CANNOT_CARRY_BUNDLES: "RETIREMENT_REPLACEMENT_CANNOT_CARRY_BUNDLES";
7249
+ /** The replacement named is the version being retired. */
7250
+ readonly RETIREMENT_REPLACEMENT_IS_RETIRED: "RETIREMENT_REPLACEMENT_IS_RETIRED";
7251
+ /** No running subscription is on the version: there is nobody to tell. Carries plan key and version. */
7252
+ readonly RETIREMENT_NOTHING_AFFECTED: "RETIREMENT_NOTHING_AFFECTED";
7253
+ /**
7254
+ * Some of the subscriptions it reaches were reached by a retirement within
7255
+ * the last twelve months, and a subscription is reached at most once in that
7256
+ * time. Carries how many and which.
7257
+ */
7258
+ readonly RETIREMENT_WITHIN_TWELVE_MONTHS: "RETIREMENT_WITHIN_TWELVE_MONTHS";
7259
+ /**
7260
+ * The subscriptions the retirement reaches, or their dates, changed since
7261
+ * the preview the operator confirmed. Carries the preview as it stands.
7262
+ */
7263
+ readonly RETIREMENT_PREVIEW_CHANGED: "RETIREMENT_PREVIEW_CHANGED";
7264
+ /** No retirement of the subscription's version waits for its date: there is nothing to switch to. */
7265
+ readonly RETIREMENT_SWITCH_NOT_PENDING: "RETIREMENT_SWITCH_NOT_PENDING";
7266
+ /** The switch opens once the trial has converted: a trial has no contract to hold the price on. */
7267
+ readonly RETIREMENT_SWITCH_IN_TRIAL: "RETIREMENT_SWITCH_IN_TRIAL";
7268
+ /**
7269
+ * An add-on running today cannot run beside the replacement's plan, which
7270
+ * the switch would move the subscription onto today. Carries the add-on,
7271
+ * the plan and the earliest day the booking can end. Its own code rather
7272
+ * than a plan change's: the switch has no other plan to choose and no day
7273
+ * to pick, so the way past it is the add-on ending.
7274
+ */
7275
+ readonly RETIREMENT_SWITCH_BUNDLE_CANNOT_FOLLOW: "RETIREMENT_SWITCH_BUNDLE_CANNOT_FOLLOW";
7276
+ /**
7277
+ * The subscription takes no switch now: a change is scheduled, it has
7278
+ * ended, or its plan is held for a special contract.
7279
+ */
7280
+ readonly RETIREMENT_SWITCH_NOT_OPEN: "RETIREMENT_SWITCH_NOT_OPEN";
7281
+ /**
7282
+ * The version named is not the replacement the retirement names. Carries
7283
+ * the retirement as it now stands.
7284
+ */
7285
+ readonly RETIREMENT_SWITCH_CHANGED: "RETIREMENT_SWITCH_CHANGED";
5866
7286
  /** The subscription has ended; its plan can no longer be changed. */
5867
7287
  readonly SUBSCRIPTION_ENDED: "SUBSCRIPTION_ENDED";
5868
7288
  /** An active special contract blocks self-service plan changes. */
@@ -5880,14 +7300,23 @@ declare const PLATFORM_ERROR_CODES: {
5880
7300
  readonly CANCELLATION_LOCKS_THE_CYCLE: "CANCELLATION_LOCKS_THE_CYCLE";
5881
7301
  /**
5882
7302
  * A bundle the tenant already holds runs past the cycle they are moving to.
7303
+ * Carries the bundle, the earliest day the booking can end, and the two
7304
+ * rhythms.
5883
7305
  *
5884
7306
  * Its own code rather than `BUNDLE_CYCLE_EXCEEDS_PLAN`, which states the
5885
7307
  * same rule about a booking that has not been made yet. The two need
5886
7308
  * different sentences: this one can name the day the obstacle lifts and
5887
- * tell the reader to cancel the booking, and that advice is wrong for
5888
- * someone who is only about to book. One template cannot serve both.
7309
+ * tell the reader how to get past it, and that advice is wrong for someone
7310
+ * who is only about to book. One template cannot serve both.
5889
7311
  */
5890
7312
  readonly BUNDLE_BOOKING_OUTLASTS_TARGET_CYCLE: "BUNDLE_BOOKING_OUTLASTS_TARGET_CYCLE";
7313
+ /**
7314
+ * A bundle the tenant already holds cannot run on the plan they are moving
7315
+ * to: that plan may not book it, or it has no price there in the rhythm
7316
+ * the booking is billed in. Carries the bundle, the plan and the earliest
7317
+ * day the booking can end.
7318
+ */
7319
+ readonly BUNDLE_BOOKING_DOES_NOT_FIT_TARGET_PLAN: "BUNDLE_BOOKING_DOES_NOT_FIT_TARGET_PLAN";
5891
7320
  /**
5892
7321
  * Features of the previewed bundle are already covered by the plan or by
5893
7322
  * another booked bundle. A warning rather than a blocker: paying twice is
@@ -5909,7 +7338,6 @@ declare const PLATFORM_ERROR_CODES: {
5909
7338
  * tenant reads and therefore needs a shipped text for.
5910
7339
  */
5911
7340
  readonly BUNDLE_FEATURE_DEPENDENCY_UNSATISFIED: "BUNDLE_FEATURE_DEPENDENCY_UNSATISFIED";
5912
- readonly NO_PENDING_PLAN_VERSION: "NO_PENDING_PLAN_VERSION";
5913
7341
  readonly ONBOARDING_CREATE_FAILED: "ONBOARDING_CREATE_FAILED";
5914
7342
  readonly BUNDLE_PREVIEW_ARGUMENT_AMBIGUOUS: "BUNDLE_PREVIEW_ARGUMENT_AMBIGUOUS";
5915
7343
  /**
@@ -5941,10 +7369,41 @@ declare const PLATFORM_ERROR_CODES: {
5941
7369
  readonly PLAN_TERMINATE_INVALID_DATE: "PLAN_TERMINATE_INVALID_DATE";
5942
7370
  readonly PLAN_TERMINATE_DATE_NOT_FUTURE: "PLAN_TERMINATE_DATE_NOT_FUTURE";
5943
7371
  readonly PLAN_TERMINATE_NOT_IMPLEMENTED: "PLAN_TERMINATE_NOT_IMPLEMENTED";
7372
+ /**
7373
+ * Subscriptions told of a retirement still move onto this version, and
7374
+ * nothing moves onto a version that has ended. Carries the version, its
7375
+ * plan key and the first day it may end: the day after the last move.
7376
+ */
7377
+ readonly PLAN_TERMINATE_BEFORE_RETIREMENT_MOVES: "PLAN_TERMINATE_BEFORE_RETIREMENT_MOVES";
7378
+ /**
7379
+ * Subscriptions told of a retirement are past their date and still to be
7380
+ * moved onto this version — a move the platform could not make yet — so it
7381
+ * cannot end until they have moved. Carries how many, the version and its
7382
+ * plan key.
7383
+ */
7384
+ readonly PLAN_TERMINATE_WHILE_MOVES_OVERDUE: "PLAN_TERMINATE_WHILE_MOVES_OVERDUE";
7385
+ /**
7386
+ * Subscriptions on a retired version have not yet been told of their move
7387
+ * onto this version — their notice has reached nobody, so their date is not
7388
+ * set — and it cannot end until they have been. Carries how many, the
7389
+ * version and its plan key.
7390
+ */
7391
+ readonly PLAN_TERMINATE_WHILE_NOTICES_UNDELIVERED: "PLAN_TERMINATE_WHILE_NOTICES_UNDELIVERED";
5944
7392
  readonly BUNDLE_VERSION_ALREADY_PUBLISHED: "BUNDLE_VERSION_ALREADY_PUBLISHED";
5945
7393
  readonly BUNDLE_VERSION_NOT_EDITABLE: "BUNDLE_VERSION_NOT_EDITABLE";
5946
7394
  readonly BUNDLE_VERSION_NOT_PUBLISHED: "BUNDLE_VERSION_NOT_PUBLISHED";
5947
7395
  readonly BUNDLE_VERSION_SUPERSEDED: "BUNDLE_VERSION_SUPERSEDED";
7396
+ /**
7397
+ * The bundle version is published, and its window has not opened: it goes
7398
+ * on sale on `validFrom`, and until then its predecessor is the one sold.
7399
+ */
7400
+ readonly BUNDLE_VERSION_NOT_YET_ON_SALE: "BUNDLE_VERSION_NOT_YET_ON_SALE";
7401
+ /**
7402
+ * The add-on itself has been deleted from the catalogue. Deleting it leaves
7403
+ * its versions' dates as they were, so the version asked for can still be
7404
+ * inside its window; it is not on offer all the same.
7405
+ */
7406
+ readonly BUNDLE_DELETED: "BUNDLE_DELETED";
5948
7407
  readonly BUNDLE_VERSION_REGRESSION: "BUNDLE_VERSION_REGRESSION";
5949
7408
  readonly BUNDLE_VERSION_ZERO_PRICE: "BUNDLE_VERSION_ZERO_PRICE";
5950
7409
  readonly BUNDLE_VERSION_NO_PRICE: "BUNDLE_VERSION_NO_PRICE";
@@ -6034,7 +7493,7 @@ declare const PLATFORM_ERROR_CODES: {
6034
7493
  /** Email already taken (mapped from `PlatformUserExistsError`). */
6035
7494
  readonly EMAIL_EXISTS: "EMAIL_EXISTS";
6036
7495
  };
6037
- type PlatformErrorCode = SetupErrorCode | AuthErrorCode | PromoErrorCode | CatalogErrorCode | BillingErrorCode | ContractErrorCode | SubscriberErrorCode | RegistrationErrorCode | PaymentErrorCode | SettingsErrorCode;
7496
+ type PlatformErrorCode = SetupErrorCode | AuthErrorCode | PromoErrorCode | CatalogErrorCode | BillingErrorCode | ContractErrorCode | SubscriberErrorCode | RegistrationErrorCode | PaymentErrorCode | SettingsErrorCode | MaintenanceErrorCode;
6038
7497
  /**
6039
7498
  * Shape of a coded error response.
6040
7499
  *
@@ -6072,6 +7531,160 @@ declare class PlatformUserExistsError extends Error {
6072
7531
  * when thrower and catcher see the class from different module instances.
6073
7532
  */
6074
7533
  declare function isPlatformUserExistsError(err: unknown): err is PlatformUserExistsError;
7534
+ /**
7535
+ * What a refused write found instead of the state its caller had read:
7536
+ * `gone` — the row no longer exists; `moved` — it exists, but no longer in the
7537
+ * state the write required, because a concurrent request changed it first.
7538
+ */
7539
+ type PersistenceRefusalReason = 'gone' | 'moved';
7540
+ /**
7541
+ * A write an adapter refused because the row was no longer what the caller
7542
+ * read, carrying the platform's error code for that case.
7543
+ *
7544
+ * The platform checks these conditions before it writes and answers with the
7545
+ * same code. The adapter's own guard is what holds when two requests pass
7546
+ * that check together — a double click, two operators — and throwing this
7547
+ * rather than a plain `Error` gives the loser of that race the answer the
7548
+ * check would have given, instead of a 500 that reads like a crash in the log.
7549
+ * The adapter says what it found; the platform decides what that is on the
7550
+ * wire.
7551
+ */
7552
+ declare class PersistenceRefusal extends Error {
7553
+ readonly code: string;
7554
+ readonly reason: PersistenceRefusalReason;
7555
+ readonly params: Readonly<Record<string, unknown>>;
7556
+ constructor(code: string, reason: PersistenceRefusalReason, message: string, params?: Readonly<Record<string, unknown>>);
7557
+ }
7558
+ /** Realm-safe type guard, like `isPlatformUserExistsError`. */
7559
+ declare function isPersistenceRefusal(err: unknown): err is PersistenceRefusal;
7560
+
7561
+ /**
7562
+ * The canonical models a schema may leave out, each with the persistence
7563
+ * bundle members that need it.
7564
+ *
7565
+ * `saasicat schema check` lists every canonical model it does not find as not
7566
+ * adopted. Naming one of these in a bundle's `notAdopted` leaves its members
7567
+ * out of the bundle, so the platform decides at start what it can do without
7568
+ * them — and says so where it cannot — instead of the first request failing
7569
+ * against a table that is not there. Any other model the bundle needs: an
7570
+ * application that leaves it out brings its own adapter for it.
7571
+ */
7572
+ declare const OPTIONAL_CANONICAL_MODELS: {
7573
+ readonly SuperAdminUser: readonly ["core.superAdminProvisioning"];
7574
+ readonly SuperAdminMfa: readonly ["core.mfa"];
7575
+ readonly AppliedSettings: readonly ["core.appliedSettings"];
7576
+ readonly SettingsChange: readonly ["core.appliedSettings"];
7577
+ readonly MaintenanceWindow: readonly ["core.maintenanceWindows"];
7578
+ readonly SubscriptionNotice: readonly ["tenantBilling.subscriptionNotices"];
7579
+ readonly VersionRetirement: readonly ["tenantBilling.versionRetirements"];
7580
+ readonly SubscriberLedgerEntry: readonly ["entitlement.subscriberLedgerRepository"];
7581
+ readonly PromoCodeHold: readonly ["promo.holdRepository"];
7582
+ };
7583
+ type OptionalCanonicalModel = keyof typeof OPTIONAL_CANONICAL_MODELS;
7584
+ /** A bundle member one of the optional models is needed by. */
7585
+ type OptionalBundleMember = (typeof OPTIONAL_CANONICAL_MODELS)[OptionalCanonicalModel][number];
7586
+ /**
7587
+ * The bundle members a `notAdopted` list leaves out.
7588
+ *
7589
+ * A name that is not one of the optional models is refused rather than
7590
+ * ignored: a typo would otherwise leave a member in that fails at its first
7591
+ * request, and a model the bundle cannot run without needs the application's
7592
+ * own adapter, which only the application can supply.
7593
+ */
7594
+ declare function membersLeftOut(notAdopted?: readonly string[]): ReadonlySet<OptionalBundleMember>;
7595
+
7596
+ /** Admits a piece of work when a place is free; see `concurrencyGate`. */
7597
+ type ConcurrencyGate = <T>(work: () => Promise<T>) => Promise<T>;
7598
+ /**
7599
+ * Admits at most `limit` pieces of work at once and queues the rest in the
7600
+ * order they arrived.
7601
+ *
7602
+ * Meant for transactions. Each holds a pooled connection for its whole life,
7603
+ * and the platform's transactions take row locks and then read further. Run
7604
+ * as many at once as there are requests, and every connection can end up held
7605
+ * by a transaction waiting for a lock — or for a connection a sibling needs —
7606
+ * while the statements that would let them finish wait for the pool: the
7607
+ * service stalls until transactions time out. A limit below the pool size
7608
+ * keeps connections free for the work outside a transaction.
7609
+ *
7610
+ * A place is handed to the next in line when work ends, however it ends, so
7611
+ * the count never rises above the limit and nobody waiting is overtaken.
7612
+ */
7613
+ declare function concurrencyGate(limit: number): ConcurrencyGate;
7614
+ /**
7615
+ * The one gate of a pool: every caller naming the same `pool` — the client or
7616
+ * database handle the transactions run on — gets the same queue.
7617
+ *
7618
+ * A bound is a property of the pool, not of whoever builds a runner. Where
7619
+ * the client is an injection token, Nest builds a runner once for every module
7620
+ * that asks for one, all on the same client; a gate per runner would admit the
7621
+ * limit once per module, and a gate per configuration object would join two
7622
+ * pools that happen to share one. A second, different limit for a pool already
7623
+ * bounded is refused rather than split.
7624
+ */
7625
+ declare function concurrencyGateOf(pool: object, limit: number): ConcurrencyGate;
7626
+
7627
+ /** The two versioned catalogue entities, by the name their rows go by. */
7628
+ type CatalogVersionKind = 'PlanVersion' | 'BundleVersion';
7629
+ /** A publish of a version that is not there, refused with the platform's code. */
7630
+ declare function catalogVersionGone(kind: CatalogVersionKind, versionId: string): PersistenceRefusal;
7631
+ /**
7632
+ * A publish or discard of a version somebody published first. A published
7633
+ * version is what tenants may have booked, so it is neither published a second
7634
+ * time — which would rewrite when and by whom — nor discarded.
7635
+ */
7636
+ declare function catalogVersionAlreadyPublished(kind: CatalogVersionKind, versionId: string): PersistenceRefusal;
7637
+ /**
7638
+ * A draft asked for where the plan or bundle already has one: there is one
7639
+ * draft at a time, so that publishing it is never a choice between two.
7640
+ */
7641
+ declare function catalogDraftExists(kind: CatalogVersionKind, key: string, draftVersion: number): PersistenceRefusal;
7642
+
7643
+ /** A plan key a plan already carries. */
7644
+ declare function planKeyTaken(planKey: string): PersistenceRefusal;
7645
+ /** A bundle key a bundle already carries. */
7646
+ declare function bundleKeyTaken(bundleKey: string): PersistenceRefusal;
7647
+ /** A marketing projection for a version and locale that already has one. */
7648
+ declare function marketingProjectionTaken(target: {
7649
+ targetType: string;
7650
+ targetVersionId: string;
7651
+ locale: string;
7652
+ }): PersistenceRefusal;
7653
+
7654
+ /**
7655
+ * The decimal a number was written as, for a `numeric` column.
7656
+ *
7657
+ * `toFixed` rounds the binary double rather than the decimal somebody typed:
7658
+ * `(1.005).toFixed(2)` is `'1.00'` and `(2.675).toFixed(2)` is `'2.67'`, while
7659
+ * `(10.005).toFixed(2)` is `'10.01'` — which way a value goes depends on its
7660
+ * binary representation, not on a rule anyone chose. The shortest decimal that
7661
+ * reads back as the same number is what was written; handed to the column, the
7662
+ * database applies its one rounding rule, half away from zero. Where the
7663
+ * platform validates an amount to the column's places at its boundary, nothing
7664
+ * is rounded at all.
7665
+ */
7666
+ declare function toDecimalString(value: number): string;
7667
+
7668
+ /**
7669
+ * A code whose name is taken — by a live code, or by a deleted one, since a
7670
+ * deleted code keeps its name (`SC-PROMO-028`). The platform checks before it
7671
+ * creates; this is what the unique index answers when two creates race past
7672
+ * that check.
7673
+ */
7674
+ declare function promoCodeTaken(code: string): PersistenceRefusal;
7675
+
7676
+ /** The tenant has no subscription to write to. */
7677
+ declare function subscriptionGone(tenantId: string): PersistenceRefusal;
7678
+ /** The plan to bind has no version in effect at `asOf`. */
7679
+ declare function noActivePlanVersion(planId: string, asOf: Date): PersistenceRefusal;
7680
+ /** The plan to bind is not in the catalogue, or was retired in the meantime. */
7681
+ declare function planNotInCatalog(planKey: string): PersistenceRefusal;
7682
+ /** The booking to cancel does not exist. */
7683
+ declare function subscriptionBundleGone(subscriptionBundleId: string): PersistenceRefusal;
7684
+ /** The booking to cancel was cancelled first, by another request. */
7685
+ declare function subscriptionBundleAlreadyCancelled(subscriptionBundleId: string): PersistenceRefusal;
7686
+ /** No contract of the tenant named carries the id. */
7687
+ declare function subscriptionContractGone(contractId: string): PersistenceRefusal;
6075
7688
 
6076
7689
  /** Feature key → its `requires` keys (from Discovery/FeatureCatalogEntry). */
6077
7690
  type FeatureRequiresIndex = ReadonlyMap<string, readonly string[]>;
@@ -6807,13 +8420,6 @@ interface SetupConfirmMfaResponse {
6807
8420
  ok: boolean;
6808
8421
  }
6809
8422
 
6810
- /** What the adapter's schema can actually answer about a version's dates. */
6811
- interface PlanVersionMappingFields {
6812
- /** `validFrom`/`validUntil` are maintained; otherwise both read as null. */
6813
- validityWindows: boolean;
6814
- /** `endsAt` exists; otherwise the field is left off the record entirely. */
6815
- endsAt: boolean;
6816
- }
6817
8423
  /** A `plans` row as either adapter reads it back. */
6818
8424
  interface CanonicalPlanRow {
6819
8425
  id: string;
@@ -6841,9 +8447,9 @@ interface CanonicalPlanVersionRow {
6841
8447
  publishedChanges: unknown;
6842
8448
  changeNote: string;
6843
8449
  nonRegressive: boolean;
6844
- validFrom?: Date | null;
6845
- validUntil?: Date | null;
6846
- endsAt?: Date | null;
8450
+ validFrom: Date | null;
8451
+ validUntil: Date | null;
8452
+ endsAt: Date | null;
6847
8453
  createdByUserId: string | null;
6848
8454
  publishedByUserId: string | null;
6849
8455
  createdAt: Date;
@@ -6856,7 +8462,7 @@ declare function toPlanRow(row: CanonicalPlanRow): PlanRow;
6856
8462
  * real foreign key has to resolve it first, and only the adapter knows which
6857
8463
  * shape it is looking at.
6858
8464
  */
6859
- declare function toPlanVersionRow(row: CanonicalPlanVersionRow, planKey: string, fields: PlanVersionMappingFields): PlanVersionRow;
8465
+ declare function toPlanVersionRow(row: CanonicalPlanVersionRow, planKey: string): PlanVersionRow;
6860
8466
 
6861
8467
  /**
6862
8468
  * The legal identity of the issuer a catalogue names, or none where it names no
@@ -7298,4 +8904,4 @@ declare function resolveErrorMessage(body: ResolvableErrorBody, overrides?: Part
7298
8904
 
7299
8905
  declare const ERROR_MESSAGES_DE: Record<PlatformErrorCode, string>;
7300
8906
 
7301
- export { ACTIVE_SUBSCRIPTION_CONTRACT_STATUSES, AUTH_ERROR_CODES, type ActionKey, type ActivationOrchestrator, type ActivePlanVersionWhere, type ActivePlanVersionWhereWithEndsAt, type ActiveVersionWhere, type ActiveVersionWhereWithEndsAt, type ActorTag, type AdminAccountEntry, type AdminAccountHolder, type AdminActor, type AdminAuditListFilter, type AdminManifest, type AdminResourcesPort, type AdminSubscriberAccount, type AdminSubscriptionListRow, type AdminTenantDetail, type AdminTenantListFilter, type AdminTenantListRow, type AdminTenantStateResult, type AdminUserListFilter, type AdminUserListRow, type AppliedSettingsPort, type AppliedSettingsRecord, type AppliedSettingsValues, type ApplyOnboardingSelectionInput, type ApplyOnboardingSelectionResult, type ApprovedCatalogKeys, type AuditActionDef, type AuditEntry, type AuditPort, type AuditQuery, type AuditQueryPort, type AuditStatsPort, type AuditStatsSnapshot, type AuthErrorCode, BILLING_ERROR_CODES, BUNDLE_PRICE_LOOKUP_LIMIT, type BillingCycle, type BillingErrorCode, type BundleAvailabilityState, type BundleCompatibility, type BundleFeatureShape, type BundleListFilter, type BundlePricingOverride, type BundleRepository, type BundleRow, type BundleVersionFields, type BundleVersionMutationResult, type BundleVersionRow, CATALOGUE_KEYS, CATALOG_ERROR_CODES, CONTRACT_ERROR_CODES, type CancelSubscriptionBundleData, type CancelSubscriptionInput, type CancelSubscriptionResult, type CancellationNoticePeriods, type CanonicalContractLineItemRow, type CanonicalContractRow, type CanonicalPlanRow, type CanonicalPlanVersionRow, type CanonicalRunningContractRow, type CanonicalSubscriberChargeRow, type CanonicalSubscriberCorrectionRow, type CanonicalSubscriberPaymentMethodRow, type CanonicalSubscriberRow, type CanonicalSubscriptionBundleRow, type CapabilityCatalogEntryRow, type CapabilityCodeStatus, type CapabilityKey, type CapabilityKind, type CatalogEntryFilter, type CatalogEntryI18n, type CatalogEntryI18nFields, type CatalogEntryRepository, type CatalogErrorCode, type ChangeDirection, type CheckoutOfferFilter, type CheckoutOfferLineItem, type CheckoutOfferLineItemKind, type CheckoutOfferPriceBreakdown, type CheckoutOfferPromoCodeSnapshot, type CheckoutOfferPromotionSnapshot, type CheckoutOfferRepository, type CheckoutOfferRow, type CheckoutOfferSelection, type CheckoutOfferSelectionUpdate, type CheckoutOfferStatus, type CleanupResult, type CliUserRow, type ComponentKey, type ConfiguratorCatalog, type ConfiguratorMarketingProvider, type ConfiguratorModel, type ConfiguratorPlanMarketing, type ConfiguratorPlanVersionRow, type ConfiguratorPriceBreakdown, type ConfiguratorSourcesLookup, type ConfirmedPaymentMethod, type ContractErrorCode, type ContractIssuerParty, type ContractLineItemKind, type ContractLineItemRecord, type ContractSubscriberParty, type CreateBundleData, type CreateBundleVersionDraftData, type CreateCheckoutOfferData, type CreateMarketingProjectionData, type CreatePlanData, type CreatePlanVersionDraftData, type CreatePromoCodeData, type CreatePromoCodeRequest, type CreatePromotionData, type CreateSubscriberData, type CreateSubscriptionBundleData, type CreateSubscriptionContractData, type CreateSuperAdminCliInput, type CreateTenantInput, type DiffResult, type DiscoveredCapability, type DiscoveredFeature, type DiscoveredQuota, type DiscoveredQuotaPolicy, type DiscoveryCodeStatus, type DiscoverySnapshot, type DiscoveryStatus, ERROR_MESSAGES_DE, ERROR_MESSAGES_EN, type EffectiveLimitsSnapshot, type EmailPort, type ErrorMessageParams, FEATURE_NOT_LICENSED, type FeatureCatalogEntryRow, type FeatureDef, type FeatureKey, type FeatureNotLicensedBody, type FeatureRequiresIndex, type FeatureTier, type FeatureUiMeta, type FeatureUiRegistry, type FinalActivationResult, type FirstTimeCustomerCheck, ForeignPaymentMethodReferenceError, type ImmediatePlanChangeInput, type InvoiceLineItemSnapshot, type IssuerCorrectionFault, type IssuerIdentityChange, type KpiCardDef, type KpiDisplayHint, LEGAL_IDENTITY_FIELDS, type LegalIdentity, type LegalIdentityField, MARKETING_PRIORITY_MAX, MARKETING_PRIORITY_MIN, type ManifestAccessPort, type ManifestContribution, type MarketingProjectionFilter, type MarketingProjectionRepository, type MarketingProjectionRow, type MarketingSettingsRepository, type MarketingSettingsRow, type MarketingTargetType, type MarketingTopFeature, type MaskedPaymentMethod, type MfaPort, type NewContractLineItemData, type NewSettingsChange, type NewSubscriberCharge, type NewSubscriberDetails, type NewSubscriptionContractData, OTP_RATE_LIMIT_MAX_SENDS, OTP_RATE_LIMIT_WINDOW_MINUTES, OTP_TTL_MINUTES, OTP_VERIFY_MAX_ATTEMPTS, type OnboardingPromoRedemption, type OnboardingSelectionRequest, type OnboardingSelectionResponse, PASSWORD_RESET_TTL_MINUTES, PAYMENT_ERROR_CODES, PAYMENT_METHOD_TYPES, PENDING_CHECKOUT_TTL_DAYS, PENDING_EMAIL_TTL_HOURS, PENDING_ONBOARDING_TTL_DAYS, PLATFORM_ERROR_CODES, PROMO_ERROR_CODES, type Paginated, type PartyAddress, type PasswordHasher, type PasswordResetCliResult, PaymentCallbackRejectedError, type PaymentErrorCode, type PaymentEventClaim, type PaymentEventLog, type PaymentGateway, type PaymentGatewayCallback, type PaymentGatewayEvent, type PaymentMethodHolder, type PaymentMethodSetupSession, type PaymentMethodSetupSubject, type PaymentMethodType, type PendingRegistration, type PendingRegistrationCreateInput, type PendingRegistrationRepository, type PendingRegistrationSnapshot, type PendingRegistrationUpdateInput, type PersistenceCapabilities, PersistenceCapabilityError, type PersistenceClassRef, type PersistenceInjectionToken, type PersistenceProvider, type PlanCatalog, type PlanCatalogApp, type PlanCatalogImportReport, type PlanCatalogImportSink, type PlanCatalogIssuer, type PlanCatalogIssuerCorrection, type PlanCatalogLookup, type PlanCatalogMarketing, type PlanCatalogNotifications, type PlanCatalogPaymentAccount, type PlanCatalogPayments, type PlanCatalogReadSink, type PlanCatalogReadSnapshot, type PlanCatalogSettings, type PlanCatalogSubscribers, type PlanCatalogTenantBilling, type PlanDef, type PlanId, type PlanListFilter, type PlanRepository, type PlanRow, type PlanVersion, type PlanVersionFields, type PlanVersionMappingFields, type PlanVersionMutationResult, type PlanVersionRecord, type PlanVersionRepository, type PlanVersionRow, type PlatformErrorBody, type PlatformErrorCode, type PlatformRole, type PlatformUserDto, PlatformUserExistsError, type ProjectPageDef, type PromoCode, type PromoCodeDurationType, type PromoCodeFilter, type PromoCodeHoldRecord, type PromoCodeHoldRepository, type PromoCodeHoldTaken, type PromoCodeRecord, type PromoCodeRedemption, type PromoCodeRedemptionListItem, type PromoCodeRedemptionRecord, type PromoCodeRedemptionRepository, type PromoCodeRedemptionStatus, type PromoCodeRepository, type PromoCodeStatsPort, type PromoCodeStatsSnapshot, type PromoCodeStatus, type PromoCodeValidationLog, type PromoCodeValidationLogRepository, type PromoCodeValidationResult, type PromoCodeValueType, type PromoErrorCode, type PromoPreviewInvalidReason, type PromoPreviewRequest, type PromoPreviewResponse, type PromoPreviewValidResponse, type PromoRevenueDeductionAggregator, type PromoSubscriptionLookup, type PromotionBillingCycle, type PromotionI18n, type PromotionI18nFields, type PromotionOnPrice, type PromotionRepository, type PromotionResult, type PromotionRow, type PromotionStatus, type PromotionTargetType, type PromotionType, type PromotionValue, type PublicBootResponse, type PublicComparisonRow, type PublicMarketingBundle, type PublicMarketingCatalogResponse, type PublicMarketingPlan, type PublicMarketingPromo, type PublicNewPaymentMethods, type PublicSignupPlan, type PublishBundleVersionData, type PublishBundleVersionMeta, type PublishPlanVersionData, type QuotaCatalogEntryRow, type QuotaEnforcementMode, type QuotaKey, type QuotaProvider, REGISTRATION_ERROR_CODES, REGISTRATION_RESUME_TTL_MINUTES, REGISTRATION_STEP_BY_STATUS, type ReassignTenantAdminCliResult, type RecommendablePlan, type RecordSubscriberPaymentMethodData, type RecordSubscriberPaymentMethodOutcome, type RecordSubscriberPaymentMethodResult, type RedeemPromoInTransactionCallback, type RegistrationActivation, type RegistrationAuditContext, type RegistrationAuditEvent, type RegistrationAuditEventType, type RegistrationAuditLogger, type RegistrationBillingDetails, type RegistrationConfigSelection, type RegistrationConfiguratorLookup, type RegistrationErrorCode, type RegistrationOtpDelivery, type RegistrationPromoPreview, type RegistrationResumeDelivery, type RegistrationResumeTokenSigner, type RegistrationStatus, type RegistrationStep, type RequiredCapabilities, type ResolvableErrorBody, type ResumeRegistrationInput, type ResumeRegistrationResult, type ReviewCatalogEntryData, type RlsBypassPort, type RunningContractIssuer, type RunningContractIssuers, SETTINGS_ERROR_CODES, SETUP_ERROR_CODES, SUBSCRIBER_ACCOUNT_CAPABILITY, SUBSCRIBER_ERROR_CODES, type SaaSiCatPersistenceAdapter, type SaaSiCatPersistenceAdminResources, type SaaSiCatPersistenceCatalog, type SaaSiCatPersistenceCore, type SaaSiCatPersistenceEntitlement, type SaaSiCatPersistencePayments, type SaaSiCatPersistencePromo, type SaaSiCatPersistenceTenantBilling, type SaveRegistrationConfigInput, type SaveRegistrationConfigResult, type ScheduledPlanChangeInput, type SelectPlanInput, type SelectPlanResult, type SelectableBundleShape, type SelfServiceBlockedPlans, type SetCatalogEntryReviewData, type SettingsChangeFilter, type SettingsChangeRecord, type SettingsDifference, type SettingsErrorCode, type SetupConfirmMfaRequest, type SetupConfirmMfaResponse, type SetupErrorCode, type SetupRequest, type SetupResult, type SetupStatusResponse, type SlugAvailabilityCheck, type StandardPageDef, type StandardPageKey, type StartCheckoutInput, type StartCheckoutResult, type StartPaymentMethodSetupInput, type StartRegistrationInput, type StartRegistrationResult, type StoredBundleStem, type StrictModeWarning, type StrictModeWarningCode, type SubscriberCharge, type SubscriberChargeOrigin, type SubscriberChargeRecord, type SubscriberChargeSource, type SubscriberContact, type SubscriberContactChange, type SubscriberCorrectionData, type SubscriberCorrectionRecord, type SubscriberCorrectionResult, type SubscriberDetails, type SubscriberErrorCode, type SubscriberIdentityCorrection, type SubscriberIdentityDelta, type SubscriberIdentityValues, type SubscriberLedgerRepository, type SubscriberPaymentMethodRecord, type SubscriberPaymentMethodReference, type SubscriberPaymentMethodRepository, type SubscriberPaymentMethodSetupData, type SubscriberPaymentMethodSetupMatch, type SubscriberPaymentMethodStatus, type SubscriberRecord, type SubscriberRepository, type Subscription, type SubscriptionBundleRecord, type SubscriptionBundleRepository, type SubscriptionBundleView, type SubscriptionContractFilter, type SubscriptionContractInvoiceSnapshot, type SubscriptionContractParties, type SubscriptionContractPriceSnapshot, type SubscriptionContractRecord, type SubscriptionContractRepository, type SubscriptionContractStatus, type SubscriptionRecord, type SubscriptionRepository, type SubscriptionStatsPort, type SubscriptionStatsSnapshot, type SubscriptionStatus, type SubscriptionUsagePort, type SubscriptionUsageRecord, type SuperAdminProvisioningPort, type SyncDiscoveryResult, type TenantActionDef, type TenantColumnDef, type TenantDto, type TenantListFilter, type TenantPort, type TenantSubscriptionWritePort, type TerminateSubscriptionContractData, type TopPromoCode, type TransactionContext, type TransactionRunner, type UpdateBundleData, type UpdateBundleVersionDraftData, type UpdateCatalogEntryBaseData, type UpdateCatalogEntryI18nData, type UpdateCheckoutOfferData, type UpdateMarketingProjectionData, type UpdateMarketingSettingsData, type UpdatePlanData, type UpdatePlanVersionDraftData, type UpdatePromoCodeData, type UpdatePromoCodeRequest, type UpdatePromotionData, type UpsellOffer, type UpsellOfferResolver, type UpsertCapabilityEntryData, type UpsertFeatureCatalogEntryInput, type UpsertFeatureEntryData, type UpsertPlanInput, type UpsertPlanVersionInput, type UpsertQuotaEntryData, type UpsertResult, type UsageSnapshotPort, type UserAccountLookup, type UserListFilter, type UserManagementPort, type UserPort, type VerifyRegistrationOtpResult, type VersionChange, type VersionChangeDirection, type VersionEditability, type VersionEditableReason, type VersionedEntityBase, applyPromo, assertPersistenceCapabilities, buildActivePlanVersionWhere, buildActiveVersionWhere, buildFeatureRequiresIndex, bundleDraftDefaults, bundleStemDefaults, canonicalJson, classifyBundleVersionDiff, classifyIssuerChange, classifyPlanDiff, collectUnsatisfiedRequires, contractPartiesOf, coverageExcludingSelf, definedFields, diffSettings, formatCustomerNumber, formatErrorMessage, identityCorrectionDelta, isBundleRedundant, isForeignPaymentMethodReferenceError, isPaymentCallbackRejectedError, isPlatformUserExistsError, isVersionEditable, issuerIdentityOf, keepOneRecommended, missingRequiresFor, movedIdentityFields, pickActivePromo, planCatalogSettingsOf, previousUtcDay, promoStatus, promotionOnPrice, readQuotaRecord, readQuotaValue, recordedIssuerIdentity, refuseForeignPaymentMethodReference, resolveBundleAvailability, resolveErrorMessage, sameLegalIdentity, selectChargeableBundles, settingsSubtreeOf, startOfUtcDay, subscriberChargeColumns, subscriberFromRegistration, subscriberPaymentMethodColumns, toBundleStemRow, toContractLineItemRecord, toPlanRow, toPlanVersionRow, toRunningContractIssuer, toSubscriberChargeRecord, toSubscriberCorrectionRecord, toSubscriberPaymentMethodRecord, toSubscriberRecord, toSubscriptionBundleRecord, toSubscriptionContractRecord };
8907
+ export { ACTIVE_SUBSCRIPTION_CONTRACT_STATUSES, AUTH_ERROR_CODES, type ActionKey, type ActivationOrchestrator, type ActivePlanVersionWhere, type ActivePlanVersionWhereWithEndsAt, type ActiveVersionWhere, type ActiveVersionWhereWithEndsAt, type ActorTag, type AdminAccountEntry, type AdminAccountHolder, type AdminActor, type AdminAuditListFilter, type AdminManifest, type AdminResourcesPort, type AdminSubscriberAccount, type AdminSubscriptionListRow, type AdminTenantDetail, type AdminTenantListFilter, type AdminTenantListRow, type AdminTenantStateResult, type AdminUserListFilter, type AdminUserListRow, type AppliedSettingsPort, type AppliedSettingsRecord, type AppliedSettingsValues, type ApplyOnboardingSelectionInput, type ApplyOnboardingSelectionResult, type ApprovedCatalogKeys, type AuditActionDef, type AuditActor, type AuditEntry, type AuditPort, type AuditQuery, type AuditQueryPort, type AuditStatsPort, type AuditStatsSnapshot, type AuthErrorCode, BILLING_ERROR_CODES, BUNDLE_PRICE_LOOKUP_LIMIT, type BillingCycle, type BillingErrorCode, type BundleAvailabilityState, type BundleCompatibility, type BundleFeatureShape, type BundleListFilter, type BundlePricingOverride, type BundleRepository, type BundleRow, type BundleVersionFields, type BundleVersionMutationResult, type BundleVersionRow, CATALOGUE_KEYS, CATALOG_ERROR_CODES, CONTRACT_ERROR_CODES, CUSTOM_LIMITS_KEYS, type CancelSubscriptionBundleData, type CancelSubscriptionInput, type CancelSubscriptionResult, type CancellationNoticePeriods, type CanonicalContractLineItemRow, type CanonicalContractRow, type CanonicalMaintenanceWindowRow, type CanonicalPlanRow, type CanonicalPlanVersionRow, type CanonicalRunningContractRow, type CanonicalSubscriberChargeRow, type CanonicalSubscriberCorrectionRow, type CanonicalSubscriberPaymentMethodRow, type CanonicalSubscriberRow, type CanonicalSubscriptionBundleRow, type CanonicalSubscriptionNoticeRow, type CanonicalVersionRetirementRow, type CapabilityCatalogEntryRow, type CapabilityCodeStatus, type CapabilityKey, type CapabilityKind, type CatalogEntryFilter, type CatalogEntryI18n, type CatalogEntryI18nFields, type CatalogEntryRepository, type CatalogErrorCode, type CatalogVersionKind, type ChangeDirection, type CheckoutOfferFilter, type CheckoutOfferLineItem, type CheckoutOfferLineItemKind, type CheckoutOfferPriceBreakdown, type CheckoutOfferPromoCodeSnapshot, type CheckoutOfferPromotionSnapshot, type CheckoutOfferRepository, type CheckoutOfferRow, type CheckoutOfferSelection, type CheckoutOfferSelectionUpdate, type CheckoutOfferStatus, type CleanupResult, type CliUserRow, type ComponentKey, type ConcurrencyGate, type ConfiguratorCatalog, type ConfiguratorMarketingProvider, type ConfiguratorModel, type ConfiguratorPlanMarketing, type ConfiguratorPlanVersionRow, type ConfiguratorPriceBreakdown, type ConfiguratorSourcesLookup, type ConfirmedPaymentMethod, type ContractErrorCode, type ContractIssuerParty, type ContractLineItemKind, type ContractLineItemRecord, type ContractSubscriberParty, type CreateBundleData, type CreateBundleVersionDraftData, type CreateCheckoutOfferData, type CreateMarketingProjectionData, type CreatePlanData, type CreatePlanVersionDraftData, type CreatePromoCodeData, type CreatePromoCodeRequest, type CreatePromotionData, type CreateSubscriberData, type CreateSubscriptionBundleData, type CreateSubscriptionContractData, type CreateSuperAdminCliInput, type CreateTenantInput, type CustomLimits, type DiffResult, type DiscoveredCapability, type DiscoveredFeature, type DiscoveredQuota, type DiscoveredQuotaPolicy, type DiscoveryCodeStatus, type DiscoverySnapshot, type DiscoveryStatus, ERROR_MESSAGES_DE, ERROR_MESSAGES_EN, type EffectiveLimitsSnapshot, type EmailPort, type ErrorMessageParams, FEATURE_NOT_LICENSED, type FeatureCatalogEntryRow, type FeatureDef, type FeatureKey, type FeatureNotLicensedBody, type FeatureRequiresIndex, type FeatureTier, type FeatureUiMeta, type FeatureUiRegistry, type FinalActivationResult, type FirstTimeCustomerCheck, ForeignPaymentMethodReferenceError, type ImmediatePlanChangeInput, type InvoiceLineItemSnapshot, type IssuerCorrectionFault, type IssuerIdentityChange, type KpiCardDef, type KpiDisplayHint, LEGAL_IDENTITY_FIELDS, type LegalIdentity, type LegalIdentityField, MAINTENANCE_CAPABILITY, MAINTENANCE_ERROR_CODES, MAINTENANCE_MESSAGE_MAX_LENGTH, MAINTENANCE_STATE_MAX_AGE_SECONDS, MARKETING_PRIORITY_MAX, MARKETING_PRIORITY_MIN, type MaintenanceErrorCode, type MaintenanceLockView, type MaintenanceNotificationPort, type MaintenanceOverview, type MaintenanceStatusView, type MaintenanceUnlockView, type MaintenanceWindowChanges, type MaintenanceWindowEvent, type MaintenanceWindowPort, type MaintenanceWindowRecord, type MaintenanceWindowStage, type MaintenanceWindowStatus, type MaintenanceWindowView, type ManifestAccessPort, type ManifestContribution, type MarketingProjectionFilter, type MarketingProjectionRepository, type MarketingProjectionRow, type MarketingSettingsRepository, type MarketingSettingsRow, type MarketingTargetType, type MarketingTopFeature, type MaskedPaymentMethod, type MfaPort, NO_SCHEDULED_CHANGE, type NewContractLineItemData, type NewMaintenanceWindow, type NewSettingsChange, type NewSubscriberCharge, type NewSubscriberDetails, type NewSubscriptionContractData, type NewVersionRetirement, type NoticeToRecord, OPTIONAL_CANONICAL_MODELS, OTP_RATE_LIMIT_MAX_SENDS, OTP_RATE_LIMIT_WINDOW_MINUTES, OTP_TTL_MINUTES, OTP_VERIFY_MAX_ATTEMPTS, type OnboardingPromoRedemption, type OnboardingSelectionRequest, type OnboardingSelectionResponse, type OptionalBundleMember, type OptionalCanonicalModel, type OrderlyRetirementSettings, PASSWORD_RESET_TTL_MINUTES, PAYMENT_ERROR_CODES, PAYMENT_METHOD_TYPES, PENDING_CHECKOUT_TTL_DAYS, PENDING_EMAIL_TTL_HOURS, PENDING_ONBOARDING_TTL_DAYS, PLATFORM_ERROR_CODES, PROMO_ERROR_CODES, type Paginated, type PartyAddress, type PasswordHasher, type PasswordResetCliResult, PaymentCallbackRejectedError, type PaymentErrorCode, type PaymentEventClaim, type PaymentEventLog, type PaymentGateway, type PaymentGatewayCallback, type PaymentGatewayEvent, type PaymentMethodHolder, type PaymentMethodSetupSession, type PaymentMethodSetupSubject, type PaymentMethodType, type PendingRegistration, type PendingRegistrationCreateInput, type PendingRegistrationRepository, type PendingRegistrationSnapshot, type PendingRegistrationUpdateInput, type PersistenceCapabilities, PersistenceCapabilityError, type PersistenceClassRef, type PersistenceInjectionToken, type PersistenceProvider, PersistenceRefusal, type PersistenceRefusalReason, type PlanCatalog, type PlanCatalogApp, type PlanCatalogImportReport, type PlanCatalogImportSink, type PlanCatalogIssuer, type PlanCatalogIssuerCorrection, type PlanCatalogLookup, type PlanCatalogMarketing, type PlanCatalogNotifications, type PlanCatalogPaymentAccount, type PlanCatalogPayments, type PlanCatalogReadSink, type PlanCatalogReadSnapshot, type PlanCatalogSettings, type PlanCatalogSubscribers, type PlanCatalogTenantBilling, type PlanDef, type PlanId, type PlanListFilter, type PlanRepository, type PlanRow, type PlanVersion, type PlanVersionFields, type PlanVersionMutationResult, type PlanVersionRecord, type PlanVersionRepository, type PlanVersionRow, type PlatformErrorBody, type PlatformErrorCode, type PlatformJobActor, type PlatformRole, type PlatformUserDto, PlatformUserExistsError, type ProjectPageDef, type PromoCode, type PromoCodeDurationType, type PromoCodeFilter, type PromoCodeHoldRecord, type PromoCodeHoldRepository, type PromoCodeHoldTaken, type PromoCodeRecord, type PromoCodeRedemption, type PromoCodeRedemptionListItem, type PromoCodeRedemptionRecord, type PromoCodeRedemptionRepository, type PromoCodeRedemptionStatus, type PromoCodeRepository, type PromoCodeStatsPort, type PromoCodeStatsSnapshot, type PromoCodeStatus, type PromoCodeValidationLog, type PromoCodeValidationLogRepository, type PromoCodeValidationResult, type PromoCodeValueType, type PromoErrorCode, type PromoPreviewInvalidReason, type PromoPreviewRequest, type PromoPreviewResponse, type PromoPreviewValidResponse, type PromoRevenueDeductionAggregator, type PromoSubscriptionLookup, type PromotionBillingCycle, type PromotionI18n, type PromotionI18nFields, type PromotionOnPrice, type PromotionRepository, type PromotionResult, type PromotionRow, type PromotionStatus, type PromotionTargetType, type PromotionType, type PromotionValue, type PublicBootResponse, type PublicComparisonRow, type PublicMarketingBundle, type PublicMarketingCatalogResponse, type PublicMarketingPlan, type PublicMarketingPromo, type PublicNewPaymentMethods, type PublicSignupPlan, type PublishBundleVersionData, type PublishBundleVersionMeta, type PublishPlanVersionData, type QuotaCatalogEntryRow, type QuotaEnforcementMode, type QuotaKey, type QuotaProvider, REGISTRATION_ERROR_CODES, REGISTRATION_RESUME_TTL_MINUTES, REGISTRATION_STEP_BY_STATUS, RETIREMENT_REMINDER_LEAD_DAYS, type ReadCustomLimits, type ReassignTenantAdminCliResult, type RecommendablePlan, type RecordSubscriberPaymentMethodData, type RecordSubscriberPaymentMethodOutcome, type RecordSubscriberPaymentMethodResult, type RedeemPromoInTransactionCallback, type RegistrationActivation, type RegistrationAuditContext, type RegistrationAuditEvent, type RegistrationAuditEventType, type RegistrationAuditLogger, type RegistrationBillingDetails, type RegistrationConfigSelection, type RegistrationConfiguratorLookup, type RegistrationErrorCode, type RegistrationOtpDelivery, type RegistrationPromoPreview, type RegistrationResumeDelivery, type RegistrationResumeTokenSigner, type RegistrationStatus, type RegistrationStep, type RequiredCapabilities, type ResolvableErrorBody, type ResumeRegistrationInput, type ResumeRegistrationResult, type RetiredVersionRef, type RetirementAnnounced, type RetirementBlocker, type RetirementMoveRun, type RetirementPreview, type RetirementProgress, type RetirementReachedRow, type RetirementSide, type RetirementSkipReason, type RetirementSkippedRow, type RetirementSwitchResult, type RetirementSwitchTerms, type ReviewCatalogEntryData, type RlsBypassPort, type RunningContractIssuer, type RunningContractIssuers, SETTINGS_ERROR_CODES, SETUP_ERROR_CODES, SUBSCRIBER_ACCOUNT_CAPABILITY, SUBSCRIBER_ERROR_CODES, type SaaSiCatPersistenceAdapter, type SaaSiCatPersistenceAdminResources, type SaaSiCatPersistenceCatalog, type SaaSiCatPersistenceCore, type SaaSiCatPersistenceEntitlement, type SaaSiCatPersistencePayments, type SaaSiCatPersistencePromo, type SaaSiCatPersistenceTenantBilling, type SaveRegistrationConfigInput, type SaveRegistrationConfigResult, type ScheduledChangeColumns, type ScheduledPlanChangeInput, type SecretSealer, type SelectPlanInput, type SelectPlanResult, type SelectableBundleShape, type SelfServiceBlockedPlans, type SetCatalogEntryReviewData, type SettingsChangeFilter, type SettingsChangeRecord, type SettingsDifference, type SettingsErrorCode, type SetupConfirmMfaRequest, type SetupConfirmMfaResponse, type SetupErrorCode, type SetupRequest, type SetupResult, type SetupStatusResponse, type SlugAvailabilityCheck, type StandardPageDef, type StandardPageKey, type StartCheckoutInput, type StartCheckoutResult, type StartPaymentMethodSetupInput, type StartRegistrationInput, type StartRegistrationResult, type StoredBundleStem, type StrictModeWarning, type StrictModeWarningCode, type SubscriberCharge, type SubscriberChargeOrigin, type SubscriberChargeRecord, type SubscriberChargeSource, type SubscriberContact, type SubscriberContactChange, type SubscriberCorrectionData, type SubscriberCorrectionRecord, type SubscriberCorrectionResult, type SubscriberDetails, type SubscriberErrorCode, type SubscriberIdentityCorrection, type SubscriberIdentityDelta, type SubscriberIdentityValues, type SubscriberLedgerRepository, type SubscriberPaymentMethodRecord, type SubscriberPaymentMethodReference, type SubscriberPaymentMethodRepository, type SubscriberPaymentMethodSetupData, type SubscriberPaymentMethodSetupMatch, type SubscriberPaymentMethodStatus, type SubscriberRecord, type SubscriberRepository, type Subscription, type SubscriptionBundleRecord, type SubscriptionBundleRepository, type SubscriptionBundleView, type SubscriptionContractFilter, type SubscriptionContractInvoiceSnapshot, type SubscriptionContractParties, type SubscriptionContractPriceSnapshot, type SubscriptionContractRecord, type SubscriptionContractRepository, type SubscriptionContractStatus, type SubscriptionNotice, type SubscriptionNoticeDelivery, type SubscriptionNoticeKey, type SubscriptionNoticeKind, type SubscriptionNoticePort, type SubscriptionNoticeRecord, type SubscriptionNoticeRepository, type SubscriptionRecord, type SubscriptionRepository, type SubscriptionStatsPort, type SubscriptionStatsSnapshot, type SubscriptionStatus, type SubscriptionUsagePort, type SubscriptionUsageRecord, type SuperAdminProvisioningPort, type SupersedeSubscriptionContractData, type SyncDiscoveryResult, type TenantActionDef, type TenantColumnDef, type TenantDto, type TenantListFilter, type TenantPort, type TenantSubscriptionUsage, type TenantSubscriptionWritePort, type TerminateSubscriptionContractData, type TopPromoCode, type TransactionContext, type TransactionRunner, type UpdateBundleData, type UpdateBundleVersionDraftData, type UpdateCatalogEntryBaseData, type UpdateCatalogEntryI18nData, type UpdateCheckoutOfferData, type UpdateMarketingProjectionData, type UpdateMarketingSettingsData, type UpdatePlanData, type UpdatePlanVersionDraftData, type UpdatePromoCodeData, type UpdatePromoCodeRequest, type UpdatePromotionData, type UpsellOffer, type UpsellOfferResolver, type UpsertCapabilityEntryData, type UpsertFeatureCatalogEntryInput, type UpsertFeatureEntryData, type UpsertPlanInput, type UpsertPlanVersionInput, type UpsertQuotaEntryData, type UpsertResult, type UsageSnapshotPort, type UserAccountLookup, type UserListFilter, type UserManagementPort, type UserPort, VERSION_RETIREMENT_CAPABILITY, type VerifyRegistrationOtpResult, type VersionChange, type VersionChangeDirection, type VersionEditability, type VersionEditableReason, type VersionOffer, type VersionOfferClass, type VersionOfferFields, type VersionOfferSide, type VersionOfferView, type VersionOfferedNotice, type VersionRetiredNotice, type VersionRetirementRecord, type VersionRetirementReminder, type VersionRetirementRepository, type VersionRetirementView, type VersionSale, type VersionSaleDates, type VersionSaleKind, type VersionSwitchResult, type VersionWindow, type VersionedEntityBase, applyPromo, assertPersistenceCapabilities, buildActivePlanVersionWhere, buildActiveVersionWhere, buildFeatureRequiresIndex, bundleDraftDefaults, bundleKeyTaken, bundleStemDefaults, canonicalJson, catalogDraftExists, catalogVersionAlreadyPublished, catalogVersionGone, classifyBundleVersionDiff, classifyIssuerChange, classifyPlanDiff, classifyVersionOffer, collectUnsatisfiedRequires, computeIncludedVat, concurrencyGate, concurrencyGateOf, contractPartiesOf, coverageExcludingSelf, customLimitsReader, definedFields, diffSettings, formatCustomerNumber, formatErrorMessage, grossFromNet, identityCorrectionDelta, isBundleRedundant, isForeignPaymentMethodReferenceError, isMaintenanceAnnouncementLapsed, isMaintenanceOverrun, isPaymentCallbackRejectedError, isPersistenceRefusal, isPlatformUserExistsError, isVersionActiveAt, isVersionEditable, issuerIdentityOf, keepOneRecommended, maintenanceRetryAfterSeconds, maintenanceStatusOf, maintenanceWindowStatusOf, maintenanceWindowViewOf, marketingProjectionTaken, membersLeftOut, missingRequiresFor, movedIdentityFields, netFromGross, noActivePlanVersion, parseZonedInstant, percentOf, pickActivePromo, planCatalogSettingsOf, planKeyTaken, planNotInCatalog, previousUtcDay, promoCodeTaken, promoStatus, promotionOnPrice, prorate, readCustomLimits, readQuotaRecord, readQuotaValue, recordedIssuerIdentity, refuseForeignPaymentMethodReference, resolveBundleAvailability, resolveErrorMessage, retirementCostsTheSubscription, retirementReminderDueAt, retirementReminderIsDue, retirementSwitchTerms, roundToCents, sameLegalIdentity, scheduledChangeAfterWrite, selectChargeableBundles, settingsSubtreeOf, startOfUtcDay, subscriberChargeColumns, subscriberFromRegistration, subscriberPaymentMethodColumns, subscriptionBundleAlreadyCancelled, subscriptionBundleGone, subscriptionContractGone, subscriptionGone, sumToCents, toBundleStemRow, toCents, toContractLineItemRecord, toDecimalString, toMaintenanceWindowRecord, toPlanRow, toPlanVersionRow, toRunningContractIssuer, toSubscriberChargeRecord, toSubscriberCorrectionRecord, toSubscriberPaymentMethodRecord, toSubscriberRecord, toSubscriptionBundleRecord, toSubscriptionContractRecord, toSubscriptionNoticeRecord, toVersionRetirementRecord, versionOnSale, versionOnSaleOrNext, versionSale };