@schematichq/schematic-typescript-node 1.6.0 → 1.6.2
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/README.md +51 -28
- package/dist/api/resources/credits/client/requests/AcquireCreditLeaseRequestBody.d.ts +1 -0
- package/dist/api/resources/credits/client/requests/ExtendCreditLeaseRequestBody.d.ts +3 -0
- package/dist/api/resources/entitlements/client/requests/CreateBillingLinkedPlanEntitlementRequestBody.d.ts +4 -0
- package/dist/api/resources/entitlements/client/requests/CreatePlanEntitlementRequestBody.d.ts +4 -0
- package/dist/api/resources/entitlements/client/requests/UpdatePlanEntitlementRequestBody.d.ts +4 -0
- package/dist/api/resources/features/client/requests/CheckAndReserveFlagRequestBody.d.ts +4 -2
- package/dist/api/resources/planmigrations/client/Client.d.ts +4 -2
- package/dist/api/resources/planmigrations/client/requests/CountMigrationsRequest.d.ts +3 -1
- package/dist/api/resources/planmigrations/client/requests/ListMigrationsRequest.d.ts +3 -1
- package/dist/api/resources/planmigrations/types/CountMigrationsParams.d.ts +1 -0
- package/dist/api/resources/planmigrations/types/ListMigrationsParams.d.ts +1 -0
- package/dist/api/resources/plans/client/Client.d.ts +4 -0
- package/dist/api/resources/plans/client/requests/CountPlansRequest.d.ts +6 -0
- package/dist/api/resources/plans/client/requests/ListPlansRequest.d.ts +6 -0
- package/dist/api/resources/plans/types/CountPlansParams.d.ts +4 -0
- package/dist/api/resources/plans/types/ListPlansParams.d.ts +4 -0
- package/dist/api/types/BillingArrearsCadence.d.ts +1 -0
- package/dist/api/types/BillingCreditGrantReason.d.ts +3 -0
- package/dist/api/types/BillingCreditResponseData.d.ts +1 -0
- package/dist/api/types/BillingPlanCreditGrantBillingMode.d.ts +5 -0
- package/dist/api/types/BillingPlanCreditGrantPriceTierResponseData.d.ts +10 -0
- package/dist/api/types/BillingPlanCreditGrantResponseData.d.ts +12 -0
- package/dist/api/types/CompanyPlanCreditGrantView.d.ts +3 -0
- package/dist/api/types/CreateBillingPlanCreditGrantRequestBody.d.ts +11 -1
- package/dist/api/types/CreateEntitlementInBundleRequestBody.d.ts +4 -0
- package/dist/api/types/CreditEventLedgerResponseData.d.ts +1 -0
- package/dist/api/types/CreditGrantPriceTierRequestBody.d.ts +8 -0
- package/dist/api/types/CreditTransferReason.d.ts +7 -0
- package/dist/api/types/PlanCreditGrantView.d.ts +3 -0
- package/dist/api/types/PlanEntitlementResponseData.d.ts +4 -0
- package/dist/api/types/PlanVersionMigrationResponseData.d.ts +2 -0
- package/dist/api/types/UpdateBillingPlanCreditGrantRequestBody.d.ts +11 -1
- package/dist/api/types/UsageBasedEntitlementRequestBody.d.ts +4 -0
- package/dist/api/types/index.d.ts +4 -0
- package/dist/cache/redis.d.ts +11 -0
- package/dist/credits/check.d.ts +10 -5
- package/dist/credits/index.d.ts +4 -4
- package/dist/credits/lease-manager.d.ts +74 -2
- package/dist/credits/lease-store.d.ts +27 -11
- package/dist/credits/redis-lease-store.d.ts +2 -2
- package/dist/credits/server-check.d.ts +40 -0
- package/dist/credits/track.d.ts +1 -1
- package/dist/credits/types.d.ts +37 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +12 -10
- package/dist/serialization/resources/credits/client/requests/ExtendCreditLeaseRequestBody.d.ts +1 -0
- package/dist/serialization/resources/entitlements/client/requests/CreateBillingLinkedPlanEntitlementRequestBody.d.ts +4 -0
- package/dist/serialization/resources/entitlements/client/requests/CreatePlanEntitlementRequestBody.d.ts +4 -0
- package/dist/serialization/resources/entitlements/client/requests/UpdatePlanEntitlementRequestBody.d.ts +4 -0
- package/dist/serialization/resources/features/client/requests/CheckAndReserveFlagRequestBody.d.ts +1 -0
- package/dist/serialization/resources/planmigrations/types/CountMigrationsParams.d.ts +1 -0
- package/dist/serialization/resources/planmigrations/types/ListMigrationsParams.d.ts +1 -0
- package/dist/serialization/resources/plans/types/CountPlansParams.d.ts +2 -0
- package/dist/serialization/resources/plans/types/ListPlansParams.d.ts +2 -0
- package/dist/serialization/types/BillingArrearsCadence.d.ts +1 -1
- package/dist/serialization/types/BillingCreditGrantReason.d.ts +1 -1
- package/dist/serialization/types/BillingCreditResponseData.d.ts +2 -0
- package/dist/serialization/types/BillingPlanCreditGrantBillingMode.d.ts +7 -0
- package/dist/serialization/types/BillingPlanCreditGrantPriceTierResponseData.d.ts +12 -0
- package/dist/serialization/types/BillingPlanCreditGrantResponseData.d.ts +10 -0
- package/dist/serialization/types/CompanyPlanCreditGrantView.d.ts +5 -0
- package/dist/serialization/types/CreateBillingPlanCreditGrantRequestBody.d.ts +8 -0
- package/dist/serialization/types/CreateEntitlementInBundleRequestBody.d.ts +4 -0
- package/dist/serialization/types/CreditEventLedgerResponseData.d.ts +2 -0
- package/dist/serialization/types/CreditGrantPriceTierRequestBody.d.ts +11 -0
- package/dist/serialization/types/CreditTransferReason.d.ts +7 -0
- package/dist/serialization/types/PlanCreditGrantView.d.ts +5 -0
- package/dist/serialization/types/PlanEntitlementResponseData.d.ts +4 -0
- package/dist/serialization/types/PlanVersionMigrationResponseData.d.ts +2 -0
- package/dist/serialization/types/UpdateBillingPlanCreditGrantRequestBody.d.ts +8 -0
- package/dist/serialization/types/UsageBasedEntitlementRequestBody.d.ts +4 -0
- package/dist/serialization/types/index.d.ts +4 -0
- package/dist/wrapper.d.ts +91 -26
- package/package.json +5 -1
|
@@ -3,7 +3,7 @@ export interface UpdateBillingPlanCreditGrantRequestBody {
|
|
|
3
3
|
applyToExisting?: boolean;
|
|
4
4
|
/** Which boundary closes a monthly arrears window: the subscription's own recurrence (billing_period_start) or the calendar month (month_end). Only applies when arrears_cadence is monthly; defaults to billing_period_start. Send null to fall back to the default. */
|
|
5
5
|
arrearsAnchor?: Schematic.BillingArrearsAnchor;
|
|
6
|
-
/** How often postpaid charges are closed and invoiced: end_of_billing_period (the default) or monthly. Send null to fall back to the default. */
|
|
6
|
+
/** How often postpaid charges are closed and invoiced: end_of_billing_period (the default) or monthly. Quarterly is not available for postpaid charges. Send null to fall back to the default. */
|
|
7
7
|
arrearsCadence?: Schematic.BillingArrearsCadence;
|
|
8
8
|
autoTopupAmount?: number;
|
|
9
9
|
autoTopupAmountType?: Schematic.CreditAutoTopupAmountType;
|
|
@@ -15,6 +15,8 @@ export interface UpdateBillingPlanCreditGrantRequestBody {
|
|
|
15
15
|
autoTopupSelfService?: boolean;
|
|
16
16
|
autoTopupThresholdCredits?: number;
|
|
17
17
|
autoTopupThresholdPercent?: number;
|
|
18
|
+
/** Whether the credits are included in the plan price (granted) or billed as their own subscription line at a price per credit (billed). Billed is only available on custom plans. */
|
|
19
|
+
billingMode?: Schematic.BillingPlanCreditGrantBillingMode;
|
|
18
20
|
/** Deprecated: use compatible_plan_ids on credit bundles instead. Still accepted; writes through to the credit's bundle compatibility. */
|
|
19
21
|
canBuyBundles?: boolean;
|
|
20
22
|
/** Credits granted once per company on top of the per-license amount. Only valid when the grant scales per license. */
|
|
@@ -33,6 +35,8 @@ export interface UpdateBillingPlanCreditGrantRequestBody {
|
|
|
33
35
|
postpaidRatePerUnit?: number;
|
|
34
36
|
/** Decimal string form of postpaid_rate_per_unit, for rates finer than one minor unit (for example 0.0002). Takes precedence over postpaid_rate_per_unit when both are set, matching how the credit's own price_per_unit_decimal behaves. Send null to clear it. */
|
|
35
37
|
postpaidRatePerUnitDecimal?: string;
|
|
38
|
+
/** Tier table pricing the credits on this grant, cheapest bound first, the last tier unbounded. Sending it moves the grant off one rate per credit. Requires tier_mode. */
|
|
39
|
+
priceTiers?: Schematic.CreditGrantPriceTierRequestBody[];
|
|
36
40
|
resetCadence: Schematic.BillingPlanCreditGrantResetCadence;
|
|
37
41
|
resetStart: Schematic.BillingPlanCreditGrantResetStart;
|
|
38
42
|
resetType?: Schematic.BillingPlanCreditGrantResetType;
|
|
@@ -40,4 +44,10 @@ export interface UpdateBillingPlanCreditGrantRequestBody {
|
|
|
40
44
|
rolloverPercentage?: number;
|
|
41
45
|
/** Whether the grant is a fixed amount per company, or issued once per license the company holds. Changing this re-issues the credits companies already hold for this grant. */
|
|
42
46
|
scaling?: Schematic.PlanCreditGrantScaling;
|
|
47
|
+
/** How price_tiers apply: volume prices every credit at the rate of the tier the total lands in, graduated prices each tier's own credits at its own rate. Required with price_tiers. */
|
|
48
|
+
tierMode?: Schematic.BillingTiersMode;
|
|
49
|
+
/** Price per credit in the plan currency's smallest unit. Required when billing_mode is billed, unless unit_price_decimal or price_tiers is set. Sending it moves a tiered grant back to one rate per credit. */
|
|
50
|
+
unitPrice?: number;
|
|
51
|
+
/** Price per credit as a decimal in the plan currency's smallest unit, for prices below one cent. */
|
|
52
|
+
unitPriceDecimal?: string;
|
|
43
53
|
}
|
|
@@ -8,7 +8,11 @@ export interface UsageBasedEntitlementRequestBody {
|
|
|
8
8
|
monthlyPriceTiers?: Schematic.CreatePriceTierRequestBody[];
|
|
9
9
|
monthlyUnitPrice?: number;
|
|
10
10
|
monthlyUnitPriceDecimal?: string;
|
|
11
|
+
/** How often overage charges are assessed and invoiced. Defaults to end_of_billing_period, where the billing provider aggregates usage over the subscription's own period and bills it at period end. Set to monthly or quarterly to have overage assessed each month or quarter and billed on its own invoice, which is the point of the setting on annual plans. A quarter charges each month's usage against that month's allowance. Only applies to overage price behavior. */
|
|
12
|
+
overageBillingCadence?: Schematic.BillingArrearsCadence;
|
|
11
13
|
overageBillingProductId?: string;
|
|
14
|
+
/** Which boundary closes a monthly or quarterly overage window: the subscription's own recurrence (billing_period_start) or the calendar month (month_end). Quarterly windows run in three-month blocks from the billing period start, held to the subscription's own period, or in calendar quarters at month_end. Only applies when overage_billing_cadence is monthly or quarterly, and must match metric_period_month_reset so each window closes when the allowance resets: billing_period_start for billing_cycle, month_end for first_of_month. Defaults to the anchor that matches the reset. */
|
|
15
|
+
overageInvoiceAnchor?: Schematic.BillingArrearsAnchor;
|
|
12
16
|
priceBehavior?: Schematic.EntitlementPriceBehavior;
|
|
13
17
|
/** Use MonthlyPriceTiers or YearlyPriceTiers instead */
|
|
14
18
|
priceTiers?: Schematic.CreatePriceTierRequestBody[];
|
|
@@ -37,6 +37,8 @@ export * from "./BillingCustomerSubscription";
|
|
|
37
37
|
export * from "./BillingCustomerWithSubscriptionsResponseData";
|
|
38
38
|
export * from "./BillingLinkedResourceResponseData";
|
|
39
39
|
export * from "./BillingMeterResponseData";
|
|
40
|
+
export * from "./BillingPlanCreditGrantBillingMode";
|
|
41
|
+
export * from "./BillingPlanCreditGrantPriceTierResponseData";
|
|
40
42
|
export * from "./BillingPlanCreditGrantResetCadence";
|
|
41
43
|
export * from "./BillingPlanCreditGrantResetStart";
|
|
42
44
|
export * from "./BillingPlanCreditGrantResetType";
|
|
@@ -168,6 +170,7 @@ export * from "./CreditEventLedgerResponseData";
|
|
|
168
170
|
export * from "./CreditEventType";
|
|
169
171
|
export * from "./CreditGrantDetail";
|
|
170
172
|
export * from "./CreditGrantExpiryRequestBody";
|
|
173
|
+
export * from "./CreditGrantPriceTierRequestBody";
|
|
171
174
|
export * from "./CreditGrantSortOrder";
|
|
172
175
|
export * from "./CreditLeaseResponseData";
|
|
173
176
|
export * from "./CreditReservationResponseData";
|
|
@@ -181,6 +184,7 @@ export * from "./CreditsAutoTopupSuccess";
|
|
|
181
184
|
export * from "./CreditsCreditPurchaseSuccess";
|
|
182
185
|
export * from "./CreditsWebhookCompanySummary";
|
|
183
186
|
export * from "./CreditsWebhookCreditSummary";
|
|
187
|
+
export * from "./CreditTransferReason";
|
|
184
188
|
export * from "./CreditTransferResponseData";
|
|
185
189
|
export * from "./CreditTransferView";
|
|
186
190
|
export * from "./CreditTriggerConfig";
|
package/dist/cache/redis.d.ts
CHANGED
|
@@ -39,6 +39,17 @@ export interface RedisClient {
|
|
|
39
39
|
arguments: string[];
|
|
40
40
|
}): Promise<unknown>;
|
|
41
41
|
pExpireAt(key: string, timestamp: number): Promise<unknown>;
|
|
42
|
+
multi?(): RedisMulti;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The queued-command half of MULTI/EXEC, as node-redis v4 returns it from
|
|
46
|
+
* `multi()`. Only the commands the SDK queues are declared; every one of them
|
|
47
|
+
* is single-key, so the transaction is safe under Redis Cluster too.
|
|
48
|
+
*/
|
|
49
|
+
export interface RedisMulti {
|
|
50
|
+
hSet(key: string, field: string | Record<string, string | number>, value?: string | number): RedisMulti;
|
|
51
|
+
pExpireAt(key: string, timestamp: number): RedisMulti;
|
|
52
|
+
exec(): Promise<unknown>;
|
|
42
53
|
}
|
|
43
54
|
export interface RedisOptions extends CacheOptions {
|
|
44
55
|
/** Pre-created, connected redis client */
|
package/dist/credits/check.d.ts
CHANGED
|
@@ -33,7 +33,7 @@ export interface CreditCheckDeps {
|
|
|
33
33
|
* check. Otherwise `creditId` + `consumptionRate` + `eventSubtype` come
|
|
34
34
|
* straight off the entitlement — no structural flag scan.
|
|
35
35
|
* 2. Acquire (or reuse) a lease for `(company, creditId)`.
|
|
36
|
-
* 3. Try to reserve `quantity × consumptionRate` from the lease.
|
|
36
|
+
* 3. Try to reserve `ceil(quantity) × consumptionRate` from the lease.
|
|
37
37
|
* 4. Run the WASM rules engine against a substituted company snapshot
|
|
38
38
|
* (`credit_balances[creditId] = lease.localRemaining` *before* the
|
|
39
39
|
* reservation we just made was debited), gating with `credit_cost` so the
|
|
@@ -45,9 +45,14 @@ export declare function checkWithLease(deps: CreditCheckDeps, key: string, evalC
|
|
|
45
45
|
* Build the preflight options envelope for a client-side rules evaluation.
|
|
46
46
|
* With an `eventSubtype` the quantity goes out as the `event_usage` pair so
|
|
47
47
|
* the engine matches it to the subtype's condition; without one it goes out
|
|
48
|
-
* as the generic `usage` knob. Exported so `client.check`'s fallback path
|
|
49
|
-
*
|
|
50
|
-
* evaluation
|
|
51
|
-
*
|
|
48
|
+
* as the generic `usage` knob. Exported so `client.check`'s fallback path and
|
|
49
|
+
* the server-mode check-and-reserve body can thread the same preflight — any
|
|
50
|
+
* evaluation path honors it even when the lease path can't run.
|
|
51
|
+
*
|
|
52
|
+
* The quantity rounds up here, once, so every consumer of this envelope asks
|
|
53
|
+
* the same whole-unit question: the engine takes an integer, the REST body
|
|
54
|
+
* takes an integer, and the settling Track event bills `ceil(actual)`. A
|
|
55
|
+
* preflight is an upper bound, so rounding up is also the only safe direction
|
|
56
|
+
* — the check must not pass on less usage than the action is about to record.
|
|
52
57
|
*/
|
|
53
58
|
export declare function buildPreflightOptions(options: CheckOptions): CheckFlagOptions | undefined;
|
package/dist/credits/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { LeaseStore, leaseKey, type LeaseEntry, type ILeaseStore } from "./lease-store";
|
|
1
|
+
export { LeaseStore, leaseKey, type LeaseEntry, type ILeaseStore, type ReserveResult } from "./lease-store";
|
|
2
2
|
export { ReservationStore, type IReservationStore } from "./reservation-store";
|
|
3
|
-
export { CreditLeaseManager } from "./lease-manager";
|
|
3
|
+
export { CreditLeaseManager, settleWithin, unrefTimer } from "./lease-manager";
|
|
4
4
|
export { RedisLeaseStore } from "./redis-lease-store";
|
|
5
5
|
export { RedisReservationStore } from "./redis-reservation-store";
|
|
6
|
-
export { DEFAULT_LEASE_DURATION_MS, DEFAULT_LEASE_SIZE, DEFAULT_LOW_WATER_MARK, DEFAULT_PREWARM_POLL_INTERVAL_MS, DEFAULT_PREWARM_RESOLVE_TIMEOUT_MS, DEFAULT_RESERVATION_TTL_MS, DEFAULT_SWEEP_INTERVAL_MS, } from "./types";
|
|
7
|
-
export type { CreditLeaseConfig, ResolvedLeaseConfig, Reservation, CheckOptions, CheckResult, OnAcquireFailure, TrackWithReservationOptions, } from "./types";
|
|
6
|
+
export { DEFAULT_LEASE_DURATION_MS, DEFAULT_LEASE_SIZE, DEFAULT_LOW_WATER_MARK, DEFAULT_PREWARM_POLL_INTERVAL_MS, DEFAULT_PREWARM_RESOLVE_TIMEOUT_MS, DEFAULT_RESERVATION_TTL_MS, DEFAULT_SWEEP_INTERVAL_MS, MAX_RESERVATION_TTL_MS, RESERVATION_TTL_SKEW_ALLOWANCE_MS, SHUTDOWN_DRAIN_TIMEOUT_MS, } from "./types";
|
|
7
|
+
export type { CreditLeaseConfig, CreditLeaseMode, ResolvedLeaseConfig, Reservation, CheckOptions, CheckResult, OnAcquireFailure, TrackWithReservationOptions, } from "./types";
|
|
@@ -2,6 +2,15 @@ import type { CreditsClient } from "../api/resources/credits/client/Client";
|
|
|
2
2
|
import type { Logger } from "../logger";
|
|
3
3
|
import { type ILeaseStore, type LeaseEntry } from "./lease-store";
|
|
4
4
|
import { type CreditLeaseConfig, type ResolvedLeaseConfig } from "./types";
|
|
5
|
+
/** Drop a timer's hold on the event loop where the runtime has one (Node). */
|
|
6
|
+
export declare function unrefTimer(timer: ReturnType<typeof setTimeout>): void;
|
|
7
|
+
/**
|
|
8
|
+
* Await `promises`, giving up after `timeoutMs`. Reports whether everything
|
|
9
|
+
* landed, so a caller winding down can say what it is abandoning. Abandoned
|
|
10
|
+
* work is not cancelled — promises have no cancellation — it just stops being
|
|
11
|
+
* waited on.
|
|
12
|
+
*/
|
|
13
|
+
export declare function settleWithin(promises: Promise<unknown>[], timeoutMs: number): Promise<boolean>;
|
|
5
14
|
/**
|
|
6
15
|
* Owns the lifecycle of `credit_lease` rows for a single client: acquire on
|
|
7
16
|
* first use or after expiry, extend when the local view dips below the low
|
|
@@ -20,6 +29,8 @@ export declare class CreditLeaseManager {
|
|
|
20
29
|
private readonly config;
|
|
21
30
|
private readonly inflightAcquire;
|
|
22
31
|
private readonly inflightExtend;
|
|
32
|
+
private readonly background;
|
|
33
|
+
private stopped;
|
|
23
34
|
constructor(opts: {
|
|
24
35
|
creditsClient: CreditsClient;
|
|
25
36
|
leaseStore: ILeaseStore;
|
|
@@ -48,12 +59,71 @@ export declare class CreditLeaseManager {
|
|
|
48
59
|
* that figure (a single check just failed a reserve for that many
|
|
49
60
|
* credits — extend opportunistically instead of waiting for the next
|
|
50
61
|
* sub-watermark check).
|
|
62
|
+
* A caller arriving while an extend is in flight joins it. If its own
|
|
63
|
+
* shortfall is larger than what that extend asked for, it waits the flight
|
|
64
|
+
* out and then issues exactly one follow-up extend for the remaining
|
|
65
|
+
* difference — otherwise it would inherit a tranche-sized ask and fail its
|
|
66
|
+
* post-extend retry with credits still sitting on the server. A flight it
|
|
67
|
+
* finds on the way back is only joined if that one covers the shortfall
|
|
68
|
+
* too; a smaller one is waited out, never inherited.
|
|
51
69
|
* Returns the in-flight promise so callers can await it or fire-and-forget.
|
|
52
70
|
* Never rejects (it is often fire-and-forget — a rejection would surface
|
|
53
71
|
* as an unhandled promise rejection).
|
|
54
72
|
*/
|
|
55
|
-
maybeExtendInBackground(companyId: string, creditTypeId: string, requiredCredits?: number, requestOptions?: CreditsClient.RequestOptions): Promise<LeaseEntry | undefined>;
|
|
73
|
+
maybeExtendInBackground(companyId: string, creditTypeId: string, requiredCredits?: number, requestOptions?: CreditsClient.RequestOptions, joinDeadlineMs?: number): Promise<LeaseEntry | undefined>;
|
|
74
|
+
private extendIfNeeded;
|
|
75
|
+
/** When a joiner's wait on a shared flight runs out, or undefined for no cap. */
|
|
76
|
+
private joinDeadline;
|
|
77
|
+
/**
|
|
78
|
+
* Await a flight somebody else is running, giving up at `deadline`. Giving
|
|
79
|
+
* up abandons only our wait: the flight keeps running for the callers still
|
|
80
|
+
* on it, and whatever it installs is there for our next check to read.
|
|
81
|
+
*/
|
|
82
|
+
private joinWithin;
|
|
83
|
+
/**
|
|
84
|
+
* Read the slot, reporting `undefined` when the read fails or the lease is
|
|
85
|
+
* absent or expired. An expired lease is never extended: the server treats
|
|
86
|
+
* it as released (its remainder already refunded to the company balance),
|
|
87
|
+
* so the right move is a fresh acquire, which the next check's
|
|
88
|
+
* `acquireIfNeeded` performs. Extending would at best waste a wire call and
|
|
89
|
+
* at worst resurrect a stale local row.
|
|
90
|
+
*/
|
|
91
|
+
private readLiveLease;
|
|
92
|
+
/** Whether `entry` sits low enough to warrant an extend. */
|
|
93
|
+
private needsExtend;
|
|
94
|
+
/**
|
|
95
|
+
* Register and run an extend as the slot's in-flight one. The cleanup is
|
|
96
|
+
* identity-guarded rather than an unconditional `delete`: a joiner whose
|
|
97
|
+
* shortfall outran this flight registers a follow-up for the same key, and
|
|
98
|
+
* this flight's `finally` must not evict it.
|
|
99
|
+
*/
|
|
100
|
+
private startExtend;
|
|
101
|
+
/**
|
|
102
|
+
* Re-read the slot now that this flight owns it, and extend only if the
|
|
103
|
+
* fresh row still warrants one. The row that decided this extend was read
|
|
104
|
+
* before the flight was registered, so an extend that landed in that gap,
|
|
105
|
+
* clearing its own flight on the way out, would otherwise be followed by a
|
|
106
|
+
* second extend, under a new idempotency key, for a lease it already topped
|
|
107
|
+
* up. The registered `requestedAdditional` stands: a joiner compares its
|
|
108
|
+
* shortfall against that figure, so the wire body has to carry it.
|
|
109
|
+
*/
|
|
110
|
+
private recheckAndExtend;
|
|
56
111
|
private extend;
|
|
112
|
+
/** Hold a reference to unawaited work so `drain()` can wait it out. */
|
|
113
|
+
private track;
|
|
114
|
+
/**
|
|
115
|
+
* Refuse new lease work. Idempotent, and paired with `drain()`: stopping
|
|
116
|
+
* first is what makes the drain terminate, since nothing can enqueue
|
|
117
|
+
* behind it.
|
|
118
|
+
*/
|
|
119
|
+
stop(): void;
|
|
120
|
+
/**
|
|
121
|
+
* Wait out lease work already on the wire, so a close releases what that
|
|
122
|
+
* work installs instead of orphaning it. Bounded: whatever has not landed
|
|
123
|
+
* by `timeoutMs` is abandoned rather than stalling the caller's shutdown,
|
|
124
|
+
* and the credits it holds fall back to server-side expiry.
|
|
125
|
+
*/
|
|
126
|
+
drain(timeoutMs?: number): Promise<void>;
|
|
57
127
|
/**
|
|
58
128
|
* Release every live lease held in the store. ONLY safe when the store is
|
|
59
129
|
* per-process (in-memory): those leases are exclusively this process's, so
|
|
@@ -63,6 +133,8 @@ export declare class CreditLeaseManager {
|
|
|
63
133
|
* drawing on the same leases — and is excluded by the `list` capability
|
|
64
134
|
* check (only the in-memory store implements it). Best-effort: failures
|
|
65
135
|
* are logged and the lease falls back to server-side expiry.
|
|
136
|
+
* Bounded by `timeoutMs`, so a store or wire call that never lands cannot
|
|
137
|
+
* hold a closing client open; whatever is abandoned expires server-side.
|
|
66
138
|
*/
|
|
67
|
-
releaseAllLocalLeases(): Promise<void>;
|
|
139
|
+
releaseAllLocalLeases(timeoutMs?: number): Promise<void>;
|
|
68
140
|
}
|
|
@@ -21,6 +21,14 @@ export interface LeaseEntry {
|
|
|
21
21
|
localRemainingCredits: number;
|
|
22
22
|
expiresAt: Date;
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Outcome of a successful `tryReserve`: the post-debit balance plus the ID of
|
|
26
|
+
* the lease the credits actually came out of.
|
|
27
|
+
*/
|
|
28
|
+
export interface ReserveResult {
|
|
29
|
+
balance: number;
|
|
30
|
+
leaseId: string;
|
|
31
|
+
}
|
|
24
32
|
/** Backing-store contract shared by `LeaseStore` (in-memory) and `RedisLeaseStore` (shared). */
|
|
25
33
|
export interface ILeaseStore {
|
|
26
34
|
get(companyId: string, creditTypeId: string): Promise<LeaseEntry | undefined> | LeaseEntry | undefined;
|
|
@@ -65,15 +73,23 @@ export interface ILeaseStore {
|
|
|
65
73
|
drop(companyId: string, creditTypeId: string): Promise<void>;
|
|
66
74
|
/**
|
|
67
75
|
* Atomically check-and-debit `credits` from the lease's remaining balance.
|
|
68
|
-
* Returns the post-debit `localRemainingCredits`
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* the
|
|
74
|
-
* follow-up read.
|
|
76
|
+
* Returns the post-debit `localRemainingCredits` together with the
|
|
77
|
+
* `leaseId` the debit landed on, or `null` if there is no live lease, the
|
|
78
|
+
* balance is insufficient, or `credits` is not a finite non-negative
|
|
79
|
+
* number (NaN must never reach the debit — it slips through every numeric
|
|
80
|
+
* comparison and would poison the balance into allowing everything).
|
|
81
|
+
* Returning the balance rather than a boolean lets the caller derive the
|
|
82
|
+
* pre-debit figure (`returned + credits`) without a follow-up read.
|
|
83
|
+
*
|
|
84
|
+
* The debit is NOT keyed by lease ID: it charges whichever lease occupies
|
|
85
|
+
* the slot at that moment, which need not be the one the caller's acquire
|
|
86
|
+
* handed back — the slot's lease can be replaced in between (by the
|
|
87
|
+
* sweeper, or by a sibling pod on a shared backend). The caller MUST
|
|
88
|
+
* therefore pin its reservation to the returned `leaseId`, never to the
|
|
89
|
+
* acquired one: the settle refund, the sweep refund, and the Track event's
|
|
90
|
+
* `lease_id` all have to name the lease that was actually charged.
|
|
75
91
|
*/
|
|
76
|
-
tryReserve(companyId: string, creditTypeId: string, credits: number): Promise<
|
|
92
|
+
tryReserve(companyId: string, creditTypeId: string, credits: number): Promise<ReserveResult | null>;
|
|
77
93
|
/**
|
|
78
94
|
* Refund credits to the slot's lease balance (clamped at `grantedAmount`).
|
|
79
95
|
* When `leaseId` is supplied, the refund applies ONLY if the slot still
|
|
@@ -126,10 +142,10 @@ export declare class LeaseStore implements ILeaseStore {
|
|
|
126
142
|
drop(companyId: string, creditTypeId: string): Promise<void>;
|
|
127
143
|
/**
|
|
128
144
|
* Attempt to reserve `credits` from the local remaining balance.
|
|
129
|
-
* Returns the post-debit balance
|
|
130
|
-
* remaining (see `ILeaseStore.tryReserve`).
|
|
145
|
+
* Returns the post-debit balance and the charged lease's ID on success,
|
|
146
|
+
* `null` if there isn't enough remaining (see `ILeaseStore.tryReserve`).
|
|
131
147
|
*/
|
|
132
|
-
tryReserve(companyId: string, creditTypeId: string, credits: number): Promise<
|
|
148
|
+
tryReserve(companyId: string, creditTypeId: string, credits: number): Promise<ReserveResult | null>;
|
|
133
149
|
/**
|
|
134
150
|
* Refund credits back to the local balance (capped at grantedAmount).
|
|
135
151
|
* When `leaseId` is given, the refund is dropped if the slot now holds a
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RedisClient } from "../cache/redis";
|
|
2
|
-
import { type ILeaseStore, type LeaseEntry } from "./lease-store";
|
|
2
|
+
import { type ILeaseStore, type LeaseEntry, type ReserveResult } from "./lease-store";
|
|
3
3
|
/**
|
|
4
4
|
* Redis-backed lease store. One hash per `(companyId, creditTypeId)` slot, with
|
|
5
5
|
* atomic mutations via single-key Lua scripts so it stays correct on both
|
|
@@ -25,6 +25,6 @@ export declare class RedisLeaseStore implements ILeaseStore {
|
|
|
25
25
|
replace(entry: Omit<LeaseEntry, "localRemainingCredits">): Promise<boolean>;
|
|
26
26
|
extend(companyId: string, creditTypeId: string, grantedAmount: number, newExpiresAt?: Date, leaseId?: string): Promise<void>;
|
|
27
27
|
drop(companyId: string, creditTypeId: string): Promise<void>;
|
|
28
|
-
tryReserve(companyId: string, creditTypeId: string, credits: number): Promise<
|
|
28
|
+
tryReserve(companyId: string, creditTypeId: string, credits: number): Promise<ReserveResult | null>;
|
|
29
29
|
refund(companyId: string, creditTypeId: string, credits: number, leaseId?: string): Promise<void>;
|
|
30
30
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type * as api from "../api";
|
|
2
|
+
import type { CreditsClient } from "../api/resources/credits/client/Client";
|
|
3
|
+
import type { FeaturesClient } from "../api/resources/features/client/Client";
|
|
4
|
+
import type { Logger } from "../logger";
|
|
5
|
+
import type { CheckOptions, CheckResult } from "./types";
|
|
6
|
+
/** Everything `checkWithServerReservation` needs to satisfy a server-mode check. */
|
|
7
|
+
export interface ServerCheckDeps {
|
|
8
|
+
features: FeaturesClient;
|
|
9
|
+
credits: CreditsClient;
|
|
10
|
+
logger: Logger;
|
|
11
|
+
/** How far out to set the hold's `expiresAt` (ms). */
|
|
12
|
+
reservationTTL: number;
|
|
13
|
+
/**
|
|
14
|
+
* Resolve the caller's default for this flag — `options.defaultValue` when
|
|
15
|
+
* set, otherwise the client-level flag default. Used by the fail-open
|
|
16
|
+
* branch, which has no local engine to re-run.
|
|
17
|
+
*/
|
|
18
|
+
getDefault: () => boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Drives a single `client.check` with `usage` set, in server mode.
|
|
22
|
+
*
|
|
23
|
+
* One `POST /flags/{key}/check-and-reserve` call does everything the client
|
|
24
|
+
* path spreads across a lease acquire, a local reserve, and a WASM eval: the
|
|
25
|
+
* server evaluates the flag against the company's real balance, applies the
|
|
26
|
+
* preflight cost, and takes the hold — all in the same round-trip. There is no
|
|
27
|
+
* lease, no local store, and no rules engine involved.
|
|
28
|
+
*
|
|
29
|
+
* The failure contract differs from client mode in one place. `fail-open` there
|
|
30
|
+
* means "re-run the engine with the credit balance assumed sufficient", so plan
|
|
31
|
+
* targeting and every non-credit condition still apply. Server mode has no
|
|
32
|
+
* local engine to re-run — the call that would have answered is the one that
|
|
33
|
+
* failed — so `fail-open` returns the caller's default value
|
|
34
|
+
* (`options.defaultValue`, else the client's configured flag default) instead.
|
|
35
|
+
* `fail-closed` denies, same as client mode.
|
|
36
|
+
*
|
|
37
|
+
* No `flag_check` event is enqueued here: the server logs the flag check for
|
|
38
|
+
* check-and-reserve itself, the same way the REST `checkFlag` path does.
|
|
39
|
+
*/
|
|
40
|
+
export declare function checkWithServerReservation(deps: ServerCheckDeps, key: string, evalCtx: api.CheckFlagRequestBody, options: CheckOptions, fallback: () => Promise<CheckResult>): Promise<CheckResult>;
|
package/dist/credits/track.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { IReservationStore } from "./reservation-store";
|
|
|
3
3
|
import type { Reservation, TrackWithReservationOptions } from "./types";
|
|
4
4
|
/** Outcome of consuming a reservation and building its Track event. */
|
|
5
5
|
export interface ReservationConsumeResult {
|
|
6
|
-
/** The Track event to emit. Always carries the `leaseId
|
|
6
|
+
/** The Track event to emit. Always carries the `leaseId` (client mode) or the `reservationId` (server mode). */
|
|
7
7
|
track: api.EventBodyTrack;
|
|
8
8
|
/**
|
|
9
9
|
* `true` when the reservation was still live and `consume` debited/refunded
|
package/dist/credits/types.d.ts
CHANGED
|
@@ -13,23 +13,47 @@ import type { RedisClient } from "../cache/redis";
|
|
|
13
13
|
export type OnAcquireFailure = "fail-open" | "fail-closed";
|
|
14
14
|
export declare const DEFAULT_LEASE_DURATION_MS: number;
|
|
15
15
|
export declare const DEFAULT_RESERVATION_TTL_MS: number;
|
|
16
|
+
export declare const MAX_RESERVATION_TTL_MS: number;
|
|
17
|
+
export declare const RESERVATION_TTL_SKEW_ALLOWANCE_MS: number;
|
|
16
18
|
export declare const DEFAULT_LEASE_SIZE: number;
|
|
17
19
|
export declare const DEFAULT_LOW_WATER_MARK: number;
|
|
18
20
|
export declare const DEFAULT_SWEEP_INTERVAL_MS: number;
|
|
19
21
|
export declare const DEFAULT_PREWARM_RESOLVE_TIMEOUT_MS: number;
|
|
20
22
|
export declare const DEFAULT_PREWARM_POLL_INTERVAL_MS: number;
|
|
23
|
+
export declare const SHUTDOWN_DRAIN_TIMEOUT_MS: number;
|
|
24
|
+
/**
|
|
25
|
+
* Where a credit hold lives for a `check()` that passes `usage`.
|
|
26
|
+
* - `client`: local leases over DataStream — the SDK acquires a tranche of
|
|
27
|
+
* credits up front and carves reservations out of it locally (today's
|
|
28
|
+
* behavior). Requires DataStream (or replicator mode), and a shared Redis
|
|
29
|
+
* backend to gate across pods.
|
|
30
|
+
* - `server`: one `check-and-reserve` API call per check — the server
|
|
31
|
+
* evaluates the flag and takes the hold in the same round-trip. No
|
|
32
|
+
* DataStream, no Redis, no local stores.
|
|
33
|
+
* - `auto` (default): `client` when DataStream is enabled, `server` otherwise.
|
|
34
|
+
*/
|
|
35
|
+
export type CreditLeaseMode = "client" | "server" | "auto";
|
|
21
36
|
/**
|
|
22
37
|
* Configuration block enabling client-side lease + reservation behavior on
|
|
23
38
|
* `client.check` / `client.trackWithReservation`. Omit to keep the SDK
|
|
24
39
|
* lease-unaware (`check` falls back to a plain flag check).
|
|
25
40
|
*/
|
|
26
41
|
export interface CreditLeaseConfig {
|
|
42
|
+
/**
|
|
43
|
+
* Where the credit hold lives. Default `auto` — client mode when
|
|
44
|
+
* DataStream is enabled, server mode otherwise. See `CreditLeaseMode`.
|
|
45
|
+
*/
|
|
46
|
+
mode?: CreditLeaseMode;
|
|
27
47
|
/** Default lease duration in milliseconds. Default `DEFAULT_LEASE_DURATION_MS` (5 minutes). */
|
|
28
48
|
defaultLeaseDuration?: number;
|
|
29
49
|
/**
|
|
30
50
|
* Default reservation TTL in milliseconds. Default `DEFAULT_RESERVATION_TTL_MS`
|
|
31
|
-
* (60 seconds).
|
|
32
|
-
*
|
|
51
|
+
* (60 seconds). In server mode it is capped at `MAX_RESERVATION_TTL_MS`
|
|
52
|
+
* (1 hour) less `RESERVATION_TTL_SKEW_ALLOWANCE_MS`, since an hour out is
|
|
53
|
+
* the furthest the API will hold credits and it measures that against its
|
|
54
|
+
* own clock. Size this above the
|
|
55
|
+
* longest expected gap between `check()` and `trackWithReservation()`: a
|
|
56
|
+
* settle arriving after the TTL still bills
|
|
33
57
|
* the server but doesn't re-debit the local lease (its hold was already
|
|
34
58
|
* swept back), so the local balance reads high until the lease rolls over.
|
|
35
59
|
*/
|
|
@@ -71,7 +95,7 @@ export interface CreditLeaseConfig {
|
|
|
71
95
|
*/
|
|
72
96
|
redisKeyPrefix?: string;
|
|
73
97
|
/** Per-credit-type overrides (keyed by credit type ID). */
|
|
74
|
-
overrides?: Record<string, Partial<Omit<CreditLeaseConfig, "overrides" | "sweepIntervalMs" | "redisClient" | "redisKeyPrefix">>>;
|
|
98
|
+
overrides?: Record<string, Partial<Omit<CreditLeaseConfig, "overrides" | "mode" | "sweepIntervalMs" | "redisClient" | "redisKeyPrefix">>>;
|
|
75
99
|
}
|
|
76
100
|
/** Resolved config for a single credit type after applying defaults + overrides. */
|
|
77
101
|
export interface ResolvedLeaseConfig {
|
|
@@ -84,8 +108,17 @@ export interface ResolvedLeaseConfig {
|
|
|
84
108
|
export interface Reservation {
|
|
85
109
|
/** Opaque reservation ID. */
|
|
86
110
|
id: string;
|
|
87
|
-
/**
|
|
111
|
+
/**
|
|
112
|
+
* Underlying lease ID this reservation draws from. In server mode there is
|
|
113
|
+
* no lease — this mirrors `id` so the field stays populated.
|
|
114
|
+
*/
|
|
88
115
|
leaseId: string;
|
|
116
|
+
/**
|
|
117
|
+
* Where the hold lives. `server` means the API holds the credits and the
|
|
118
|
+
* settling Track event routes by `reservation_id`; absent (or `client`)
|
|
119
|
+
* means the hold is a local carve-out of a lease.
|
|
120
|
+
*/
|
|
121
|
+
mode?: "client" | "server";
|
|
89
122
|
/** Company that owns the lease. */
|
|
90
123
|
companyId: string;
|
|
91
124
|
/** Credit type the reservation reserves against. */
|
package/dist/index.d.ts
CHANGED
|
@@ -6,5 +6,5 @@ export { ConsoleLogger, LogLevel, type Logger } from "./logger";
|
|
|
6
6
|
export { SchematicEnvironment } from "./environments";
|
|
7
7
|
export { SchematicError, SchematicTimeoutError } from "./errors";
|
|
8
8
|
export { RulesEngineClient } from "./rules-engine";
|
|
9
|
-
export type { CheckOptions, CheckResult, CreditLeaseConfig, OnAcquireFailure, Reservation, TrackWithReservationOptions, } from "./credits";
|
|
9
|
+
export type { CheckOptions, CheckResult, CreditLeaseConfig, CreditLeaseMode, OnAcquireFailure, Reservation, TrackWithReservationOptions, } from "./credits";
|
|
10
10
|
export { verifyWebhookSignature, verifySignature, computeSignature, computeHexSignature, WebhookSignatureError, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_TIMESTAMP_HEADER } from "./webhooks";
|