@saasicat/core 1.0.0-rc.4 → 1.0.0-rc.6
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/.build-stamp +1 -1
- package/dist/index.d.cts +130 -5
- package/dist/index.d.ts +130 -5
- package/package.json +1 -1
package/dist/.build-stamp
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
0c786c82cbbbdc8c75e264412f0eb8783769f233ca44e937e497a3d7992a54da
|
package/dist/index.d.cts
CHANGED
|
@@ -2505,7 +2505,20 @@ interface CreateSubscriptionContractData {
|
|
|
2505
2505
|
}
|
|
2506
2506
|
interface TerminateSubscriptionContractData {
|
|
2507
2507
|
effectiveUntil: Date;
|
|
2508
|
-
|
|
2508
|
+
/**
|
|
2509
|
+
* The terminal status, or `null` to end the contract by date alone.
|
|
2510
|
+
*
|
|
2511
|
+
* `findActiveByTenantId` already asks its question as a window —
|
|
2512
|
+
* `effectiveFrom <= asOf` and `effectiveUntil` null or after it — so a
|
|
2513
|
+
* contract given an end in the FUTURE is found until that moment and not
|
|
2514
|
+
* afterwards, with no scheduled job to flip anything.
|
|
2515
|
+
*
|
|
2516
|
+
* Writing a terminal status instead makes the contract disappear from that
|
|
2517
|
+
* lookup at once, which for a cancellation declared months ahead removes an
|
|
2518
|
+
* agreement the customer is still under. Null is how a caller says "it ends
|
|
2519
|
+
* then", and a status is how it says "it is over now".
|
|
2520
|
+
*/
|
|
2521
|
+
status: Extract<SubscriptionContractStatus, 'terminated' | 'superseded'> | null;
|
|
2509
2522
|
}
|
|
2510
2523
|
interface SubscriptionContractFilter {
|
|
2511
2524
|
projectKey?: string;
|
|
@@ -2564,6 +2577,24 @@ interface SubscriptionRecord {
|
|
|
2564
2577
|
} | null;
|
|
2565
2578
|
planVersionId: string;
|
|
2566
2579
|
planVersion: PlanVersionRecord;
|
|
2580
|
+
/**
|
|
2581
|
+
* When a cancellation was declared, and when it takes effect.
|
|
2582
|
+
*
|
|
2583
|
+
* Required, and required together, because entitlement resolution ends a
|
|
2584
|
+
* subscription by reading them: without the second date it cannot tell a
|
|
2585
|
+
* subscription that ends next January from one that ended last January, and
|
|
2586
|
+
* it grants the latter everything. Nothing else in the platform would
|
|
2587
|
+
* notice — no repository filters a cancelled subscription out, and stopping
|
|
2588
|
+
* the billing period is a different decision from ending what a tenant may
|
|
2589
|
+
* do.
|
|
2590
|
+
*
|
|
2591
|
+
* `null` on both means no cancellation. On a row written before the two
|
|
2592
|
+
* fields separated, `canceledAt` carries the effective date and
|
|
2593
|
+
* `canceledEffectiveAt` is genuinely null; every reader in the platform
|
|
2594
|
+
* applies `canceledEffectiveAt ?? canceledAt` for that reason.
|
|
2595
|
+
*/
|
|
2596
|
+
canceledAt: Date | null;
|
|
2597
|
+
canceledEffectiveAt: Date | null;
|
|
2567
2598
|
}
|
|
2568
2599
|
/** Snapshot of a `PlanVersion` row. */
|
|
2569
2600
|
interface PlanVersionRecord {
|
|
@@ -2724,6 +2755,27 @@ interface SubscriptionUsageRecord {
|
|
|
2724
2755
|
/** Current period window — for proration and change-effective date. */
|
|
2725
2756
|
currentPeriodStart: Date | null;
|
|
2726
2757
|
currentPeriodEnd: Date | null;
|
|
2758
|
+
/**
|
|
2759
|
+
* End of what was committed to, which the period end need not equal.
|
|
2760
|
+
*
|
|
2761
|
+
* The cancellation rules measure against this: a subscription cancelled
|
|
2762
|
+
* inside its term keeps running until the term ends, not until the period
|
|
2763
|
+
* does. Null on a trial, and on any subscription written before the field
|
|
2764
|
+
* existed — readers treat that as "the period end is the answer".
|
|
2765
|
+
*/
|
|
2766
|
+
minimumTermUntil?: Date | null;
|
|
2767
|
+
/**
|
|
2768
|
+
* When a cancellation was declared, and when it lands.
|
|
2769
|
+
*
|
|
2770
|
+
* Required for the reason the same pair is required on
|
|
2771
|
+
* `SubscriptionRecord`: the tenant billing route reads them to refuse a
|
|
2772
|
+
* plan change on a subscription that has ended, and a record that omits
|
|
2773
|
+
* them answers "not cancelled" — so the change is applied and prorated
|
|
2774
|
+
* while entitlement resolution, which reads a record that does carry them,
|
|
2775
|
+
* grants nothing.
|
|
2776
|
+
*/
|
|
2777
|
+
canceledAt: Date | null;
|
|
2778
|
+
canceledEffectiveAt: Date | null;
|
|
2727
2779
|
pendingPlan: string | null;
|
|
2728
2780
|
pendingBillingCycle: string | null;
|
|
2729
2781
|
pendingEffectiveAt: Date | null;
|
|
@@ -2799,12 +2851,29 @@ interface ImmediatePlanChangeInput {
|
|
|
2799
2851
|
* change, or target package without trial). A `Date` is persisted.
|
|
2800
2852
|
*/
|
|
2801
2853
|
trialEndsAt?: Date | null;
|
|
2854
|
+
/**
|
|
2855
|
+
* `canceledAt` as the caller read it, so the write can claim the row only
|
|
2856
|
+
* while that is still true.
|
|
2857
|
+
*
|
|
2858
|
+
* Three of the plan route's decisions depend on the cancellation — whether
|
|
2859
|
+
* the change is refused at all, whether the billing cycle may move, and
|
|
2860
|
+
* whether a fresh period is opened — and a read and a write are two
|
|
2861
|
+
* moments. A cancellation declared in between made every one of them answer
|
|
2862
|
+
* about a state that no longer existed, and the write went ahead anyway: a
|
|
2863
|
+
* plan term recorded past the date the subscription ends.
|
|
2864
|
+
*
|
|
2865
|
+
* `null` is a value here rather than an absence. It claims a row that has
|
|
2866
|
+
* no cancellation, and loses against one that has acquired one.
|
|
2867
|
+
*/
|
|
2868
|
+
expectedCanceledAt: Date | null;
|
|
2802
2869
|
}
|
|
2803
2870
|
/** Input for `schedulePlanChange` (change at period end). */
|
|
2804
2871
|
interface ScheduledPlanChangeInput {
|
|
2805
2872
|
pendingPlan: string;
|
|
2806
2873
|
pendingBillingCycle: string;
|
|
2807
2874
|
pendingEffectiveAt: Date;
|
|
2875
|
+
/** See `ImmediatePlanChangeInput.expectedCanceledAt`. */
|
|
2876
|
+
expectedCanceledAt: Date | null;
|
|
2808
2877
|
}
|
|
2809
2878
|
/**
|
|
2810
2879
|
* Input for `applyOnboardingSelection`. Plan-change fields that the
|
|
@@ -2813,6 +2882,12 @@ interface ScheduledPlanChangeInput {
|
|
|
2813
2882
|
interface ApplyOnboardingSelectionInput {
|
|
2814
2883
|
planId: string;
|
|
2815
2884
|
cycle: string;
|
|
2885
|
+
/**
|
|
2886
|
+
* See `ImmediatePlanChangeInput.expectedCanceledAt`. The atomic path needs
|
|
2887
|
+
* it for the same reason the sequential one does: without it, the preferred
|
|
2888
|
+
* implementation is the one where the race stays open.
|
|
2889
|
+
*/
|
|
2890
|
+
expectedCanceledAt: Date | null;
|
|
2816
2891
|
/** For TRIAL → null, otherwise period start from `initialPeriodWindow`. */
|
|
2817
2892
|
periodStart: Date | null;
|
|
2818
2893
|
periodEnd: Date | null;
|
|
@@ -2829,6 +2904,12 @@ interface ApplyOnboardingSelectionResult {
|
|
|
2829
2904
|
subscriptionId: string;
|
|
2830
2905
|
/** null if no redeemPromo callback was provided or the callback returned null. */
|
|
2831
2906
|
promoRedemption: PromoCodeRedemptionRecord | null;
|
|
2907
|
+
/**
|
|
2908
|
+
* False when the row's cancellation moved since the caller read it, in
|
|
2909
|
+
* which case nothing was written — including the promo redemption, which
|
|
2910
|
+
* shares the transaction.
|
|
2911
|
+
*/
|
|
2912
|
+
claimed: boolean;
|
|
2832
2913
|
}
|
|
2833
2914
|
/**
|
|
2834
2915
|
* Callback signature for promo-code redemption WITHIN the onboarding
|
|
@@ -2851,9 +2932,13 @@ interface TenantSubscriptionWritePort {
|
|
|
2851
2932
|
changePlanImmediate(tenantId: string, input: ImmediatePlanChangeInput): Promise<{
|
|
2852
2933
|
plan: string;
|
|
2853
2934
|
billingCycle: string;
|
|
2935
|
+
/** False when the row's cancellation moved since the caller read it. */
|
|
2936
|
+
claimed: boolean;
|
|
2854
2937
|
}>;
|
|
2855
2938
|
/** Change at period end: set pending fields. */
|
|
2856
|
-
schedulePlanChange(tenantId: string, input: ScheduledPlanChangeInput): Promise<
|
|
2939
|
+
schedulePlanChange(tenantId: string, input: ScheduledPlanChangeInput): Promise<{
|
|
2940
|
+
claimed: boolean;
|
|
2941
|
+
}>;
|
|
2857
2942
|
/**
|
|
2858
2943
|
* Marks the pending PlanVersion as accepted. Idempotent — a duplicate
|
|
2859
2944
|
* accept is a no-op. Returns `alreadyAccepted: true` if the status was
|
|
@@ -2866,12 +2951,52 @@ interface TenantSubscriptionWritePort {
|
|
|
2866
2951
|
alreadyAccepted: boolean;
|
|
2867
2952
|
}>;
|
|
2868
2953
|
/**
|
|
2869
|
-
*
|
|
2870
|
-
*
|
|
2954
|
+
* Record a cancellation. The dates are decided above this port.
|
|
2955
|
+
*
|
|
2956
|
+
* `canceledAt` is when the customer said it; `effectiveAt` is when it
|
|
2957
|
+
* lands. They differ for every ordinary cancellation, because a
|
|
2958
|
+
* subscription cancelled inside its term keeps running, keeps being billed
|
|
2959
|
+
* and keeps its entitlements until the term ends. An adapter that computed
|
|
2960
|
+
* the second from the first — which this one did, as
|
|
2961
|
+
* `immediate ? now : currentPeriodEnd` — was deciding a commercial
|
|
2962
|
+
* question in a persistence layer, and could not see the minimum term or
|
|
2963
|
+
* the notice period at all.
|
|
2964
|
+
*
|
|
2965
|
+
* `terminateNow` flips the status immediately, and is set when the
|
|
2966
|
+
* cancellation is already effective: an operator ending a contract, or the
|
|
2967
|
+
* rules finding nothing left to run — no period, no term, as on a trial.
|
|
2968
|
+
* It is never a client's request. A tenant may always declare a
|
|
2969
|
+
* cancellation and may never shorten the term they are in; what decides
|
|
2970
|
+
* this flag is the date the rules returned, not the date they asked for.
|
|
2971
|
+
*
|
|
2972
|
+
* `minimumTermUntil` extends the stored commitment, and is set only when
|
|
2973
|
+
* the cancellation itself extends it: a declaration made after the notice
|
|
2974
|
+
* deadline buys the following period. Left unset the stored term end is
|
|
2975
|
+
* unchanged, which is the ordinary case.
|
|
2871
2976
|
*/
|
|
2872
|
-
cancelSubscription(tenantId: string,
|
|
2977
|
+
cancelSubscription(tenantId: string, input: {
|
|
2978
|
+
canceledAt: Date;
|
|
2979
|
+
effectiveAt: Date;
|
|
2980
|
+
terminateNow: boolean;
|
|
2981
|
+
minimumTermUntil?: Date;
|
|
2982
|
+
}): Promise<{
|
|
2873
2983
|
canceledAt: Date | null;
|
|
2984
|
+
canceledEffectiveAt: Date | null;
|
|
2874
2985
|
status: string;
|
|
2986
|
+
/**
|
|
2987
|
+
* True when a cancellation was already recorded and this call changed
|
|
2988
|
+
* nothing — the stored dates are returned instead.
|
|
2989
|
+
*
|
|
2990
|
+
* The caller checks first, but a check and a write are two moments, and
|
|
2991
|
+
* two requests can pass the check before either writes. Straddling a
|
|
2992
|
+
* notice deadline that costs a billing cycle: the first declaration
|
|
2993
|
+
* lands on time, the second recomputes against a later `now`, and an
|
|
2994
|
+
* unconditional write replaces the first answer with one a period
|
|
2995
|
+
* further out. An implementation therefore claims the row only while
|
|
2996
|
+
* both cancellation fields are still empty, and answers `true` here
|
|
2997
|
+
* when the claim finds nothing to claim.
|
|
2998
|
+
*/
|
|
2999
|
+
alreadyCanceled: boolean;
|
|
2875
3000
|
}>;
|
|
2876
3001
|
/**
|
|
2877
3002
|
* Atomic onboarding creation: sets plan + cycle + period window
|
package/dist/index.d.ts
CHANGED
|
@@ -2505,7 +2505,20 @@ interface CreateSubscriptionContractData {
|
|
|
2505
2505
|
}
|
|
2506
2506
|
interface TerminateSubscriptionContractData {
|
|
2507
2507
|
effectiveUntil: Date;
|
|
2508
|
-
|
|
2508
|
+
/**
|
|
2509
|
+
* The terminal status, or `null` to end the contract by date alone.
|
|
2510
|
+
*
|
|
2511
|
+
* `findActiveByTenantId` already asks its question as a window —
|
|
2512
|
+
* `effectiveFrom <= asOf` and `effectiveUntil` null or after it — so a
|
|
2513
|
+
* contract given an end in the FUTURE is found until that moment and not
|
|
2514
|
+
* afterwards, with no scheduled job to flip anything.
|
|
2515
|
+
*
|
|
2516
|
+
* Writing a terminal status instead makes the contract disappear from that
|
|
2517
|
+
* lookup at once, which for a cancellation declared months ahead removes an
|
|
2518
|
+
* agreement the customer is still under. Null is how a caller says "it ends
|
|
2519
|
+
* then", and a status is how it says "it is over now".
|
|
2520
|
+
*/
|
|
2521
|
+
status: Extract<SubscriptionContractStatus, 'terminated' | 'superseded'> | null;
|
|
2509
2522
|
}
|
|
2510
2523
|
interface SubscriptionContractFilter {
|
|
2511
2524
|
projectKey?: string;
|
|
@@ -2564,6 +2577,24 @@ interface SubscriptionRecord {
|
|
|
2564
2577
|
} | null;
|
|
2565
2578
|
planVersionId: string;
|
|
2566
2579
|
planVersion: PlanVersionRecord;
|
|
2580
|
+
/**
|
|
2581
|
+
* When a cancellation was declared, and when it takes effect.
|
|
2582
|
+
*
|
|
2583
|
+
* Required, and required together, because entitlement resolution ends a
|
|
2584
|
+
* subscription by reading them: without the second date it cannot tell a
|
|
2585
|
+
* subscription that ends next January from one that ended last January, and
|
|
2586
|
+
* it grants the latter everything. Nothing else in the platform would
|
|
2587
|
+
* notice — no repository filters a cancelled subscription out, and stopping
|
|
2588
|
+
* the billing period is a different decision from ending what a tenant may
|
|
2589
|
+
* do.
|
|
2590
|
+
*
|
|
2591
|
+
* `null` on both means no cancellation. On a row written before the two
|
|
2592
|
+
* fields separated, `canceledAt` carries the effective date and
|
|
2593
|
+
* `canceledEffectiveAt` is genuinely null; every reader in the platform
|
|
2594
|
+
* applies `canceledEffectiveAt ?? canceledAt` for that reason.
|
|
2595
|
+
*/
|
|
2596
|
+
canceledAt: Date | null;
|
|
2597
|
+
canceledEffectiveAt: Date | null;
|
|
2567
2598
|
}
|
|
2568
2599
|
/** Snapshot of a `PlanVersion` row. */
|
|
2569
2600
|
interface PlanVersionRecord {
|
|
@@ -2724,6 +2755,27 @@ interface SubscriptionUsageRecord {
|
|
|
2724
2755
|
/** Current period window — for proration and change-effective date. */
|
|
2725
2756
|
currentPeriodStart: Date | null;
|
|
2726
2757
|
currentPeriodEnd: Date | null;
|
|
2758
|
+
/**
|
|
2759
|
+
* End of what was committed to, which the period end need not equal.
|
|
2760
|
+
*
|
|
2761
|
+
* The cancellation rules measure against this: a subscription cancelled
|
|
2762
|
+
* inside its term keeps running until the term ends, not until the period
|
|
2763
|
+
* does. Null on a trial, and on any subscription written before the field
|
|
2764
|
+
* existed — readers treat that as "the period end is the answer".
|
|
2765
|
+
*/
|
|
2766
|
+
minimumTermUntil?: Date | null;
|
|
2767
|
+
/**
|
|
2768
|
+
* When a cancellation was declared, and when it lands.
|
|
2769
|
+
*
|
|
2770
|
+
* Required for the reason the same pair is required on
|
|
2771
|
+
* `SubscriptionRecord`: the tenant billing route reads them to refuse a
|
|
2772
|
+
* plan change on a subscription that has ended, and a record that omits
|
|
2773
|
+
* them answers "not cancelled" — so the change is applied and prorated
|
|
2774
|
+
* while entitlement resolution, which reads a record that does carry them,
|
|
2775
|
+
* grants nothing.
|
|
2776
|
+
*/
|
|
2777
|
+
canceledAt: Date | null;
|
|
2778
|
+
canceledEffectiveAt: Date | null;
|
|
2727
2779
|
pendingPlan: string | null;
|
|
2728
2780
|
pendingBillingCycle: string | null;
|
|
2729
2781
|
pendingEffectiveAt: Date | null;
|
|
@@ -2799,12 +2851,29 @@ interface ImmediatePlanChangeInput {
|
|
|
2799
2851
|
* change, or target package without trial). A `Date` is persisted.
|
|
2800
2852
|
*/
|
|
2801
2853
|
trialEndsAt?: Date | null;
|
|
2854
|
+
/**
|
|
2855
|
+
* `canceledAt` as the caller read it, so the write can claim the row only
|
|
2856
|
+
* while that is still true.
|
|
2857
|
+
*
|
|
2858
|
+
* Three of the plan route's decisions depend on the cancellation — whether
|
|
2859
|
+
* the change is refused at all, whether the billing cycle may move, and
|
|
2860
|
+
* whether a fresh period is opened — and a read and a write are two
|
|
2861
|
+
* moments. A cancellation declared in between made every one of them answer
|
|
2862
|
+
* about a state that no longer existed, and the write went ahead anyway: a
|
|
2863
|
+
* plan term recorded past the date the subscription ends.
|
|
2864
|
+
*
|
|
2865
|
+
* `null` is a value here rather than an absence. It claims a row that has
|
|
2866
|
+
* no cancellation, and loses against one that has acquired one.
|
|
2867
|
+
*/
|
|
2868
|
+
expectedCanceledAt: Date | null;
|
|
2802
2869
|
}
|
|
2803
2870
|
/** Input for `schedulePlanChange` (change at period end). */
|
|
2804
2871
|
interface ScheduledPlanChangeInput {
|
|
2805
2872
|
pendingPlan: string;
|
|
2806
2873
|
pendingBillingCycle: string;
|
|
2807
2874
|
pendingEffectiveAt: Date;
|
|
2875
|
+
/** See `ImmediatePlanChangeInput.expectedCanceledAt`. */
|
|
2876
|
+
expectedCanceledAt: Date | null;
|
|
2808
2877
|
}
|
|
2809
2878
|
/**
|
|
2810
2879
|
* Input for `applyOnboardingSelection`. Plan-change fields that the
|
|
@@ -2813,6 +2882,12 @@ interface ScheduledPlanChangeInput {
|
|
|
2813
2882
|
interface ApplyOnboardingSelectionInput {
|
|
2814
2883
|
planId: string;
|
|
2815
2884
|
cycle: string;
|
|
2885
|
+
/**
|
|
2886
|
+
* See `ImmediatePlanChangeInput.expectedCanceledAt`. The atomic path needs
|
|
2887
|
+
* it for the same reason the sequential one does: without it, the preferred
|
|
2888
|
+
* implementation is the one where the race stays open.
|
|
2889
|
+
*/
|
|
2890
|
+
expectedCanceledAt: Date | null;
|
|
2816
2891
|
/** For TRIAL → null, otherwise period start from `initialPeriodWindow`. */
|
|
2817
2892
|
periodStart: Date | null;
|
|
2818
2893
|
periodEnd: Date | null;
|
|
@@ -2829,6 +2904,12 @@ interface ApplyOnboardingSelectionResult {
|
|
|
2829
2904
|
subscriptionId: string;
|
|
2830
2905
|
/** null if no redeemPromo callback was provided or the callback returned null. */
|
|
2831
2906
|
promoRedemption: PromoCodeRedemptionRecord | null;
|
|
2907
|
+
/**
|
|
2908
|
+
* False when the row's cancellation moved since the caller read it, in
|
|
2909
|
+
* which case nothing was written — including the promo redemption, which
|
|
2910
|
+
* shares the transaction.
|
|
2911
|
+
*/
|
|
2912
|
+
claimed: boolean;
|
|
2832
2913
|
}
|
|
2833
2914
|
/**
|
|
2834
2915
|
* Callback signature for promo-code redemption WITHIN the onboarding
|
|
@@ -2851,9 +2932,13 @@ interface TenantSubscriptionWritePort {
|
|
|
2851
2932
|
changePlanImmediate(tenantId: string, input: ImmediatePlanChangeInput): Promise<{
|
|
2852
2933
|
plan: string;
|
|
2853
2934
|
billingCycle: string;
|
|
2935
|
+
/** False when the row's cancellation moved since the caller read it. */
|
|
2936
|
+
claimed: boolean;
|
|
2854
2937
|
}>;
|
|
2855
2938
|
/** Change at period end: set pending fields. */
|
|
2856
|
-
schedulePlanChange(tenantId: string, input: ScheduledPlanChangeInput): Promise<
|
|
2939
|
+
schedulePlanChange(tenantId: string, input: ScheduledPlanChangeInput): Promise<{
|
|
2940
|
+
claimed: boolean;
|
|
2941
|
+
}>;
|
|
2857
2942
|
/**
|
|
2858
2943
|
* Marks the pending PlanVersion as accepted. Idempotent — a duplicate
|
|
2859
2944
|
* accept is a no-op. Returns `alreadyAccepted: true` if the status was
|
|
@@ -2866,12 +2951,52 @@ interface TenantSubscriptionWritePort {
|
|
|
2866
2951
|
alreadyAccepted: boolean;
|
|
2867
2952
|
}>;
|
|
2868
2953
|
/**
|
|
2869
|
-
*
|
|
2870
|
-
*
|
|
2954
|
+
* Record a cancellation. The dates are decided above this port.
|
|
2955
|
+
*
|
|
2956
|
+
* `canceledAt` is when the customer said it; `effectiveAt` is when it
|
|
2957
|
+
* lands. They differ for every ordinary cancellation, because a
|
|
2958
|
+
* subscription cancelled inside its term keeps running, keeps being billed
|
|
2959
|
+
* and keeps its entitlements until the term ends. An adapter that computed
|
|
2960
|
+
* the second from the first — which this one did, as
|
|
2961
|
+
* `immediate ? now : currentPeriodEnd` — was deciding a commercial
|
|
2962
|
+
* question in a persistence layer, and could not see the minimum term or
|
|
2963
|
+
* the notice period at all.
|
|
2964
|
+
*
|
|
2965
|
+
* `terminateNow` flips the status immediately, and is set when the
|
|
2966
|
+
* cancellation is already effective: an operator ending a contract, or the
|
|
2967
|
+
* rules finding nothing left to run — no period, no term, as on a trial.
|
|
2968
|
+
* It is never a client's request. A tenant may always declare a
|
|
2969
|
+
* cancellation and may never shorten the term they are in; what decides
|
|
2970
|
+
* this flag is the date the rules returned, not the date they asked for.
|
|
2971
|
+
*
|
|
2972
|
+
* `minimumTermUntil` extends the stored commitment, and is set only when
|
|
2973
|
+
* the cancellation itself extends it: a declaration made after the notice
|
|
2974
|
+
* deadline buys the following period. Left unset the stored term end is
|
|
2975
|
+
* unchanged, which is the ordinary case.
|
|
2871
2976
|
*/
|
|
2872
|
-
cancelSubscription(tenantId: string,
|
|
2977
|
+
cancelSubscription(tenantId: string, input: {
|
|
2978
|
+
canceledAt: Date;
|
|
2979
|
+
effectiveAt: Date;
|
|
2980
|
+
terminateNow: boolean;
|
|
2981
|
+
minimumTermUntil?: Date;
|
|
2982
|
+
}): Promise<{
|
|
2873
2983
|
canceledAt: Date | null;
|
|
2984
|
+
canceledEffectiveAt: Date | null;
|
|
2874
2985
|
status: string;
|
|
2986
|
+
/**
|
|
2987
|
+
* True when a cancellation was already recorded and this call changed
|
|
2988
|
+
* nothing — the stored dates are returned instead.
|
|
2989
|
+
*
|
|
2990
|
+
* The caller checks first, but a check and a write are two moments, and
|
|
2991
|
+
* two requests can pass the check before either writes. Straddling a
|
|
2992
|
+
* notice deadline that costs a billing cycle: the first declaration
|
|
2993
|
+
* lands on time, the second recomputes against a later `now`, and an
|
|
2994
|
+
* unconditional write replaces the first answer with one a period
|
|
2995
|
+
* further out. An implementation therefore claims the row only while
|
|
2996
|
+
* both cancellation fields are still empty, and answers `true` here
|
|
2997
|
+
* when the claim finds nothing to claim.
|
|
2998
|
+
*/
|
|
2999
|
+
alreadyCanceled: boolean;
|
|
2875
3000
|
}>;
|
|
2876
3001
|
/**
|
|
2877
3002
|
* Atomic onboarding creation: sets plan + cycle + period window
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saasicat/core",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.6",
|
|
4
4
|
"description": "TypeScript contracts for SaaSiCat — capabilities, catalogs, subscriptions, entitlements, admin manifests, persistence ports — and the pure domain logic both server and browser run: error catalogues, plan-diff classification, promo arithmetic, feature requirements.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|